如何在类子类化时运行代码?

时间:2013-08-08 12:51:59

标签: python inheritance

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

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

2 个答案:

答案 0 :(得分:55)

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

如果你想在课堂创作的那一刻发生特殊事情,那么你必须修改创建课程的东西 - 即type。这样做的方法是定义type的子类 - 即元类。

元类是它的类,因为类是它的实例。

在Python2中,您将使用

定义类的元类
class SuperClass:
    __metaclass__ = Watcher

其中Watchertype的子类。

在Python3中,语法已更改为

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

答案 1 :(得分:6)

编辑:我的旧帖实际上没有用。来自classmethod的子类无法按预期工作。

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