繁体   English   中英

Python 中的元类:需要澄清的几个问题

[英]Metaclasses in Python: a couple of questions to clarify

在与元类崩溃之后,我深入研究了 Python 中的元编程主题,我有几个问题,恕我直言,在可用的文档中没有明确回答。

  1. 在元类中同时使用__new____init__时,它们的 arguments 必须定义相同吗?
  2. 在元类中定义 class __init__的最有效方法是什么?
  3. 有没有办法在元类中引用 class 实例(通常是self )?
  1. 在元类中同时使用__new____init__时,它们的 arguments 必须定义相同吗?

    我认为Alex Martelli解释得最简洁:

     class Name(Base1,Base2): <<body>> __metaclass__==suitable_metaclass

    方法

    Name = suitable_metaclass('Name', (Base1,Base2), <<dict-built-by-body>>)

    所以暂时停止将合适的元类视为元类,而将其视为 class。 每当你看到

    suitable_metaclass('Name', (Base1,Base2), <<dict-built-by-body>>)

    它告诉你合适的元类的__new__方法必须有一个类似的签名

    def __new__(metacls, name, bases, dct)

    和一个__init__方法

    def __init__(cls, name, bases, dct)

    所以签名并不完全相同,但它们仅在第一个参数上有所不同。

  2. 在元类中定义 class __init__的最有效方法是什么?

    你说的高效是什么意思? 除非您愿意,否则没有必要定义__init__

  3. 有没有办法在元类中引用 class 实例(通常是自我)?

    不,你不应该这样做。 任何依赖于 class 实例的东西都应该在 class 定义中处理,而不是在元类中。

对于 1:任何class 的__init____new__都必须接受相同的 arguments,因为它们将使用相同的 arguments 调用。 It's common for __new__ to take more arguments that it ignores (eg object.__new__ takes any arguments and it ignores them) so that __new__ doesn't have to be overridden during inheritance, but you usually only do that when you have no __new__ at all .

这在这里不是问题,因为如前所述,元类总是使用同一组 arguments 调用,因此您不会遇到麻烦。 至少使用 arguments。 但是,如果您要修改传递给父 class 的 arguments,则需要同时修改它们。

对于 2:您通常不在元类中定义 class __init__ 您可以编写一个包装器并在元类的__new____init__中替换 class 的__init__ ,或者您可以在元类上重新定义__call__ 如果您使用 inheritance,前者的行为会很奇怪。

import functools

class A(type):
    def __call__(cls, *args, **kwargs):
        r = super(A, cls).__call__(*args, **kwargs)
        print "%s was instantiated" % (cls.__name__, )
        print "the new instance is %r" % (r, )
        return r


class B(type):
    def __init__(cls, name, bases, dct):
        super(B, cls).__init__(name, bases, dct)
        if '__init__' not in dct:
            return
        old_init = dct['__init__']
        @functools.wraps(old_init)
        def __init__(self, *args, **kwargs):
            old_init(self, *args, **kwargs)
            print "%s (%s) was instantiated" % (type(self).__name__, cls.__name__)
            print "the new instance is %r" % (self, )
        cls.__init__ = __init__


class T1:
    __metaclass__ = A

class T2:
    __metaclass__ = B
    def __init__(self): 
        pass

class T3(T2):
    def __init__(self):
        super(T3, self).__init__()

调用它的结果是:

>>> T1()
T1 was instantiated
the new instance is <__main__.T1 object at 0x7f502c104290>
<__main__.T1 object at 0x7f502c104290>
>>> T2()
T2 (T2) was instantiated
the new instance is <__main__.T2 object at 0x7f502c0f7ed0>
<__main__.T2 object at 0x7f502c0f7ed0>
>>> T3()
T3 (T2) was instantiated
the new instance is <__main__.T3 object at 0x7f502c104290>
T3 (T3) was instantiated
the new instance is <__main__.T3 object at 0x7f502c104290>
<__main__.T3 object at 0x7f502c104290>

对于 3:是的,来自__call__ ,如上所示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM