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
Watcher
是type
的子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