当我的类被子类化时,有没有办法触发代码?
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
答案 0 :(得分:55)
类(默认情况下)是type
的实例。
就像Foo
创建了一个类foo = Foo(...)
的实例一样,
type
创建myclass = type(name, bases, clsdict)
(即类)的实例。
如果你想在课堂创作的那一刻发生特殊事情,那么你必须修改创建课程的东西 - 即type
。这样做的方法是定义type
的子类 - 即元类。
元类是它的类,因为类是它的实例。
在Python2中,您将使用
定义类的元类class SuperClass:
__metaclass__ = Watcher
其中Watcher
是type
的子类。
在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