【问题标题】:Is there a way to set metaclass after the class definition?有没有办法在类定义之后设置元类?
【发布时间】:2011-07-04 11:45:17
【问题描述】:

为了设置一个类的元类,我们使用__metaclass__ 属性。元类在定义类时使用,因此在类定义之后显式设置它没有效果。

当我尝试显式设置元类时会发生这种情况;

>>> class MetaClass(type):
    def __new__(cls, name, bases, dct):
        dct["test_var"]=True
        return type.__new__(cls, name, bases, dct)
    def __init__(cls, name, bases, dct):
        super(MetaClass, cls).__init__(name, bases, dct)


>>> class A:
    __metaclass__=MetaClass


>>> A.test_var
True
>>> class B:
    pass

>>> B.__metaclass__=MetaClass
>>> B.test_var

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    B.test_var
AttributeError: class B has no attribute 'test_var'

我能想到的最好办法是重新定义整个类并以某种方式动态添加__metaclass__ 属性。或者你知道在类定义之后设置元类的更好方法吗?

【问题讨论】:

    标签: python metaclass


    【解决方案1】:

    您可以在创建类后更改元类,就像更改对象的类一样,但是您会遇到很多问题。对于初学者,初始元类需要与type不同,新元类的__init____new__不会被调用(尽管您可以手动调用__init__或执行__init__的方法'的工作)。

    更改元类最不麻烦的方法可能是从头开始重新创建该类:

    B = MetaClass(B.__name__, B.__bases__, B.__dict__)
    

    但是如果你坚持动态改变元类,你首先需要用一个临时的自定义元类来定义B:

    class _TempMetaclass(type):
        pass
    
    class B:
        __metaclass__ = _TempMetaclass # or: type('temp', (type, ), {})
    

    然后你可以这样定义元类:

    class MetaClass(type):
        def __init__(cls, *a, **kw):
            super(MetaClass, cls).__init__(*a, **kw)
            cls._actual_init(*a, **kw)
        def _actual_init(cls, *a, **kw):
            # actual initialization goes here
    

    然后执行以下操作:

    B.__class__ = MetaClass
    MetaClass._actual_init(B, B.__name__, B.__bases__, B.__dict__)
    

    您还需要确保类的所有初始化都已完成_actual_init。您还可以将classmethod 添加到为您更改元类的元类中。

    这两种解决方案都有一个轻微的缺点,即 B 的基础会受到限制 - 它们需要与原始元类和新元类兼容,但我想这对你来说不是问题。

    【讨论】:

    • 感谢您的回答,B = MetaClass(B.__name__, B.__bases__, B.__dict__) 正是我所需要的。
    【解决方案2】:

    元类的目的不是修改属性访问,而是自定义类的创建。 __metaclass__ 属性定义用于从类定义构造类型对象的可调用对象,默认为 type()。因此,该属性仅在类创建时评估。显然在以后的任何时候更改它都没有任何意义,因为您不能为已经创建的类自定义类创建。详情请见Python Language Reference, 3.4.3 Customizing class creation

    元类根本不是您想要做的事情的正确工具。如果你只是想给一个已经存在的类添加一个新属性,为什么不直接赋值呢?

    【讨论】:

      【解决方案3】:

      您可以认为元类描述了class 语句的作用。这就是为什么稍后设置元类是不可能的:类代码已经转换为类型对象,更改它为时已晚。

      您可以简单地将原始类型子类化以添加元类:

      class C(B):
          __metaclass__=MetaClass
      

      【讨论】:

      • 所以我想问题应该是,是否有办法获取类型对象并对其进行修改,以便
      • @funktku:怎么修改?您可以为任何类型添加属性和方法,即B.test_var = True
      【解决方案4】:

      为什么需要这个?我不相信这是可能的,但如果你能解释一下用例,我可能会建议一个替代方案。

      按照重新定义整个班级的思路,您可以这样做:

      import types
      NewClass = types.ClassType('NewClass', (object, ), {'__metaclass__':MetaClass})
      

      然后将所有旧方法复制过来:

      for item in dir(OldClass):
          setattr(NewClass, item, getattr(OldClass, item))
      

      【讨论】:

      • 将属性设置为{'__metaclass__':MetaClass}不会实现MetaClass。
      • 人力资源部。我想这种说法是有道理的。你只会做 MetaClass(...) 一些事情,不是吗?我从未尝试过以这种方式使用元类。是时候进行实验了...
      【解决方案5】:

      我现在无法对此进行测试(我现在无法在我的机器中访问 Python :-(),但除非 metaclass 是不可变的,否则您可以尝试类似这个:

      >>> class B:
              pass
      >>> setattr(B, "__metaclass__", MetaClass)
      

      同样,我现在无法测试它,所以如果这是一个无效的答案,我很抱歉,只是想提供帮助。 :)

      【讨论】:

      • 它不仅不起作用,而且很荒谬,就像问题一样。元类用于在创建类时挂钩某些东西,在创建类后更改它没有意义。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      • 1970-01-01
      • 2023-01-26
      相关资源
      最近更新 更多