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