Python:当一个类被子类化时运行代码

当我的课程被分类时,是否有办法触发代码?

class SuperClass: def triggered_routine(subclass): print("was subclassed by " + subclass.__name__) magically_register_triggered_routine() print("foo") class SubClass0(SuperClass): pass print("bar") class SubClass1(SuperClass): print("test") 

应该输出

 foo was subclassed by SubClass0 bar test was subclassed by SubClass1 

类(默认)是type实例。 就像Foo类的一个实例由foo = Foo(...)创build的那样, type实例(即一个类)由myclass = type(name, bases, clsdict)

如果你想在创build类的时候发生一些特殊的事情,那么你必须修改创build这个类的东西 – 即type 。 要做到这一点的方法是定义一个type的子type – 即元类。

一个元类就是它的类,因为一个类就是它的实例。

在Python2中,你可以用类来定义类的元类

 class SuperClass: __metaclass__ = Watcher 

Watchertype的子type

在Python3中,语法已经更改为

 class SuperClass(metaclass=Watcher) 

两者相当于

 Superclass = Watcher(name, bases, clsdict) 

在这种情况下, name等于string'Superclass' ,而bases是元组(object, )clsdict是在类定义的主体中定义的类属性的字典。

注意与myclass = type(name, bases, clsdict)的相似性。

所以,就像你使用类的__init__来控制创build实例时的事件一样,你可以在创build类的时候用元类的__init__来控制事件:


 class Watcher(type): def __init__(cls, name, bases, clsdict): if len(cls.mro()) > 2: print("was subclassed by " + name) super(Watcher, cls).__init__(name, bases, clsdict) class SuperClass: __metaclass__ = Watcher print("foo") class SubClass0(SuperClass): pass print("bar") class SubClass1(SuperClass): print("test") 

版画

 foo was subclassed by SubClass0 bar test was subclassed by SubClass1 

编辑:我的旧post实际上没有工作。 从classmethod子类不能按预期方式工作。

首先,我们希望有一些方法来告诉元类,这个特定的方法应该具有对子类行为的特殊调用,我们只需要在我们想要调用的函数上设置一个属性。 为了方便起见,我们甚至会把函数转换成一个classmethod这样它的实际基类也能被发现。 我们将返回类方法,以便它可以用作装饰器,这是最方便的。

 import types import inspect def subclass_hook(func): func.is_subclass_hook = True return classmethod(func) 

我们也想要一个方便的方法来看到使用了subclass_hook装饰器。 我们知道classmethod已经被使用了,所以我们将检查它,然后查找is_subclass_hook属性。

 def test_subclass_hook(thing): x = (isinstance(thing, types.MethodType) and getattr(thing.im_func, 'is_subclass_hook', False)) return x 

最后,我们需要一个作用于信息的元类:在大多数情况下,这里最有趣的事情就是检查每个提供的钩子基础。 就这样,超级作品以最不令人吃惊的方式出现。

 class MyMetaclass(type): def __init__(cls, name, bases, attrs): super(MyMetaclass, cls).__init__(name, bases, attrs) for base in bases: if base is object: continue for name, hook in inspect.getmembers(base, test_subclass_hook): hook(cls) 

而且应该这样做。

 >>> class SuperClass: ... __metaclass__ = MyMetaclass ... @subclass_hook ... def triggered_routine(cls, subclass): ... print(cls.__name__ + " was subclassed by " + subclass.__name__) >>> class SubClass0(SuperClass): ... pass SuperClass was subclassed by SubClass0 >>> class SubClass1(SuperClass): ... print("test") test SuperClass was subclassed by SubClass1