Is there a way to trigger code when my class is subclassed?
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")
Should output
foo
was subclassed by SubClass0
bar
test
was subclassed by SubClass1
Answer
Classes (by default) are instances of type
.
Just as an instance of a class Foo
is created by foo = Foo(...)
,
an instance of type
(i.e. a class) is created by myclass = type(name, bases, clsdict)
.
If you want something special to happen at the moment of class-creation, then you have to modify the thing creating the class -- i.e. type
. The way to do that is to define a subclass of type
-- i.e. a metaclass.
A metaclass is to its class as a class is to its instance.
In Python2 you would define the metaclass of a class with
class SuperClass:
__metaclass__ = Watcher
where Watcher
is a subclass of type
.
In Python3 the syntax has been changed to
class SuperClass(metaclass=Watcher)
Both are equivalent to
Superclass = Watcher(name, bases, clsdict)
where in this case, name
equals the string 'Superclass'
, and bases
is the tuple (object, )
. The clsdict
is a dictionary of the class attributes defined in the body of the class definition.
Note the similarity to myclass = type(name, bases, clsdict)
.
So, just as you would use a class's __init__
to control events at the moment of a instance's creation, you can control events at the moment of a class's creation with a metaclass's __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")
prints
foo
was subclassed by SubClass0
bar
test
was subclassed by SubClass1
No comments:
Post a Comment