Wednesday, 30 November 2016

python - How to run code when a class is subclassed?




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

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...