jac*_*ack 5 python python-3.x python-attrs
当我为什么要使用attr.ib(default=attr.Factory(list))过attr.ib(default=[])?
从文档中我看到Factory用于生成一个新值,如果你使用带输入的lambda表达式,这是有意义的; 但是,如果您只是生成一个空列表,我不明白为什么要使用它.
我在这里错过了什么?
Mar*_*ers 11
您希望避免将可变对象用作默认值.如果您使用attr.ib(default=[]),生成的是__init__使用该列表对象作为关键字参数default的方法:
def __init__(self, foo=[]):
self.foo = foo
Run Code Online (Sandbox Code Playgroud)
参数的默认值在定义时创建一次.每次调用方法时都不会重新评估它们.然后,在所有实例中共享该对象的任何突变.请参阅"最小惊讶"和可变默认参数.
attr.Factory()但是,使用该方法,默认设置为sentinel,当参数保留为sentinel值时,函数本身的值将替换为调用工厂的结果.这相当于:
def __init__(self, foo=None):
if foo is None:
foo = []
self.foo = foo
Run Code Online (Sandbox Code Playgroud)
所以现在每个实例创建一个新的列表对象.
一个快速演示,展示了差异:
>>> import attr
>>> @attr.s
... class Demo:
... foo = attr.ib(default=[])
... bar = attr.ib(default=attr.Factory(list))
...
>>> d1 = Demo()
>>> d1.foo, d1.bar
([], [])
>>> d1.foo.append('d1'), d1.bar.append('d1')
(None, None)
>>> d1.foo, d1.bar
(['d1'], ['d1'])
>>> d2 = Demo()
>>> d2.foo, d2.bar
(['d1'], [])
Run Code Online (Sandbox Code Playgroud)
因为demo.foo使用共享列表对象,所以对其进行的更改d1.foo在任何其他实例下立即可见.
当我们用来inspect.getargspec()查看Demo.__init__方法时,我们会看到原因:
>>> import inspect
>>> inspect.getargspec(Demo.__init__)
ArgSpec(args=['self', 'foo', 'bar'], varargs=None, keywords=None, defaults=(['d1'], NOTHING))
Run Code Online (Sandbox Code Playgroud)
默认值foo是相同的列表对象,附加的'd1'字符串仍然存在.bar设置为一个sentinel对象(这里使用attr.NOTHING;一个值可以使用Demo(bar=None)而不用转换为列表对象):
>>> print(inspect.getsource(Demo.__init__))
def __init__(self, foo=attr_dict['foo'].default, bar=NOTHING):
self.foo = foo
if bar is not NOTHING:
self.bar = bar
else:
self.bar = __attr_factory_bar()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1214 次 |
| 最近记录: |