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

时间:2011-07-03 11:11:06

标签: python metaprogramming metaclass

在使用元类崩溃之后,我深入研究了Python中元编程的主题,我有几个问题,即imho,在可用的文档中没有明确的答案。

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

2 个答案:

答案 0 :(得分:2)

  1. 同时使用__new____init__ 在元类中,他们的论点必须 定义相同吗?

    我认为Alex Martelli解释道 它最简洁:

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

    装置

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

    所以不要再想了 proper_metaclass作为元类 暂时只是把它当作一个 类。每当你看到

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

    它告诉你 proper_metaclass的__new__ 方法必须有签名

    之类的东西
    def __new__(metacls, name, bases, dct)
    

    和<{p>}之类的__init__方法

    def __init__(cls, name, bases, dct)
    

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

  2. 什么是最有效的定义方式 元类中的类__init__

    高效的是什么意思?它是 没有必要定义__init__ 除非你想。

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

    不,你不应该这样做。 任何依赖于课程的东西 实例应该在 类定义,而不是在 元类。

答案 1 :(得分:1)

For 1:任何类的__init____new__必须接受相同的参数,因为它们将使用相同的参数进行调用。 __new__采用它忽略的更多参数是很常见的(例如object.__new__接受任何参数并忽略它们),因此在继承期间不必覆盖__new__,但通常只有当你没有__new__时才这样做。

这不是问题所在,因为正如所述,始终使用相同的参数集调用元类,因此您不会遇到麻烦。至少有论据。但是,如果要修改传递给父类的参数,则需要在两者中修改它们。

For 2:您通常不在元类中定义类__init__。您可以编写包装器并替换元类的__init____new__中的类__init__,也可以重新定义元类的__call__。如果使用继承,前者会很奇怪。

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>

For 3:Yes,from __call__,如上所示。