I wanted to create a class at runtime to do updates to databases, for example, but without being dependent on DBMS, instead using python and wondering if it's possible to create these classes at runtime.
I wanted to create a class at runtime to do updates to databases, for example, but without being dependent on DBMS, instead using python and wondering if it's possible to create these classes at runtime.
In the Python language, it is trivial to create classes at runtime - and with some work and care, you can even create instrumented classes with ORM (Relational Object Mapping) functionalities that work well.
Your system making good use of them, of course, will depend on other components - not only the party "talking" to the database have to use data from these dynamically built classes to be able to do something useful. (Which fields will be displayed in the UI? In what order? Which users can change which fields?)It is possible to construct an example that introspects the database and from there is completely dynamic. (And it will actually make little difference if the elements of a given table are represented by a Python class internally, or in a "stupid" dictionary structure) - but rule path is better you define well some templates for your system and have these templates - at least some fields - placed as code in your system.
All this said, to create a new class in Python, just call the type
constructor, passing the parameters "name", "bases" and "namespace" - the latter is a dictionary with statements equivalent to we put in the body of a class written with the keyword class
:
def init(self):
print("Inicializando ", self)
MinhaClasse = type("MinhaClasse", (object,), {"__init__": init})
MinhaClasse()
Ready - MyClass was created at runtime with values that could be in goblets. Now, to give some use to this is another story. (For example, you can set a "attributes" parameter in the namespace - and cause the __init__
code to receive these attributes as named parameters and automatically assign them to the instance:
class Base:
atributos = ""
def __init__(self, **kwargs):
for atributo in atributos.split():
setattr(self, atributo, kwargs.pop(atributo, None))
ClassePessoa = type("ClassePessoa", (Base,), {'atributos': 'nome idade'})
p1 = ClassePessoa(nome='Lucas', idade=23)
print(p1.nome)
( TL; DR )
It is possible and actually relatively easy, though laborious. I will reproduce an explanation / example well spread on the internet:
First, let's focus on the relationship between type
and class
. When we define classes we can ask ourselves "what happens behind the scenes". We can see that if we apply type to an object, return is the class from which that object was instantiated.
a = [1, 2, 3]
b = "Hello"
print(type(a), type(b))
<class 'list'> <class 'str'>
If we apply type in the class name, we get the "type" class:
print(type(list), type(str))
<class 'type'> <class 'type'>
This is similar to applying type in type (a) and type (b)
a = [4, 5, 9]
b = "Hello"
print(type(a), type(b))
print(type(type(a)), type(type(b)))
<class 'list'> <class 'str'>
<class 'type'> <class 'type'>
A user-defined class (or class object) is an instance of the "type" class. As you can see, classes are created from type. In python3 there is no difference between "classes" and "types". These terms can in many cases be used as synonyms.
The fact that classes are instances of a "type" class allows or supports the use of metaclasses. We can create classes, which inherit directly from the "type" class, so a metaclass is a subclass of the "type" class.
Type can be called with three arguments
type(classname, superclasses, attributes_dict)
When type is called with 3 arguments, it returns a new object type. This provides a way to dynamically overload a class.
Let's look at a simple definition
class A:
pass
x = A()
print(type(x))
<class '__main__.A'>
We can use "type" to define class A:
A = type("A", (), {})
x = A()
print(type(x))
<class '__main__.A'>
When we call "type", the type call type is called. The call method performs two other methods: new and init :
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(cls, classname, superclasses, attributedict)
The new method creates and returns the new class object, and then the init method initializes the newly created class.
See two identical classes (Robot), defined in two completely different syntactic terms:
class Robot1:
counter = 0
def __init__(self, name):
self.name = name
def sayHello(self):
return "Hi, I am " + self.name
def Rob_init(self, name):
self.name = name
Robot2 = type("Robot2",
(),
{"counter":0,
"__init__": Rob_init,
"sayHello": lambda self: "Hi, I am " + self.name})
Now we will instantiate the two classes and present their attributes:
r1 = Robot('Robocop')
print(r1.name)
print(r1.sayHello())
print(r1.__dict__)
print(r1.__dict__)
Robocop
Hi, I am Robocop
{'name': 'Robocop'}
{'name': 'Robocop'}
r2 = Robot2('Robocop')
print(r2.name)
print(r2.sayHello())
print(r2.__dict__)
print(r2.__dict__)
Robocop
Hi, I am Robocop
{'name': 'Robocop'}
{'name': 'Robocop'}
With this explanation you can see that in python it is relatively easy (compared to other languages / platforms) to define classes dynamically.
View this explanation and code running on a jupyter notebook.