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

mic*_*c_e 95 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")
Run Code Online (Sandbox Code Playgroud)

应输出

foo
was subclassed by SubClass0
bar
test
was subclassed by SubClass1
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 57

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

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

元类是它的类,因为类就是它的实例.

在Python2中,您将使用定义类的元类

class SuperClass:
    __metaclass__ = Watcher
Run Code Online (Sandbox Code Playgroud)

where Watcher是的子类type.

在Python3中,语法已更改为

class SuperClass(metaclass=Watcher)
Run Code Online (Sandbox Code Playgroud)

两者都相当于

Superclass = Watcher(name, bases, clsdict)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,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")
Run Code Online (Sandbox Code Playgroud)

版画

foo
was subclassed by SubClass0
bar
test
was subclassed by SubClass1
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.但是,您的代码仅适用于python2.在python3中,SuperClass需要是`class SuperClass(metaclass = Watcher):pass` (3认同)

Sin*_*ion 6

编辑:我的旧帖实际上没有用.子类化classmethod不能按预期工作.

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

import types
import inspect

def subclass_hook(func):
    func.is_subclass_hook = True
    return classmethod(func)
Run Code Online (Sandbox Code Playgroud)

我们还想要一种方便的方法来查看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
Run Code Online (Sandbox Code Playgroud)

最后,我们需要一个对信息起作用的元类:对于大多数情况,这里最有趣的事情就是检查每个提供的钩子基础.通过这种方式,超级工作以最不令人惊讶的方式工作.

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)
Run Code Online (Sandbox Code Playgroud)

那应该这样做.

>>> 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
Run Code Online (Sandbox Code Playgroud)