元类、setattr和属性在Python和怪异行为中的使用
我编写了一个元类、setattr和属性在Python和怪异行为中的使用,python,closures,Python,Closures,我编写了一个元类,它定义了使用它的类的运行时属性。它只是从REQUIRED_KEYS属性中获取这些属性,然后声明一个属性,例如: class Base(object): REQUIRED_KEYS = () class __metaclass__(type): def __init__(cls, name, bases, nmspc): type.__init__(cls, name, bases, nmspc)
元类
,它定义了使用它的类的运行时属性。它只是从REQUIRED_KEYS属性中获取这些属性,然后声明一个属性
,例如:
class Base(object):
REQUIRED_KEYS = ()
class __metaclass__(type):
def __init__(cls, name, bases, nmspc):
type.__init__(cls, name, bases, nmspc)
for attr in cls.REQUIRED_KEYS:
setattr(cls, attr, property(lambda self: self._dict.get(attr, None)))
def __init__(self, **kwargs):
self._dict = dict(**kwargs)
class Config(Base):
REQUIRED_KEYS = ('foo', 'bar')
如果未指定一个键,则在运行时中定义的属性将返回一个None
值。我希望以下执行的行为正确,返回1
和None
:
config = Config(**{'foo': 1})
config.foo
config.bar
但是对于foo
属性,它返回None,其中正确的预期值为1
如果我通过一个闭包修改属性
函数使用的lambda
,如下面的代码片段所示,它可以正常工作
class __metaclass__(type):
def __init__(cls, name, bases, nmspc):
def get(attr):
def _get(self):
return self._dict.get(attr, None)
return _get
type.__init__(cls, name, bases, nmspc)
for attr in cls.REQUIRED_KEYS:
setattr(cls, attr, property(get(attr)))
在执行第一个代码段之后,我意识到lambda函数总是使用最后一个attr
值调用。这意味着,当我们试图访问foo
属性时,lambda使用bar
值
有人知道这里发生了什么吗?这是python中闭包的工作方式
attr
的值不存储在lambda中,只是对变量的引用
在代码中,所有lambda都包含对attr
变量的相同引用,当调用lambda时,它将包含其最后一个值
可以使用默认参数强制复制该值:
setattr(cls, attr, property(lambda self, attr=attr: self._dict.get(attr, None)))
^^^^^^^^^
这就是闭包在python中的工作方式
attr
的值不存储在lambda中,只是对变量的引用
在代码中,所有lambda都包含对attr
变量的相同引用,当调用lambda时,它将包含其最后一个值
可以使用默认参数强制复制该值:
setattr(cls, attr, property(lambda self, attr=attr: self._dict.get(attr, None)))
^^^^^^^^^
根据,如果名称在代码块中使用,但未绑定到代码块(且未声明为全局),则该使用将被视为对最近封闭函数的引用。(我的)。名称
attr
未在lambda getter中声明,因此它是元类\uuuu init\uuuu
函数中变量attr
的引用,该函数始终指向最后一个属性。另外,dict(**kwargs)
(在Base
class init函数中)是不必要的,因为kwargs
已经是一个字典(您可以键入self.\u dict=kwargs
),在您的示例中,您可以将类初始化为Config(foo=1)
,而不是Config(**{foo':1}),如果在代码块中使用了名称,但该名称未绑定到代码块(并且未声明为全局),则该名称的使用将被视为对最近封闭函数的引用。(我的)。名称attr
未在lambda getter中声明,因此它是元类\uuuu init\uuuu
函数中变量attr
的引用,该函数始终指向最后一个属性。此外,dict(**kwargs)
(在Base
class init函数中)是不必要的,因为kwargs
已经是一个字典(您可以键入self.\u dict=kwargs
),在您的示例中,您可以将类初始化为Config(foo=1)
,而不是Config(**{foo':1})
,谢谢,我在这里没有意识到这个问题。谢谢,我在这里没有意识到这个问题。关于第二个问题,用一个隐含的句子从一个kwars或一个显式的句子来建立一个字典只是做同样的事情的不同方式,但不是一个问题。关于最后一条注释,Config(**{'foo':'1'})
实际上来自类似d={'foo':1};Config(**d)
只是在我的评论中放松了一下,让它更具可读性。关于第二个问题,给出一个隐式句子来从kwars或显式语句构建字典只是做同样事情的不同方法,但不是问题。关于最后一条注释,Config(**{'foo':'1'})
实际上来自类似d={'foo':1};Config(**d)
只是在我的注释中放松了,使其可读。