在Python 2.7上使用AttrDict的奇怪错误

时间:2018-05-04 18:55:20

标签: python python-2.7

我在Python 2.7上使用 AttrDict 2.0时遇到了一个奇怪的重复错误。奇怪的是,传递分配似乎破裂了,但只有在使用AttrDict时才会破坏。

发生的事情是,如果某个对象不存在,我想在对象上实例化一个新列表,然后将数据附加到该对象上。

如果我使用AttrDict,列表会以某种方式转换为元组,我会得到一个异常。

from attrdict import AttrDict

class Test(object):
    pass

try:

    for cls_ in [Test,AttrDict]:
        foo = cls_()        
        print ("\ntesting with class %s" % (cls_))

        #this 
        chk = foo.li = getattr(foo, "li", None) or []
        print("  type(chk):%s, id(chk):%s" % (type(chk),id(chk)))
        print("  type(foo.li):%s, id(foo.li):%s" % (type(foo.li),id(foo.li)))
        foo.li.append(3)
        print ("  success appending with class %s: foo.li:%s" % (cls_, foo.li))

except (Exception,) as e:
    # pdb.set_trace()
    raise

现在查看输出,当我使用Test类时使用AttrDict时。

testing with class <class '__main__.Test'>
  type(chk):<type 'list'>, id(chk):4465207704
  type(foo.li):<type 'list'>, id(foo.li):4465207704
  success appending with class <class '__main__.Test'>: foo.li:[3]

根据预期,自定义Test类,chkfoo.li都是列表,并且具有相同的id。附加作品。

使用AttrDict查看传递,id不匹配,foo.li是元组而不是列表。

testing with class <class 'attrdict.dictionary.AttrDict'>
  type(chk):<type 'list'>, id(chk):4465207848
  type(foo.li):<type 'tuple'>, id(foo.li):4464595080
Traceback (most recent call last):
  File "test_attrdict2.py", line 25, in <module>
    test()
  File "test_attrdict2.py", line 18, in test
    foo.li.append(3)
AttributeError: 'tuple' object has no attribute 'append'

attrdict赋值实际上是否会返回某些属性/访问者对象,第二次访问它时会被更改?

接受了@ abartnet的建议:

from attrdict import AttrDict
a = AttrDict()
a.li = [] 
print(a.li) 

输出: ()

好的,但即使这指出了AttrDict结束时的一些奇怪的行为,传递分配如何也不分配元组?

返工:

from attrdict import AttrDict
a = AttrDict()
b = a.li = [] 
print("a.li:", a.li)    
print("b:",b)   

输出:

('a.li:', ())
('b:', [])

1 个答案:

答案 0 :(得分:2)

这是AttrDict的自动递归的一部分。内联help(您可以在源代码中找到here)比在README中更好地解释了这一点:

  

如果作为属性访问的值是Sequence-type(并且不是字符串/字节),则它将转换为_sequence_type,其中的任何映射都转换为Attrs。

换句话说,为了在执行属性访问时在dictAttrDict值内递归地自动转换任何AttrDict或其他映射,它还会将所有序列转换为(通过默认)tuple。这有点奇怪,但似乎是故意的,有些记录的行为,而不是错误。

>>> a = AttrDict()
>>> a._sequence_type
tuple
>>> a.li = []
>>> a.li
()

更灵活的AttrMap类型允许您指定序列类型,以及通过传递None来禁用此递归重新映射内容的文档:

>>> a = AttrMap(sequence_type=None)
>>> a.li = []
>>> a.li
[]

但当然AttrMap不是dict(尽管它是collections.abc.MutableMapping,更常见的是鸭子类型为dict - 类型)。

  

好的,但即使这指向了AttrDict结尾的一些奇怪行为,传递分配如何也不分配元组?

因为这不是链式分配的工作方式。过度简化:

target1 = target2 = value

......不等于此:

target2 = value
target1 = target2

......但是对此:

target2 = value
target1 = value

理解为什么这是真的最好方法:目标不是表达式,因此没有值。当然,通常完全相同的标记序列作为语法中其他地方的表达式有效,但该标记序列永远不会在赋值语句中的任何位置被评估为表达式 - 否则,简单的事情如{如果d['spam'] = 'eggs'不存在,{1}}必须提出异常。

此外,d['spam']实际上并未将a.li = []分配到任何地方;它实际上会在内部存储tuple([]),并在您尝试访问[]时执行tuple(…)。如果没有阅读消息来源,你无法确定这一点,但是当你认为a.li给你a['li']而不是[]时,它几乎必须是真的。事实上:

()