How to run code when a class is subclassed?
当我的类是子类时,是否有触发代码的方法?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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") |
应该输出
1 2 3 4 5 | foo was subclassed by SubClass0 bar test was subclassed by SubClass1 |
类(默认情况下)是
如果您希望在创建类的时候发生一些特殊的事情,那么您必须修改创建类的东西——即
元类是对其类的,类是对其实例的。
在python2中,您将使用
1 2 | class SuperClass: __metaclass__ = Watcher |
其中,
在python3中,语法已更改为
1 | class SuperClass(metaclass=Watcher) |
两者都等同于
1 | Superclass = Watcher(name, bases, clsdict) |
在这种情况下,
注意与
因此,就像使用类的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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") |
印刷品
1 2 3 4 5 | foo was subclassed by SubClass0 bar test was subclassed by SubClass1 |
编辑:我以前的帖子实际上不起作用。从
首先,我们想用某种方法来告诉元类,这个特定的方法应该对子类行为有特殊的调用,我们只需要在要调用的函数上设置一个属性。为了方便起见,我们甚至将把函数转换成一个
1 2 3 4 5 6 | import types import inspect def subclass_hook(func): func.is_subclass_hook = True return classmethod(func) |
我们还需要一个方便的方法来查看使用了
1 2 3 4 | def test_subclass_hook(thing): x = (isinstance(thing, types.MethodType) and getattr(thing.im_func, 'is_subclass_hook', False)) return x |
最后,我们需要一个对信息起作用的元类:对于大多数情况,这里要做的最有趣的事情就是检查每个提供的钩子基。这样一来,Super的工作方式就不足为奇了。
1 2 3 4 5 6 7 8 9 | 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) |
这样就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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 |