Object has no attribute '.__dict__' in python3

24

我有一个在Python2中通过,在Python3中失败的测试,我正在尝试找出原因。测试在以下行处失败:

我有一个在Python2中通过,在Python3中失败的测试,我正在尝试找出原因。测试在以下行处失败:

self._timeseries[0].resource.__dict__

遇到以下错误:

AttributeError: 'Resource' object has no attribute '__dict__'

如果我在调试测试时,在调试器中打印对象,我可以看到以下内容:

(Pdb) p self._timeseries[0].resource.__dict__
OrderedDict([('type', 'type_value'), ('labels', {'label1': 'value1', 'label2': 'value2', 'label3': 'value3'})])

如果我在Python3调试器中执行相同的操作,将得到以下结果:

(Pdb) p self._timeseries[0].resource.__dict__
*** AttributeError: 'Resource' object has no attribute '__dict__'

你有什么想法是为什么会发生这种情况吗?如果我在调试器中打印它而不使用.__dict__,对象看起来完全相同,为什么Python3会失败?


3
也许是某个库在Python 3中将此类型更改为__slots__对象?如果没有看到类定义和更多信息,就很难确定。 - anthony sottile
在这种情况下,有没有一种方法可以获取动态定义的属性?执行 .__slots__ 只返回 () - yelsayed
这相当令人惊讶。self.timeseries[0].resource 到底应该是什么?当我在 Python 2 和 3 中检查任意对象的 __dict__ 时,我得到的是一个 dict 类型的对象,而不是 OrderedDict 类型的对象。我的猜测是,在 Python 3 中,你用 Python 2 的方式设置 resourceOrderedDict 类型的 __dict__ 属性的方法不起作用。 - Right leg
@Rightleg 你说得对,这很有趣。不过我无法轻易找到答案,所以我猜我永远也不会知道这是怎么发生的了。 - yelsayed
1
虽然听起来很惊讶,但我正在使用命名元组进行实验,并发现了一些奇怪的事情:当您创建一个命名元组,然后对其进行实例化时,该实例的__dict__是一个OrderedDict。你的Python 2测试是否导入了collections,而你的Python 3测试忘记导入了它? - Right leg
2个回答

25
我在查找后找到了答案,这确实是Python2和Python3之间的差异,而且还是一个令人讨厌的差异。
原来代码中的类型“Resource”实际上是一个命名元组。在Python2中,“.__dict__”被添加为方便的属性包装器._asdict(),但在Python3中没有这样做: https://docs.python.org/2/library/collections.html#collections.somenamedtuple._asdict(在此处找到__dict__https://docs.python.org/3/library/collections.html#collections.somenamedtuple._asdict 因此,看起来._asdict实际上是真相之源,并且应该用于可移植的2to3代码。
值得一提的是,vars也是仅存在于Python2中的包装器。

2
不错啊!我不知道namedtuple上曾经有一个__dict__,我很高兴他们去掉了它 :). vars在Python3中也存在。 - anthony sottile
1
我刚在你的原问题上评论了类似的内容,结果发现你已经发布了这个答案。英雄所见略同 :) - Right leg
2
注意:vars在Py2和Py3中都存在,但它基本上是一个薄包装器,相当于在给定的参数上查找__dict__,因此当他们修复了namedtuple的错误后,它停止工作。使用__slots__贯穿继承层次结构的用户定义类(其中没有__dict__槽)将始终存在此问题,因为它们将__dict__使用替换为(有效地)一堆类级属性,这些属性访问存储在C级对象元数据中的值,该元数据没有等效的__dict__ - ShadowRanger

3

我写了一个小函数,可能会错过一些边缘情况,但它满足我编写的几个小测试用例(对于多重继承可能会出现问题)。

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


class D(object):
    __slots__ = ('x', 'y')
    def __init__(self):
        self.x = 1
        self.y = 1


class E(D):
    __slots__ = ()


class F(D):
    __slots__ = ('z',)
    def __init__(self):
        super(F, self).__init__()
        self.z = 1


def vars2(x):
    if hasattr(x, '__dict__'):
        return vars(x)
    else:
        ret = {slot: getattr(x, slot) for slot in x.__slots__}
        for cls in type(x).mro():
            spr = super(cls, x)
            if not hasattr(spr, '__slots__'):
                break
            for slot in spr.__slots__:
                ret[slot] = getattr(x, slot)
        return ret


def main():
    print(vars2(C()))
    print(vars2(D()))
    print(vars2(E()))
    print(vars2(F()))
    OUTPUT = '''\
{'y': 1, 'x': 1}
{'y': 1, 'x': 1}
{'y': 1, 'x': 1}
{'y': 1, 'x': 1, 'z': 1}
'''

if __name__ == '__main__':
    exit(main())

这很棒,我为你的努力鼓掌!这应该可以工作。然而在我的情况下,我只需要调用 ._asdict(),请看一下我下面的答案。 - yelsayed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接