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

发布于 2024-09-14 12:06:23 字数 1299 浏览 2 评论 0原文

今天,我在这里发现了Python中元类的令人惊讶的定义,其中包含元类定义有效内联。相关部分是

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

何时使用这样的内联定义有意义?

进一步的论点:

一种论点是,使用此技术创建的元类不能在其他地方重用。一个反驳的论点是,使用元类的常见模式是定义元类并在一个类中使用它,然后从中继承。例如,在保守元类中,定义

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

任何其他考虑因素?

Today I have come across a surprising definition of a metaclass in Python here, with the metaclass definition effectively inlined. The relevant part is

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

When does it make sense to use such an inline definition?

Further Arguments:

An argument one way would be that the created metaclass is not reusable elsewhere using this technique. A counter argument is that a common pattern in using metaclasses is defining a metaclass and using it in one class and then inhertiting from that. For example, in a conservative metaclass the definition

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

could have been written as

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

Any other considerations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

青衫负雪 2024-09-21 12:06:23

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

基本上,您将得到一个模块中定义的所有自定义元类,而不是给元类一个正确的顶级名称,它们基于 __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 类”,答案并不是那么有用:它它是 main__metaclass__ > 模块,但这甚至不是真的:

>>> 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__) —— 一个简单、有用且一致的规则 —— 但在许多情况下,例如 < code>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 编码的函数那么方便一样)。通过与 lambdadef 类比,您可以合理地声称匿名的“嵌套”定义对于元类来说是可以的,这些元类是如此简单,如此简单,无需调试或可以想象,反省是必要的。

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

正如计算的历史向我们展示的那样,您期望被丢弃的代码有一个可爱的习惯,它会让您完全惊讶,并且在大约 20 年后仍然存在(而也许您大约在同一时间编写的代码) “多年来”完全被遗忘了;-)。这似乎肯定会建议避免元类的嵌套定义。

Like every other form of nested class definition, a nested metaclass may be more "compact and convenient" (as long as you're OK with not reusing that metaclass except by inheritance) for many kinds of "production use", but can be somewhat inconvenient for debugging and introspection.

Basically, instead of giving the metaclass a proper, top-level name, you're going to end up with all custom metaclasses defined in a module being undistiguishable from each other based on their __module__ and __name__ attributes (which is what Python uses to form their repr if needed). Consider:

>>> 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, if you want to examine "which type is class A" (a metaclass is the class's type, remember), you get a clear and useful answer -- it's Mcl in the main module. However, if you want to examine "which type is class B", the answer is not all that useful: it says it's __metaclass__ in the main module, but that's not even true:

>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>> 

...there is no such thing, actually; that repr is misleading and not very helpful;-).

A class's repr is essentially '%s.%s' % (c.__module__, c.__name__) -- a simple, useful, and consistent rule -- but in many cases such as, the class statement not being unique at module scope, or not being at module scope at all (but rather within a function or class body), or not even existing (classes can of course be built without a class statement, by explicitly calling their metaclass), this can be somewhat misleading (and the best solution is to avoid, in as far as possible, those peculiar cases, except when substantial advantage can be obtained by using them). For example, consider:

>>> 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

with two class statement at the same scope, the second one rebinds the name (here, A), but existing instances refer to the first binding of the name by object, not by name -- so both class objects remain, one accessible only through the type (or __class__ attribute) of its instances (if any -- if none, that first object disappears) -- the two classes have the same name and module (and therefore the same representation), but they're distinct objects. Classes nested within class or function bodies, or created by directly calling the metaclass (including type), may cause similar confusion if debugging or introspection is ever called for.

So, nesting the metaclass is OK if you'll never need to debug or otherwise introspect that code, and can be lived with if whoever is so doing understand this quirks (though it will never be as convenient as using a nice, real name, of course -- just like debugging a function coded with lambda cannot possibly ever be so convenient as debugging one coded with def). By analogy with lambda vs def you can reasonably claim that anonymous, "nested" definition is OK for metaclasses which are so utterly simple, such no-brainers, that no debugging or introspection will ever conceivably be required.

In Python 3, the "nested definition" just doesn't work -- there, a metaclass must be passed as a keyword argument to the class, as in class A(metaclass=Mcl):, so defining __metaclass__ in the body has no effect. I believe this also suggests that a nested metaclass definition in Python 2 code is probably appropriate only if you know for sure that code will never need to be ported to Python 3 (since you're making that port so much harder, and will need to de-nest the metaclass definition for the purpose) -- "throwaway" code, in other words, which won't be around in a few years when some version of Python 3 acquires huge, compelling advantages of speed, functionality, or third-party support, over Python 2.7 (the last ever version of Python 2).

Code that you expect to be throwaway, as the history of computing shows us, has an endearing habit of surprising you utterly, and being still around 20 years later (while perhaps the code you wrote around the same time "for the ages" is utterly forgotten;-). This would certainly seem to suggest avoiding nested definition of metaclasses.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文