Python - metaclass 将变量范围从实例更改为 class
Python - metaclass changes variable scope from instance to class
运行 以下代码:
class NonMeta:
def __new__(cls):
x = super().__new__(cls)
x.attr = 100
return x
class Meta(type):
def __new__(mcs, name, bases, dct):
x = super().__new__(mcs, name, bases, dct)
x.attr = 100
return x
class WithMeta(metaclass=Meta):
pass
print(WithMeta.attr)
print(NonMeta.attr)
结果
/usr/bin/python3.7 /home/lookash/PycharmProjects/PythonLearning/classes.py
100
Traceback (most recent call last):
File "/home/lookash/PycharmProjects/PythonLearning/classes.py", line 20, in <module>
print(NonMeta.attr)
AttributeError: type object 'NonMeta' has no attribute 'attr'
为什么 WithMeta class 中的 attr 是一个 class 变量,而在 NonMeta class 中它是一个实例变量?
在NonMeta.__new__
中,x
是NonMeta
的一个实例,因此对x.attr
的赋值是为实例x
创建一个实例属性。
在Meta.__new__
里面,x
是Meta
的一个实例,即一个新的class。在这种情况下,x
是 class WithMeta
,因此对 x.attr
的赋值会创建一个 class 属性。
class
语句在某种意义上是调用元类型的语法糖。也就是说,
class WithMeta(metaclass=Meta):
pass
等同于
WithMeta = Meta('WithMeta', (object,), {})
# == Meta.__new__(Meta, 'WithMeta', (object,), {})
metaclass 的实例是声明他们将使用它的 classes(及其子classes)。因此,当您在 Meta.__new__
中执行 x.attr = 100
时,x
是一个 class(在本例中是 metaclass、WithMeta
的一个实例)。
这与 运行 在 NonMeta.__new__
中的同一行不同,其中 x
是 NonMeta
的一个实例,而 class 本身不是' 永远不会被分配 attr
属性。
出于这个原因,更改 __new__
方法中的代码以使用与 x
不同且更好的名称可能是个好主意。在 NonMeta
中,一个好的名称可能是 self
,因为这是方法中实例的传统名称。在 Meta
中,您可以使用 cls
,因为实例将是 class.
没有 metaclass 的等效代码与你正在做的 with metaclass 很简单:
class NonMeta:
x = 100
classes 中定义的 __new__
方法仅在创建 class 的实例时调用 - 它不会 运行 这样做。在这段代码中:
class NonMeta:
def __new__(cls):
x = super().__new__(cls)
x.attr = 100
return x
来自 super().__new__(cls)
并存储在 x
中的 return 只是实例本身 - 以名称 self
传递给常规实例的同一对象方法(包括__init__
)。
另一方面,Metaclass 在创建 class 时具有其 __new__
(和 __init__
)方法 运行。在普通的模块导入中,这发生在 class
语句及其主体被执行时。在主体的末尾,Python 运行 时间具有创建新 class 所需的所有信息,并且元 class' __new__
被调用.无论它 returns 变成 class。由于它的 return 值设置了 x
属性,它已经在 class.
上可见
运行 以下代码:
class NonMeta:
def __new__(cls):
x = super().__new__(cls)
x.attr = 100
return x
class Meta(type):
def __new__(mcs, name, bases, dct):
x = super().__new__(mcs, name, bases, dct)
x.attr = 100
return x
class WithMeta(metaclass=Meta):
pass
print(WithMeta.attr)
print(NonMeta.attr)
结果
/usr/bin/python3.7 /home/lookash/PycharmProjects/PythonLearning/classes.py
100
Traceback (most recent call last):
File "/home/lookash/PycharmProjects/PythonLearning/classes.py", line 20, in <module>
print(NonMeta.attr)
AttributeError: type object 'NonMeta' has no attribute 'attr'
为什么 WithMeta class 中的 attr 是一个 class 变量,而在 NonMeta class 中它是一个实例变量?
在NonMeta.__new__
中,x
是NonMeta
的一个实例,因此对x.attr
的赋值是为实例x
创建一个实例属性。
在Meta.__new__
里面,x
是Meta
的一个实例,即一个新的class。在这种情况下,x
是 class WithMeta
,因此对 x.attr
的赋值会创建一个 class 属性。
class
语句在某种意义上是调用元类型的语法糖。也就是说,
class WithMeta(metaclass=Meta):
pass
等同于
WithMeta = Meta('WithMeta', (object,), {})
# == Meta.__new__(Meta, 'WithMeta', (object,), {})
metaclass 的实例是声明他们将使用它的 classes(及其子classes)。因此,当您在 Meta.__new__
中执行 x.attr = 100
时,x
是一个 class(在本例中是 metaclass、WithMeta
的一个实例)。
这与 运行 在 NonMeta.__new__
中的同一行不同,其中 x
是 NonMeta
的一个实例,而 class 本身不是' 永远不会被分配 attr
属性。
出于这个原因,更改 __new__
方法中的代码以使用与 x
不同且更好的名称可能是个好主意。在 NonMeta
中,一个好的名称可能是 self
,因为这是方法中实例的传统名称。在 Meta
中,您可以使用 cls
,因为实例将是 class.
没有 metaclass 的等效代码与你正在做的 with metaclass 很简单:
class NonMeta:
x = 100
classes 中定义的 __new__
方法仅在创建 class 的实例时调用 - 它不会 运行 这样做。在这段代码中:
class NonMeta:
def __new__(cls):
x = super().__new__(cls)
x.attr = 100
return x
来自 super().__new__(cls)
并存储在 x
中的 return 只是实例本身 - 以名称 self
传递给常规实例的同一对象方法(包括__init__
)。
Metaclass 在创建 class 时具有其 __new__
(和 __init__
)方法 运行。在普通的模块导入中,这发生在 class
语句及其主体被执行时。在主体的末尾,Python 运行 时间具有创建新 class 所需的所有信息,并且元 class' __new__
被调用.无论它 returns 变成 class。由于它的 return 值设置了 x
属性,它已经在 class.