Python classes are profoundly dynamic. We can add new methods to a class or an instance, modify the inheritance chain, switch one method implementation for another one, or dynamically create a new class... Remember from my previous post that classes are instances of the type metaclass or of a custom metaclass.
Given these methods and data attributes that we want in our dynamic class:
# initializer
def init(self, name):
self.name = name
# method
def say_hi(self, to):
return f"{self.name} says hi to {to}"
# class method
@classmethod
def my_class_method(cls, arg):
return f"{cls.__name__}.{cls.my_class_method.__name__} invoked with arg: {arg}, {cls.class_attribute}"
class_items = {
# constructor
"__init__": init,
# class data
"class_attribute": "AAAA",
# methods
"say_hi": say_hi,
"my_class_method": my_class_method
}
def test_dynamic_class(cls):
p1 = cls("Francois")
print(p1.say_hi("Antoine"))
print(cls.my_class_method("BB"))
print(cls.class_attribute)
We can create a class with type as its metaclass by invoking type like this:
# creating class dynamically
Person = type("Person", (object, ), class_items)
test_dynamic_class(Person)
# Francois says hi to Antoine
# Person.my_class_method invoked with arg: BB, AAAA
# AAAA
An alternative would be creating an "empty" class and adding the methods and data attributes like this:
class Person2:
pass
for key, value in class_items.items():
setattr(Person2, key, value)
test_dynamic_class(Person2)
# Francois says hi to Antoine
# Person2.my_class_method invoked with arg: BB, AAAA
# AAAA
If we want to dynamically create a class using a custom metaclass we have to use types.new_class. Notice that rather than passing a dictionary with the attributes, we pass a function that adds those attributes to the "class namespace"
class Meta1(type):
pass
def fill_namespace(ns):
ns.update(class_items)
Person3 = types.new_class(
name="Person3",
#bases=(B, C),
kwds={"metaclass": Meta1},
exec_body=fill_namespace
)
test_dynamic_class(Person3)
# Francois says hi to Antoine
# Person3.my_class_method invoked with arg: BB, AAAA
# AAAA
We can also use the alternative technique of creating an "empty class" (but with our custom metaclass): class Person3(metaclass=Meta1) and then adding the methods and data attributes to it.
No comments:
Post a Comment