【问题标题】:Metaclasses in Python: a couple of questions to clarifyPython 中的元类:需要澄清的几个问题
【发布时间】:2011-09-27 14:22:32
【问题描述】:

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

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

【问题讨论】:

    标签: python metaprogramming metaclass


    【解决方案1】:
    1. 同时使用__new____init__ 时 在元类中,他们的论点必须 定义一样吗?

      我认为Alex Martelli 解释了 最简洁的:

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

      意思

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

      所以别再想了 合适的元类作为元类 一会儿,只是把它当作一个 班级。每当你看到

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

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

      def __new__(metacls, name, bases, dct)
      

      还有一个__init__ 之类的方法

      def __init__(cls, name, bases, dct)
      

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

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

      高效是什么意思?它是 不需要定义__init__ 除非你愿意。

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

      不,您不需要这样做。 任何取决于班级的事情 实例应在处理 类定义,而不是在 元类。

    【讨论】:

    • 我的意思是当仍然需要定义一些初始例程(即实际的“工作”而不是简单地传递属性)时,什么是最优雅、最清晰、最快或只是你最喜欢的方式类。
    • 你为什么不提供一个你目前如何做的例子?
    • @Red:我认为def __init__(cls, name, bases, dct) 非常标准。没有那么多选择,除了变量名的细微变化,例如klass 对应cls,或clsdict 对应dct 等。
    【解决方案2】:

    对于 1:any 类的 __init____new__ 必须接受相同的参数,因为它们将使用相同的参数调用。 __new__ 接受更多它忽略的参数是很常见的(例如,object.__new__ 接受任何参数并忽略它们)因此__new__ 在继承期间不必被覆盖,但通常只有在你有根本没有__new__

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

    对于 2:您通常不在元类中定义类 __init__。您可以编写一个包装器并在元类的__new____init__ 中替换类的__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>
    

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

    【讨论】:

    • 如果它是如此非典型,那么当我需要动态创建类以及__init__ 时,推荐的方法是什么? (故意不称这个“构造函数”,因为它在元编程上下文中很重要)
    • 要动态创建类,您需要使用类工厂(一个返回可能在其主体中定义的类的函数)。如果您想以编程方式修改静态定义的类,您将使用类装饰器。元类可以用来做这两件事,但它通常比你想要的要多得多。
    猜你喜欢
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多