Python 元类的 __new__ 方法中的奇怪行为
Odd behaviour within __new__ method of Python metaclass
我在 Python 元类的 __new__
方法中遇到了一个奇怪的行为。我知道以下代码可以正常工作:
def create_property(name, _type):
def getter(self):
return self.__dict__.get(name)
def setter(self, val):
if isinstance(val, _type):
self.__dict__[name] = val
else:
raise ValueError("Type not correct.")
return property(getter, setter)
class Meta(type):
def __new__(cls, clsname, bases, clsdict):
for key, val in clsdict.items():
if isinstance(val, type):
clsdict[key] = create_property(key, val)
return super().__new__(cls, clsname, bases, clsdict)
但是当避免定义 define_property
函数并将代码放在 for
中时 __new__
会发生奇怪的事情。以下是修改后的代码:
class Meta(type):
def __new__(meta, name, bases, clsdict):
for attr, data_type in clsdict.items():
if not attr.startswith("_"):
def getter(self):
return self.__dict__[attr]
def setter(self, val):
if isinstance(val, data_type):
self.__dict__[attr] = val
else:
raise ValueError(
"Attribute '" + attr + "' must be " + str(data_type) + ".")
clsdict[attr] = property(getter, setter)
return super().__new__(meta, name, bases, clsdict)
这个想法是能够创建 类 表现得像表单,即:
class Company(metaclass=Meta):
name = str
stock_value = float
employees = list
if __name__ == '__main__':
c = Company()
c.name = 'Apple'
c.stock_value = 125.78
c.employees = ['Tim Cook', 'Kevin Lynch']
print(c.name, c.stock_value, c.employees, sep=', ')
执行时,开始出现不同的错误,例如:
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 13, in setter
if isinstance(val, data_type):
TypeError: isinstance() arg 2 must be a type or tuple of types
Traceback (most recent call last):
File "main.py", line 38, in <module>
c.stock_value = 125.78
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'name' must be <class 'str'>.
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'stock_value' must be <class 'float'>.
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'employees' must be <class 'list'>.
那么,这是怎么回事?单独定义 create_property
与在 __new__
方法中定义有什么区别?
这是由于范围界定和变量绑定在 python 中的工作方式。您在访问局部变量的循环中定义一个函数;但是这个局部变量在函数 执行 期间查找,在其定义期间未绑定:
fcts = []
for x in range(10):
def f(): print x
fcts.append(f)
for f in fcts: f() #prints '9' 10 times, as x is 9 after the loop
如您所见,您可以简单地使用效用函数在当前循环值上创建一个闭包:
fcts = []
def make_f(x):
def f(): print x
return f
for x in range(10):
fcts.append(make_f(x))
for f in fcts: f() #prints '0' to '9'
另一种可能性是(ab)使用默认参数,因为它们是在函数创建期间分配的:
fcts = []
for x in range(10):
def f(n=x): print n
fcts.append(f)
for f in fcts: f() #prints '0' to '9'
我在 Python 元类的 __new__
方法中遇到了一个奇怪的行为。我知道以下代码可以正常工作:
def create_property(name, _type):
def getter(self):
return self.__dict__.get(name)
def setter(self, val):
if isinstance(val, _type):
self.__dict__[name] = val
else:
raise ValueError("Type not correct.")
return property(getter, setter)
class Meta(type):
def __new__(cls, clsname, bases, clsdict):
for key, val in clsdict.items():
if isinstance(val, type):
clsdict[key] = create_property(key, val)
return super().__new__(cls, clsname, bases, clsdict)
但是当避免定义 define_property
函数并将代码放在 for
中时 __new__
会发生奇怪的事情。以下是修改后的代码:
class Meta(type):
def __new__(meta, name, bases, clsdict):
for attr, data_type in clsdict.items():
if not attr.startswith("_"):
def getter(self):
return self.__dict__[attr]
def setter(self, val):
if isinstance(val, data_type):
self.__dict__[attr] = val
else:
raise ValueError(
"Attribute '" + attr + "' must be " + str(data_type) + ".")
clsdict[attr] = property(getter, setter)
return super().__new__(meta, name, bases, clsdict)
这个想法是能够创建 类 表现得像表单,即:
class Company(metaclass=Meta):
name = str
stock_value = float
employees = list
if __name__ == '__main__':
c = Company()
c.name = 'Apple'
c.stock_value = 125.78
c.employees = ['Tim Cook', 'Kevin Lynch']
print(c.name, c.stock_value, c.employees, sep=', ')
执行时,开始出现不同的错误,例如:
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 13, in setter
if isinstance(val, data_type):
TypeError: isinstance() arg 2 must be a type or tuple of types
Traceback (most recent call last):
File "main.py", line 38, in <module>
c.stock_value = 125.78
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'name' must be <class 'str'>.
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'stock_value' must be <class 'float'>.
Traceback (most recent call last):
File "main.py", line 37, in <module>
c.name = 'Apple'
File "main.py", line 17, in setter
"Attribute '" + attr + "' must be " + str(data_type) + ".")
ValueError: Attribute 'employees' must be <class 'list'>.
那么,这是怎么回事?单独定义 create_property
与在 __new__
方法中定义有什么区别?
这是由于范围界定和变量绑定在 python 中的工作方式。您在访问局部变量的循环中定义一个函数;但是这个局部变量在函数 执行 期间查找,在其定义期间未绑定:
fcts = []
for x in range(10):
def f(): print x
fcts.append(f)
for f in fcts: f() #prints '9' 10 times, as x is 9 after the loop
如您所见,您可以简单地使用效用函数在当前循环值上创建一个闭包:
fcts = []
def make_f(x):
def f(): print x
return f
for x in range(10):
fcts.append(make_f(x))
for f in fcts: f() #prints '0' to '9'
另一种可能性是(ab)使用默认参数,因为它们是在函数创建期间分配的:
fcts = []
for x in range(10):
def f(n=x): print n
fcts.append(f)
for f in fcts: f() #prints '0' to '9'