gpt4 book ai didi

python - 什么时候以及为什么我应该使用 attr.Factory?

转载 作者:太空狗 更新时间:2023-10-30 00:34:29 26 4
gpt4 key购买 nike

我什么时候应该使用 attr.ib(default=attr.Factory(list)) 而不是 attr.ib(default=[])

来自docs我看到工厂用于生成新值,如果您使用带输入的 lambda 表达式,这是有意义的;但是,如果您只是生成一个空列表,我不明白为什么要使用它。

我在这里错过了什么?

最佳答案

您想避免使用可变对象作为默认值。如果您使用 attr.ib(default=[]),生成的是一个 __init__ 方法,使用该列表对象作为关键字参数 default:

def __init__(self, foo=[]):
self.foo = foo

参数的默认值在定义时一次创建。每次调用该方法时都不会重新评估它们。然后对该对象的任何更改都在所有实例之间共享。参见 "Least Astonishment" and the Mutable Default Argument .

然而,使用 attr.Factory() 方法,默认设置为哨兵,当参数保留为哨兵值时,在函数本身是随后被调用工厂的结果替换的值。这相当于:

def __init__(self, foo=None):
if foo is None:
foo = []
self.foo = foo

现在每个实例都创建了一个新的列表对象。

一个演示差异的快速演示:

>>> 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'], [])

因为 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))

foo 的默认值是完全相同的列表对象,附加的 'd1' 字符串仍然存在。 bar 设置为哨兵对象(此处使用 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()

关于python - 什么时候以及为什么我应该使用 attr.Factory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44141104/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com