对象在python3中没有属性'.__ dict__'

时间:2017-01-15 05:10:05

标签: python python-3.x

我有一个在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失败?

2 个答案:

答案 0 :(得分:10)

所以我在经过一番挖掘后找到了答案,这确实是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中的包装器。

答案 1 :(得分:2)

我写了一个小函数,我可能错过了一些边缘情况,但它满足了我写的几个小测试用例(可能因多重继承而被破坏)

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