【问题标题】:Pythonic way to "flatten" object hierarchy to nested dicts?将对象层次结构“展平”为嵌套字典的 Pythonic 方式?
【发布时间】:2009-09-08 09:41:50
【问题描述】:

我需要将对象“扁平化”为对象属性的嵌套字典。我想要这样做的对象通常只是基本类型的容器或以类似方式起作用的其他对象。例如:

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' } ] }

stdlib 中有什么可以为我做这件事的吗?如果不是,我是否必须针对所有已知的基本类型测试isinstance,以确保我不会尝试转换无法转换的对象(例如:bool)?

编辑:

这些对象从外部库返回到我的代码,因此我无法控制它们。我可以在我的方法中按原样使用它们,但是将它们转换为字典会更容易(更安全?)——尤其是对于单元测试。

【问题讨论】:

  • 我认为解决这个问题的唯一方法是通过迭代它自己的成员变量来编写一个支持序列化的基类。我不确定这是否是您想要的,但如果是这样,我很乐意提供一个示例实现。
  • 你能给我们用例吗?可能有一种更 Pythonic 的方式来解决整体问题。

标签: python


【解决方案1】:

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

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

错误? 在您的代码中,而不是:

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 列表。

【讨论】:

    【解决方案2】:

    不,标准库中没有任何内容。是的,您必须以某种方式测试这些类型是否是基本类型,例如 str、unicode、bool、int、float、long...

    您可能会创建一个方法注册表来“序列化”不同类型,但这仅在您有一些不应该序列化所有属性的类型时才有用,例如,或者如果您还需要展平类属性等。

    【讨论】:

    • 正是我在评论中写的,但是当您的扩展类型支持某种序列化方法时,您不一定需要检查基本类型。只需尝试在每种类型上调用它并捕获基本类型的适当异常。
    【解决方案3】:

    几乎每个对象都有一个字典(称为__dict__),包含它的所有方法和成员。
    通过一些类型检查,您可以编写一个仅过滤掉您感兴趣的成员的函数。

    这不是一项艰巨的任务,但正如 chrispy 所说,值得尝试从完全不同的角度看待您的问题。

    【讨论】:

    • 谢谢,但这些是我从另一个库传递的对象,因此无法控制它们。我可以在我的方法中按原样使用它们,但将它们转换为字典会更容易。
    【解决方案4】:

    好吧,我对此并不感到自豪,但可以做到以下几点:

    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()
    

    要指出的事情。如果您不设置 var 是 init ,则 dict 将不起作用,因为它访问的是实例变量而不是类变量(我想您的意思是实例变量)。其次,确保 Serializable 不会从对象继承,您是否会有一个 TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution

    希望对你有帮助!

    编辑:如果您只是复制 dict 使用 deepcopy 模块,这只是一个示例:P

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多