gpt4 book ai didi

python - 何时在 Python 中内联元类的定义?

转载 作者:太空狗 更新时间:2023-10-29 17:05:41 25 4
gpt4 key购买 nike

今天我在 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

还有其他考虑吗?

最佳答案

与嵌套类定义的所有其他形式一样,嵌套元类对于多种“生产用途”可能更“紧凑和方便”(只要您可以不重用该元类,除非通过继承),但可能有点不方便调试和自省(introspection)。

基本上,不是给元类一个适当的顶级名称,你最终会得到一个模块中定义的所有自定义元类基于它们的 __module__ 彼此无法区分。和 __name__属性(如果需要,这是 Python 用来形成它们的 repr)。考虑:

>>> class Mcl(type): pass
...
>>> class A: __metaclass__ = Mcl
...
>>> class B:
... class __metaclass__(type): pass
...
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>

IOW,如果您想检查“哪个类型是 A 类”(元类是类的类型,请记住),您会得到一个清晰而有用的答案——它是 Mcl在主模块中。然而,如果你想检查“哪种类型是 B 类”,答案并不是那么有用:它说它是 __metaclass__main模块,但这甚至不是真的:
>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>>

...那里 实际上没有这样的事情;该代表具有误导性并且不是很有帮助;-)。

一个类的repr本质上是 '%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声明在同一范围内,第二个重新绑定(bind)名称(此处为 A ),但现有实例通过对象而不是名称引用名称的第一个绑定(bind)——因此两个类对象都保留下来,一个只能通过 type (或 __class__ 属性)其实例(如果有的话——如果没有,第一个对象消失)——这两个类具有相同的名称和模块(因此具有相同的表示),但它们是不同的对象。嵌套在类或函数体中的类,或通过直接调用元类(包括 type )创建的类,如果需要调试或内省(introspection),可能会导致类似的混淆。

因此,如果您永远不需要调试或以其他方式内省(introspection)该代码,则嵌套元类是可以的,并且如果这样做的人理解此怪癖,则可以与之共存(尽管它永远不会像使用一个漂亮的真实姓名那样方便,当然——就像调试用 lambda 编码的函数一样,不可能像调试用 def 编码的函数那样方便)。类比于 lambda对比 def您可以合理地声称匿名的“嵌套”定义对于元类来说是可以的,元类非常简单,无需动脑筋,以至于根本不需要调试或内省(introspection)。

在 Python 3 中,“嵌套定义”不起作用——在那里,必须将元类作为关键字参数传递给类,如 class A(metaclass=Mcl): ,所以定义 __metaclass__在体内没有影响。我相信这也表明 Python 2 代码中的嵌套元类定义可能只有在您确定代码永远不需要移植到 Python 3 时才可能是合适的(因为您使该移植变得更加困难,并且需要为此目的取消嵌套元类定义)——换句话说,当某些版本的 Python 3 在速度、功能或第三方面获得巨大的、引人注目的优势时,这些代码将在几年内不会出现——派对支持,通过 Python 2.7(Python 2 的最后一个版本)。

正如计算历史向我们展示的那样,您希望被抛弃的代码有一种令人讨厌的习惯,让您大吃一惊,并且在大约 20 年后仍然存在(而您大约在​​同一时间“多年”编写的代码可能完全是忘记了;-)。这当然似乎建议避免元类的嵌套定义。

关于python - 何时在 Python 中内联元类的定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3483718/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com