为什么元类要继承类型?
Why metaclass should inherit from type?
我们可以将函数用作元class,我的理解是它们不是从类型派生的,如下所示:
def test_meta(name, bases, atts):
print("testmeta called for " + name)
return type(name,bases,atts);
class Computer:
__metaclass__ = test_meta
def __init__(self,brand,model,price):
self.brand = brand
self.model = model
self.price = price
#
#
ob = Computer('p1','core2duo',21985.25)
然而,当我们写 metaclass 时,它应该是从类型继承的,我无法理解这背后的原因:
class MyMeta:
def __new__(meta, name, bases, dct):
print ('-----------------------------------')
print ("Allocating memory for class", name)
print (meta)
print (bases)
print (dct)
return type.__new__(meta, name, bases, dct)
def __init__(cls, name, bases, dct):
print ('-----------------------------------')
print ("Initializing class", name)
print (cls)
print (bases)
print (dct)
type.__init__(cls,name, bases, dct)
def __call__(mcs, *args, **kwargs):
print ('-----------------------------------')
print ("calling class")
print mcs
class Computer:
__metaclass__ = MyMeta
def __init__(self,brand,model,price):
self.brand = brand
self.model = model
self.price = price
#
#
ob = Computer('p1','core2duo',21985.25)
例如在上面的代码中我无法理解为什么 MyMeta class 应该 inherited from type 当我们显式调用类型函数时,即 new、init、call。
此外,只有在创建 i=instance (ob) 时才会出现错误 "descriptor 'init' requires a 'type' object but received a 'instance'"。
在普通的 Python 代码中,唯一真正在内存中创建 class 对象的调用是 type.__new__
,具有二进制结构和所有需要的字段。使用像 metaclass 这样可调用的函数将不得不实例化 type
本身,或者创建一些根本不是 class 的东西(见下文)。
可以使用本机 C 代码创建另一个 "base metaclass",甚至在纯 Python 中,调用本机 OS 内存分配函数并填充相同的 structure defined for a "type object", - 然而,这只会完成与 type.__new__
已经完成的完全相同的任务,因此这只是一个复杂且容易出错的轮子的重新发明,无法进一步改进,因为由此产生的二进制布局必须与该结构中定义的相同(即实现 "magic fuctions" 的方法的指针,如 __init__
、__geitem__
等,必须与该结构中的偏移量相同)
(此结构中的其他字段,甚至是数据的精美值都可以通过代码继承类型来实现 - 因此无论如何都可以通过普通元 class 实现)
正如你所说,任何可调用的——即使是一个简单的函数,都可以在 class 主体上表示为 "metaclass"(在 Python 2 和 Python 3) - 然而,无论这个可调用函数做什么,在某个时候它都必须调用 type.__new__
(函数通过调用 type
间接调用)。然而,一旦构建了 class,它本身就是一个 Python 对象,它是 class 的一个实例 - class 的类型,(它的__class__
属性)是否有效 metaclass。如果 metaclass 在代码中作为 __metaclass__
属性或作为 Python 3 中的 metaclass kwarg 指向,则为 [=15= 的子 class ],那也将是 metaclass 本身。否则,如果它是一个普通函数,只是在其主体中充当 class 工厂调用 type
,则有效的 metaclass 只是 type.
换句话说:
In [1]: def function_meta(name, bases, ns):
...: return type(name, bases, ns)
...:
In [2]: class test(metaclass=function_meta):
...: pass
...:
In [3]: type(test)
Out[3]: type
In [4]: class ClassMeta(type):
...: pass
...:
...:
In [5]: class test2(metaclass=ClassMeta):
...: pass
...:
In [6]: type(test2)
Out[6]: __main__.ClassMeta
所以,为什么不能使用不继承自 type
的 class?
问题不在于指定的元class 不是从类型继承的 - 可以使用任何可调用对象,如上所示。
尝试这样做时出现的错误是由于调用 type.__new__
时使用非子 class 类型作为第一个参数:
In [11]: class InheritFromOther(object):
...: def __new__(mcls, name, bases, ns):
...: return type.__new__(mcls, name, bases, ns)
...: # the line above is the one that errors - as
...: # mcls here is not a subclass of type.
...: # type(name, bases, ns) would work.
In [12]: class test4(metaclass=InheritFromOther):
...: pass
...:
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-bf5b1fa4bb7f> in <module>()
----> 1 class test4(metaclass=InheritFromOther):
2 pass
<ipython-input-11-62d1fe46490b> in __new__(mcls, name, bases, ns)
1 class InheritFromOther(object):
2 def __new__(mcls, name, bases, ns):
----> 3 return type.__new__(mcls, name, bases, ns)
4
TypeError: type.__new__(InheritFromOther): InheritFromOther is not a subtype of type
现在,如果我们调用 type.__new__
并将类型的有效子 class 作为第一个参数:
In [13]: class InheritFromOtherTake2(object):
...: def __new__(mcls, name, bases, ns):
...: return type.__new__(type, name, bases, ns)
...:
In [14]: class test5(metaclass=InheritFromOtherTake2):
...: pass
...:
In [15]: type(test5)
Out[15]: type
为了完整起见,如上所述,确实可以将可调用对象用作元class return 类型实例以外的其他东西(或子class它)。在那种情况下,由 class
语句主体生成的对象将不会是 class,而是任何可调用的 returned:
In [7]: def dict_maker_meta(name, bases, ns):
...: return ns
...:
In [8]: class test3(metaclass=dict_maker_meta):
...: a = 1
...: b = 2
...: c = 'test'
...:
In [9]: type(test3)
Out[9]: dict
In [10]: test3
Out[10]:
{'__module__': '__main__',
'__qualname__': 'test3',
'a': 1,
'b': 2,
'c': 'test'}
我们可以将函数用作元class,我的理解是它们不是从类型派生的,如下所示:
def test_meta(name, bases, atts):
print("testmeta called for " + name)
return type(name,bases,atts);
class Computer:
__metaclass__ = test_meta
def __init__(self,brand,model,price):
self.brand = brand
self.model = model
self.price = price
#
#
ob = Computer('p1','core2duo',21985.25)
然而,当我们写 metaclass 时,它应该是从类型继承的,我无法理解这背后的原因:
class MyMeta:
def __new__(meta, name, bases, dct):
print ('-----------------------------------')
print ("Allocating memory for class", name)
print (meta)
print (bases)
print (dct)
return type.__new__(meta, name, bases, dct)
def __init__(cls, name, bases, dct):
print ('-----------------------------------')
print ("Initializing class", name)
print (cls)
print (bases)
print (dct)
type.__init__(cls,name, bases, dct)
def __call__(mcs, *args, **kwargs):
print ('-----------------------------------')
print ("calling class")
print mcs
class Computer:
__metaclass__ = MyMeta
def __init__(self,brand,model,price):
self.brand = brand
self.model = model
self.price = price
#
#
ob = Computer('p1','core2duo',21985.25)
例如在上面的代码中我无法理解为什么 MyMeta class 应该 inherited from type 当我们显式调用类型函数时,即 new、init、call。 此外,只有在创建 i=instance (ob) 时才会出现错误 "descriptor 'init' requires a 'type' object but received a 'instance'"。
在普通的 Python 代码中,唯一真正在内存中创建 class 对象的调用是 type.__new__
,具有二进制结构和所有需要的字段。使用像 metaclass 这样可调用的函数将不得不实例化 type
本身,或者创建一些根本不是 class 的东西(见下文)。
可以使用本机 C 代码创建另一个 "base metaclass",甚至在纯 Python 中,调用本机 OS 内存分配函数并填充相同的 structure defined for a "type object", - 然而,这只会完成与 type.__new__
已经完成的完全相同的任务,因此这只是一个复杂且容易出错的轮子的重新发明,无法进一步改进,因为由此产生的二进制布局必须与该结构中定义的相同(即实现 "magic fuctions" 的方法的指针,如 __init__
、__geitem__
等,必须与该结构中的偏移量相同)
(此结构中的其他字段,甚至是数据的精美值都可以通过代码继承类型来实现 - 因此无论如何都可以通过普通元 class 实现)
正如你所说,任何可调用的——即使是一个简单的函数,都可以在 class 主体上表示为 "metaclass"(在 Python 2 和 Python 3) - 然而,无论这个可调用函数做什么,在某个时候它都必须调用 type.__new__
(函数通过调用 type
间接调用)。然而,一旦构建了 class,它本身就是一个 Python 对象,它是 class 的一个实例 - class 的类型,(它的__class__
属性)是否有效 metaclass。如果 metaclass 在代码中作为 __metaclass__
属性或作为 Python 3 中的 metaclass kwarg 指向,则为 [=15= 的子 class ],那也将是 metaclass 本身。否则,如果它是一个普通函数,只是在其主体中充当 class 工厂调用 type
,则有效的 metaclass 只是 type.
换句话说:
In [1]: def function_meta(name, bases, ns):
...: return type(name, bases, ns)
...:
In [2]: class test(metaclass=function_meta):
...: pass
...:
In [3]: type(test)
Out[3]: type
In [4]: class ClassMeta(type):
...: pass
...:
...:
In [5]: class test2(metaclass=ClassMeta):
...: pass
...:
In [6]: type(test2)
Out[6]: __main__.ClassMeta
所以,为什么不能使用不继承自 type
的 class?
问题不在于指定的元class 不是从类型继承的 - 可以使用任何可调用对象,如上所示。
尝试这样做时出现的错误是由于调用 type.__new__
时使用非子 class 类型作为第一个参数:
In [11]: class InheritFromOther(object):
...: def __new__(mcls, name, bases, ns):
...: return type.__new__(mcls, name, bases, ns)
...: # the line above is the one that errors - as
...: # mcls here is not a subclass of type.
...: # type(name, bases, ns) would work.
In [12]: class test4(metaclass=InheritFromOther):
...: pass
...:
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-bf5b1fa4bb7f> in <module>()
----> 1 class test4(metaclass=InheritFromOther):
2 pass
<ipython-input-11-62d1fe46490b> in __new__(mcls, name, bases, ns)
1 class InheritFromOther(object):
2 def __new__(mcls, name, bases, ns):
----> 3 return type.__new__(mcls, name, bases, ns)
4
TypeError: type.__new__(InheritFromOther): InheritFromOther is not a subtype of type
现在,如果我们调用 type.__new__
并将类型的有效子 class 作为第一个参数:
In [13]: class InheritFromOtherTake2(object):
...: def __new__(mcls, name, bases, ns):
...: return type.__new__(type, name, bases, ns)
...:
In [14]: class test5(metaclass=InheritFromOtherTake2):
...: pass
...:
In [15]: type(test5)
Out[15]: type
为了完整起见,如上所述,确实可以将可调用对象用作元class return 类型实例以外的其他东西(或子class它)。在那种情况下,由 class
语句主体生成的对象将不会是 class,而是任何可调用的 returned:
In [7]: def dict_maker_meta(name, bases, ns):
...: return ns
...:
In [8]: class test3(metaclass=dict_maker_meta):
...: a = 1
...: b = 2
...: c = 'test'
...:
In [9]: type(test3)
Out[9]: dict
In [10]: test3
Out[10]:
{'__module__': '__main__',
'__qualname__': 'test3',
'a': 1,
'b': 2,
'c': 'test'}