今天我在Python here中遇到了一个令人惊讶的元类定义,其中元类定义有效内联。相关部分是
class Plugin(object):
class __metaclass__(type):
def __init__(cls, name, bases, dict):
type.__init__(name, bases, dict)
registry.append((name, cls))
何时使用这样的内联定义是有意义的?
进一步的论点:
一种方式论证是创建的元类在其他地方使用这种技术是不可重用的。一个反驳论点是,使用元类的一个常见模式是定义元类并在一个类中使用它,然后从中继承。例如,在a conservative metaclass定义中
class DeclarativeMeta(type):
def __new__(meta, class_name, bases, new_attrs):
cls = type.__new__(meta, class_name, bases, new_attrs)
cls.__classinit__.im_func(cls, new_attrs)
return cls
class Declarative(object):
__metaclass__ = DeclarativeMeta
def __classinit__(cls, new_attrs): pass
本来可以写成
class Declarative(object): #code not tested!
class __metaclass__(type):
def __new__(meta, class_name, bases, new_attrs):
cls = type.__new__(meta, class_name, bases, new_attrs)
cls.__classinit__.im_func(cls, new_attrs)
return cls
def __classinit__(cls, new_attrs): pass
还有其他考虑吗?
答案 0 :(得分:19)
与所有其他形式的嵌套类定义一样,嵌套的元类可能更“紧凑和方便”(只要你没有重用那个元类除了通过继承)对于许多种类的“生产用途”,但是对于调试和内省来说可能有些不方便。
基本上,不是给元类一个正确的顶级名称,而是基于它们__module__
和{{1属性(如果需要,这是Python用来形成__name__
的东西)。考虑:
repr
IOW,如果你想检查“哪个类是A类”(一个元类是类的类型,请记住),你会得到一个明确而有用的答案 - 它是主模块中的>>> class Mcl(type): pass
...
>>> class A: __metaclass__ = Mcl
...
>>> class B:
... class __metaclass__(type): pass
...
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>
。但是,如果你想检查“哪个类型是B类”,答案就不那么有用了:在Mcl
模块中说它是__metaclass__
,但那不是甚至是真的:
main
实际上, 没有这样的事情;那个repr是误导性的,不是很有帮助; - )。
一个类的repr本质上是>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>>
- 一个简单,有用且一致的规则 - 但在许多情况下,例如,'%s.%s' % (c.__module__, c.__name__)
语句在模块范围内不是唯一的,或者不在模块范围(但在一个函数或类体内),或者甚至不存在(类当然可以在没有class
语句的情况下通过显式调用它们的元类来构建),这可能有些误导(并且最好的解决方案是尽可能避免那些特殊情况,除非使用它们可以获得实质性的优势)。例如,考虑:
class
在同一范围内有两个>>> class A(object):
... def foo(self): print('first')
...
>>> x = A()
>>> class A(object):
... def foo(self): print('second')
...
>>> y = A()
>>> x.foo()
first
>>> y.foo()
second
>>> x.__class__
<class '__main__.A'>
>>> y.__class__
<class '__main__.A'>
>>> x.__class__ is y.__class__
False
语句,第二个重新绑定名称(此处为class
),但现有实例引用的是名称的第一个绑定,而不是名称 - - 所以两个类对象都保留,只能通过其实例的A
(或type
属性)访问(如果有的话 - 如果没有,则第一个对象消失) - 两个类具有相同的名称和模块(因此表示相同),但它们是不同的对象。嵌套在类或函数体中的类,或通过直接调用元类(包括__class__
)创建的类,如果需要调试或内省,可能会引起类似的混淆。
因此,如果您永远不需要调试或以其他方式内省该代码,那么嵌套元类是可以的,并且如果这样做的人理解这个怪癖可以一直生活(尽管它永远不会像使用一个好的那样方便,实名,当然 - 就像调试用type
编码的函数一样,不可能像调试用lambda
编码的函数那么方便。通过类比def
vs lambda
,您可以合理地声称匿名的“嵌套”定义对于非常简单的元类来说是可以的,例如没有脑子,没有必要进行调试或内省。
在Python 3中,“嵌套定义”不起作用 - 在那里,元类必须作为关键字参数传递给类,如def
中所示,因此定义class A(metaclass=Mcl):
in身体没有效果。我相信这也表明Python 2代码中的嵌套元类定义可能只有在您确定代码永远不需要移植到Python 3时才适用(因为您使端口变得更加困难,并且需要为了这个目的而去嵌入元类定义 - 换句话说,当一些版本的Python 3获得速度,功能或第三方的巨大,引人注目的优势时,它将在几年后出现。派对支持,通过Python 2.7(Python 2的最后一个版本)。