2

YAML を使用して Python オブジェクトの階層をダンプできます。

import yaml

class C():
    def __init__(self, x, y):
        self.x = x
        self.y = y

class D():
    def __init__(self, c, d):
        self.c = c
        self.d = d

d = D(c=C(x=1, y='hi'),
      d='bye')

print yaml.dump(d)

出力を生成します:

!!python/object:__main__.D
c: !!python/object:__main__.C {x: 1, y: hi}
d: bye

しかし、いくつかの属性を選択的に非表示にしたいと考えています。したがってattribs_to_dump(obj)、任意のオブジェクトに対して、ダンプしたい属性名のリストを返す関数があるとします。たとえば、次のようになります。

def attribs_to_dump(obj):
    if obj.__class__ == C: return ['x']
    if obj.__class__ == D: return ['c']

私の質問は、次の出力を得るためにどのようにフックattribs_to_dumpするのですか?yaml.dump

!!python/object:__main__.D
c: !!python/object:__main__.C {x: 1}

複雑な要因があります。オブジェクト階層を自分で前処理するのではなく、Yaml がオブジェクト階層をクロールするときに Yaml にフックすることで、効果を達成したいと考えています。その理由は、私が使用しているいくつかのライブラリに存在する魔法のために、階層内のすべてのオブジェクトが簡単に内省できるわけではないためですsetattr/getattr/__dict__:-(...

すべての助けに感謝します!

4

1 に答える 1

1

これは興味深い質問です、私はそれを解決するのを楽しんでいました、ありがとう:)

from copy import deepcopy


class C(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

class D(object):
    def __init__(self, c, d):
        self.c = c
        self.d = d

d = D(
    c=C(x=1, y='hi'),
    d='bye'
)


FILTER_PARAMS = (
    #(class_reference, process_recursively, ['attributes', 'on', 'whitelist'])
    (D, True, ['c']),
    (C, False, ['x']),
)

def attr_filter(obj, filter_params):
    for attr in dir(obj):
        if attr.startswith('__'):
            # ignore builtins
            continue

        attr_val = obj.__getattribute__(attr)
        # loop through filter params
        for (cls, do_recursive, whitelist) in filter_params:
            if isinstance(obj, cls) and attr in whitelist:
                # filter class matches the current obj's class and
                # current attribute is on the whitelist
                if do_recursive:
                    # must process this attribute the same way as the parent
                    setattr(obj, attr, attr_filter(attr_val, filter_params))                
                # break will avoid the execution of the else clause meaning
                # the attribute was on the white list so don't delete it
                break
        else:
            # delete the current attribute of the instance as it was
            # not on the whitelist
            delattr(obj, attr)

    return obj


# do a deepcopy of the object you pass in, so the original will be preserved
filtered_instance = attr_filter(deepcopy(d), FILTER_PARAMS)
print dir(filtered_instance)
print dir(filtered_instance.c)

# now pass filtered_instance to yaml's dump function
于 2012-10-02T14:55:44.320 に答える