Factorías de clases en Python

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

One comment

  1. Jake says:

    ¡Genial! Ya echaba de menos tus publicaciones técnicas, aunque he de admitir que, aunque de esta he entendido los conceptos, los detalles que mencionas de la aplicación se me escapan porque, por desgracia, no he tenido oportunidad de tocar Django.

    Las factorías de clases dan mucho juego, y a partir de ese patrón de diseño se puede derivar otro que también es muy útil, que es el llamado Singleton (no sé si tiene un nombre específico en castellano), que es básicamente una factoría de clases que sólo crea una única instancia de la clase, la primera vez que se llama al método fábrica, y la almacena para devolver siempre la misma en las llamadas subsiguientes. Muy útil, por ejemplo, cuando la clase que creas contiene la conexión a una base de datos o incluye el acceso a algún recurso limitado. Espero que no te moleste la parrafada, sólo es un pequeño aporte.

    Saludos,
    Jake

Leave a Reply

Your email address will not be published. Required fields are marked *