当类被子类化时如何运行代码?

当我的类被子类化时,有没有触发代码的方法?

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
10522 次浏览

类(默认情况下)是 type的实例。 正如类 Foo的实例是由 foo = Foo(...)创建的一样, type的一个实例(即一个类)是由 myclass = type(name, bases, clsdict)创建的。

如果您希望在创建类的时候发生一些特殊的事情,那么您必须修改创建类的东西——也就是 type。这样做的方法是定义 type的一个子类——也就是一个元类。

元类对其类的作用就像类对其实例的作用一样。

在 Python 2中,定义类的元类

class SuperClass:
__metaclass__ = Watcher

其中 Watchertype的一个子类。

在 Python 3中,语法已更改为

class SuperClass(metaclass=Watcher)

两者都相当于

Superclass = Watcher(name, bases, clsdict)

在本例中,name等于字符串 'Superclass',而 bases是元组 (object, )clsdict是类定义主体中定义的类属性的字典。

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

因此,正如您可以使用类的 __init__来控制实例创建时的事件一样,您也可以使用元类的 __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

编辑: 我的旧文章实际上没有工作。子类从 classmethod不工作的预期。

首先,我们希望有一些方法来告诉元类,这个特定的方法应该具有对子类行为的特殊调用,我们只需要在想要调用的函数上设置一个属性。为了方便起见,我们甚至将函数转换为 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