Hay veces que necesitas crear en tiempo de ejecución clases nuevas (Ojo, no nuevas instancias de una clase, sino clases en si mismas). Es útil cuando las clases se tienen que generar en función del contenido, o cuando hay que generar muchas clases parecidas y no queremos estar escribiendo.
Un ejemplo es el framework para aplicaciones web Django. En este framework, se pueden generar fomularios HTML automágicamente, por ejemplo, para crear un formulario html que permita introducir los datos del modelo Tutu, simplemente hay que hacer una clase del tipo:
>>> class TutuForm(ModelForm):
... class Meta:
... model = Tutu
El problema surge cuando tenemos muchos modelos y tenemos que generar autoformularios para todos. Si tenemos que hacer esta clase para cada uno, se nos hace el código muy grande, y tenemos que trabajar con un esquema fijo de urls por ejemplo.
En este caso, una mejor aproximación es utilizar el concepto de Factoría de Clases, es decir, una función (o clase incluso) que genera nuevas clases en tiempo de ejecución. Es posible hacerlo en varios lenguajes, por ejemplo Perl, pero la belleza de la sintáxis de Python lo hace realmente sencillo y claro:
def formClassFactory(modelName):
""" Función que genera clases autoformulario en base al nombre del modelo del que queremos el formulario """
# Buscamos la clase que corresponde al modelo
modelClass = getModel(modelName)
# Si no la encuentra, no devolvemos ninguna
if modelClass == None:
print "No existe un modelo para esa clase"
return None
else:
print "Model Class" + str(modelClass)
# creamos la clase fantasma (sólo tiene ámbito local)
class _innerFormClassTemplate(ModelForm):
class Meta:
# Le injertamos el atributo modelo con la clase
model = modelClass
exclude = ('grupo','id', 'user')
# Devolvemos la clase injertada, con la que podemos definir el formulario modelo del método que queramos
return _innerFormClassTemplate
El código es muy pythónico y claro, pero aún así lo cuento un poco. Como se dice en la docstring, la función genera clases de formulario automáticamente en función del nombre del modelo que queramos. Por ejemplo, si tenemos un modelo que se llama Tutu, esta función devolverá una clase que genere un formulario, y que excluya tres campos que no queremos que aparezcan.
Para ello, primero obtiene la referencia a la clase cuyo nombre tenemos en una cadena. Se podría hacer con un getattr directamente, pero utilizando la función getModel se realizan ciertas comprobaciones de seguridad que no vienen al caso. Luego comprobamos que la clase devuelta no sea nula, porque en ese caso no existiría el modelo del que queremos generar el formulario.
Aquí es donde aparece El Truco. Creamos una clase, con un nombre cualquiera, con la forma que queremos utilizar. Ésta clase tiene ámbito local a la función, así que sólo existe (a priori) en la función local. Aprovechando que tenemos la variable modelClass, fijamos el valor del atributo model de la clase Meta a modelClass, para que la clase nos genere un formulario modelo de la clase. Lo que hacemos finalmente es que la función nos devuelva una referencia a la clase que hemos creado. Como existe esa referencia, la clase sigue existiendo (recordemos que al ser un lenguaje interpretado, las variables existen mientras hayan referencias hacia ellas), y la podemos utilizar como una nueva clase desde el lugar donde hayamos invocado a la función.
En el momento de invocar a la función y obtener la clase, podemos crear una instancia de la misma, como por ejemplo:
form = formClassFactory(_type)(instance = grp)
Como hemos visto, una factoría de clases es una forma muy cómoda de gestionar la creación de muchas clases similares, ahorrándo código y tiempo, sin poner en peligro la legibilidad.
Algunos enlaces para ampliar:
[1] http://www.ibm.com/developerworks/linux/library/l-pymeta.html?S_TACT=105AGX03&S_CMP=ART
[2] http://rgruet.free.fr/PQR25/PQR2.5.html