何时在Python中内联元类的定义? [英] When to inline definitions of metaclass in Python?

查看:67
本文介绍了何时在Python中内联元类的定义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

还有其他注意事项吗?

解决方案

就像其他每种形式的嵌套类定义一样,嵌套元类可能更紧凑且方便"(只要您可以不重用该元类就可以了)除了通过继承),可以用于多种生产用途",但调试和自省可能会有些不便.

从根本上讲,不是给元类一个适当的顶级名称,而是将最终使模块中定义的所有自定义元类基于它们的__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类",答案并不是那么有用:它它是mainmain模块中,但事实并非如此:

>>> 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语句,第二个重新绑定名称(此处为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年(尽管您编写的代码完全忘记了古往今来" ;-).当然,这似乎似乎建议避免元类的嵌套定义.

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?

解决方案

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.

这篇关于何时在Python中内联元类的定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆