将对象层次结构"扁平化"为嵌套字典的Pythonic方法?

0 投票
4 回答
3055 浏览
提问于 2025-04-15 14:09

我需要把对象“扁平化”,也就是把对象的属性变成嵌套的字典。这些对象通常只是一些基本类型或者其他以类似方式工作的对象的容器。例如:

class foo(object):
    bar = None
    baz = None

class spam(object):
    eggs = []

x = spam()
y = foo()
y.bar = True
y.baz = u"boz"
x.eggs.append(y)

我想把它“扁平化”成:

{ 'eggs': [ { 'bar': True, 'baz': u'boz' } ] }

在标准库里有没有可以帮我做到这一点的东西?如果没有,我是不是得对所有已知的基本类型用 isinstance 来测试,以确保我不会尝试转换那些不能转换的对象(比如:布尔值)?

编辑:

这些对象是从一个外部库返回给我的,所以我对它们没有控制权。我可以直接在我的方法中使用它们,但把它们转换成字典会更简单(也可能更安全)——特别是在单元测试的时候。

4 个回答

0

几乎每个对象都有一个字典(叫做 __dict__),里面包含了它的所有方法和属性。
通过一些类型检查,你可以写一个函数,只筛选出你感兴趣的那些属性。

这并不是个大任务,但正如chrispy所说,尝试从完全不同的角度来看待你的问题,可能会很有帮助。

0

好吧,我对此不是很自豪,但其实可以做到以下几点:

  1. 创建一个超级类,这个类里有序列化的方法,并且可以遍历它的属性。
  2. 在运行时通过bases来扩展你的类。
  3. 从新的超级类执行这个类。这样它就能访问子类中的dict数据并正常工作。

这里有个例子:

class foo(object):
    def __init__(self):
        self.bar = None
        self.baz = None

class spam(object):
    delf __init__(self):
       self.eggs = []

class Serializable():
    def serialize(self):
       result = {}
       for property in self.__dict__.keys():
           result[property] = self.__dict__[property]
       return result
foo.__bases__ += (Serializable,)
spam.__bases__ += (Serializable,)

x = spam()
y = foo()
y.bar = True
y.baz = u"boz"
x.eggs.append(y)
y.serialize()

需要注意的几点。如果你没有设置变量初始化,dict就无法正常工作,因为它是在访问实例变量,而不是类变量(我想你是指实例变量)。其次,确保Serializable类不要继承自object,如果继承了,你会遇到一个 TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution

希望这能帮到你!

编辑:如果你只是想复制dict,可以使用deepcopy模块,这只是个例子 :P

3

代码:你可能需要处理其他可迭代的类型:

def flatten(obj):
    if obj is None:
        return None
    elif hasattr(obj, '__dict__') and obj.__dict__:
        return dict([(k, flatten(v)) for (k, v) in obj.__dict__.items()])
    elif isinstance(obj, (dict,)):
        return dict([(k, flatten(v)) for (k, v) in obj.items()])
    elif isinstance(obj, (list,)):
        return [flatten(x) for x in obj]
    elif isinstance(obj, (tuple,)):
        return tuple([flatten(x) for x in obj])
    else:
        return obj

有bug吗? 在你的代码中,不要这样写:

class spam(object):
    eggs = []

x = spam()
x.eggs.add(...)

请这样做:

class spam(object):
    eggs = None #// if you need this line at all though

x = spam()
x.eggs = []
x.eggs.add(...)

如果你不这样做,那么所有的 spam 实例都会共享同一个 eggs 列表。

撰写回答