【问题标题】:Python metaclass to run a class method automatically on derived classPython元类在派生类上自动运行类方法
【发布时间】:2012-06-23 06:17:53
【问题描述】:

我想在创建类期间自动运行任何派生类的基类中定义的类方法。例如:

class Base(object):
  @classmethod
  def runme():
    print "I am being run"

  def __metclass__(cls,parents,attributes):
    clsObj = type(cls,parents,attributes)
    clsObj.runme()
    return clsObj


class Derived(Base):
  pass:

这里发生的是,当 Base 被创建时,''runme()'' 将被触发。但是在创建 Derived 时没有任何反应。

问题是:如何在创建 Derived 时让 ''runme()'' 也触发。

这是我迄今为止的想法:如果我将Derived 的metaclass 显式设置为Base,它将起作用。但我不希望这种情况发生。我基本上希望 Derived 使用 Base 的元类,而无需我显式设置它。

【问题讨论】:

  • 你为什么要在类中这样定义__metaclass__?它应该用于将元类设置为外部定义的类。
  • @BrenBarn:但是为什么不能像我一样使用它。据我所知, metaclass 属性是指向类对象还是函数对象都无关紧要。但这确实很重要,我认为你是对的。我想知道为什么?如果可以,请提供一些简单的代码示例。

标签: python metaprogramming metaclass


【解决方案1】:

如果您想在创建类时执行某些操作,您必须在 __metaclass__ 的 __init__ 方法中执行此操作。

class foo(object):
    @classmethod
    def method(cls):
        print 'I am being run by', cls.__name__

    class __metaclass__(type):
        def __init__(self, *args, **kwargs):
            type.__init__(self, *args, **kwargs)
            self.method()

class bar(foo): pass

哪些打印:

I am being run by foo
I am being run by bar

【讨论】:

    【解决方案2】:

    试试这个,

    class MyMetaclass(type): 
    
        def __new__(cls, name, bases, dct):
    
            runme()
    
            return super(MyMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
    

    【讨论】:

      【解决方案3】:

      见this answer。基本上,当调用type(cls,parents,attributes) 时,您正在创建一个类而没有传递有关该类的元类是什么的信息。因此,返回的类没有您想要的元类;相反,它有元类type。 (具有讽刺意味的是,通过定义 __metaclass__ 来执行它,您明确地导致您的类不具有该元类。)您需要调用 type.__new__(meta, cls, parents, attrs),而不是直接调用类型,其中 meta是元类。

      但是,当您定义 __metaclass__ 内联时,您无法实现此目的。从您的__metaclass__ 方法内部,您无法引用该方法,因为它是尚未定义的类的方法。你想做类似的事情

      def __metaclass__(cls, bases, attrs):
          type.__new__(metaclassGoesHere, cls, bases, attrs)
      

      。 . .但是您无法为metaclassGoesHere 输入任何内容来使其做正确的事情,因为您要引用的是您要引用它的方法。

      所以只需在外部定义您的元类。

      【讨论】:

      • 感谢@BrenBarn。你已经为我讲得很清楚了。基本上,需要控制元类对象的实际创建,使用new,我们可以控制它并设置该对象的元类。谢谢。
      【解决方案4】:

      将runme() 重命名为__init__(self) 并且不要覆盖__init__() 方法,每次创建Derived 的实例时都会调用它

      class Base(object):
          def __init__(self):
              print "I am being run"
      
      class Derived(Base):
          pass
      
      dummy_instance = Derived()
      

      复制并粘贴,它将打印I am being run。

      【讨论】:

      • 是的,我知道派生实例会发生这种情况。但我不是在谈论派生实例,而是在谈论派生类。 init 函数初始化实例属性,但我希望在类级别上进行初始化。它是针对我正在设计的 API 的,所以我真的需要一些元类魔法,但我不明白为什么它没有被继承......
      • 您能否提供此用例@BarrySteyn
      • @JonClements 我正在使用 SQLalchemy 制作 ORM。做事的一种模式是使用所谓的声明性反射模式,但这需要调用基类的类方法。我希望在加载类后自动调用此方法。
      • 我只是更多:“你想要实现什么” - 而不是“我们如何以这种方式具体做到这一点”
      • @notbad.jpeg 但 Django 确实使用了该语言的一些角落 :)
      猜你喜欢
      • 2019-05-02
      • 2021-04-14
      • 2013-03-23
      • 2017-05-19
      • 1970-01-01
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多