jueves, 23 de diciembre de 2010

pyLorca: Diseño y diagrama de clases, en Python

Hola. En este post os presento una pequeña aplicación, desarrollada en Python, para diseñar clases en Python de manera rápida, mediante sintaxis Yaml, y con la opción de poder visualizarlo gráficamente, al estilo UML, aunque no es UML. Este software se puede descargar del site de El viaje del navegante:

https://sites.google.com/site/elviajedelnavegante/codigo

Los requisitos para que funcione pyLorca (a día de hoy) es tener instalado pyYAML, que podéis descargar desde su página web, http://pyyaml.org/. Opcionalmente, si se quiere utilizar el entorno gráfico, se ha de tener instalado wxPython, que se puede conseguir en http://www.wxpython.org/.

Al grano...

Con pyLorca de lo que se trata es crear código ó partes de código que normalmente se repiten a la hora de crear clases bases, de las cuales más tarde se heredará (o no). La cuestión es crear, en un fichero Yaml, las especificaciones de las clases y opcionalmente sus atributos, métodos y propiedades, así como comentarios. Automáticamente se generará un fichero Python con el código resultado de dicha especificación.

¿Por qué Yaml? Pues porque tiene una sintaxis fácil de leer y comprensible, apta para poder ser manipulada de manera rápida. Esta es la principal baza de crear scripts en Yaml: poder diseñar jerarquías de clases de manera sencilla y muy rápida.

Un ejemplo sencillo...

Imagina que estás diseñando un problema... defines ciertas clases e identificas sus atributos. Claro, a dichos atributos se les tiene que poder asignar un valor y devolver dicho valor. Bien, podríamos utilizar propiedades en este caso. Otros atributos quizás no interese que sean propiedades, pero como buen programador quieres que sean atributos ocultos. Evidentemente, las clases tienen métodos. ¿La mejor manera de representar esta información? Vamos a verlo...

Se nos plantea un problema que está definido por una clase, padre, que tiene ciertos atributos que podrían accederse como propiedades, esto es, se les aplicará un get y un set. Hay otra clase, hijo, que hereda de la clase padre, y que tiene propiedades suyas. Podríamos escribirlo tal que así:

clase: padre
propiedades: 
  - altura
  - peso
  - edad
  - etnia
clase: hijo
hereda_de: padre
propiedades:
  - color_ojos

Bien, si esto lo escribimos en código Python tendríamos algo parecido a esto:

class padre(object):


    # Constructor
    def __init__(self):
        self.__altura = None
        self.__peso = None
        self.__edad = None
        self.__etnia = None


    # Getters
    def __getAltura(self):
        return self.__altura
    def __getPeso(self):
        return self.__peso
    def __getEdad(self):
        return self.__edad
    def __getEtnia(self):
        return self.__etnia


    # Setters
    def __setAltura(self, altura):
        self.__altura = altura
    def __setPeso(self, peso):
        self.__peso = peso
    def __setEdad(self, edad):
        self.__edad = edad
    def __setEtnia(self, etnia):
        self.__etnia = etnia


    # Properties
    altura = property(fget = __getAltura,fset = __setAltura)
    peso = property(fget = __getPeso,fset = __setPeso)
    edad = property(fget = __getEdad,fset = __setEdad)
    etnia = property(fget = __getEtnia,fset = __setEtnia)


class hijo(padre):


    # Constructor
    def __init__(self):
        padre.__init__(self)
        self.__color_ojos = None


    # Getters
    def __getColor_ojos(self):
        return self.__color_ojos


    # Setters
    def __setColor_ojos(self, color_ojos):
        self.__color_ojos = color_ojos


    # Properties
    color_ojos = property(fget = __getColor_ojos,fset = __setColor_ojos)

Bien, como podemos observar hay una pequeña diferencia de número de líneas de código entre la notación de arriba y la de abajo, en Python. Pues bien, la primera notación es sintaxis Yaml.


pyLorca lo que hace es, a partir de un fichero con la notación Yaml, generar en un fichero Python (.py) el segundo código expuesto.

Un ejemplo algo más completo

El fichero de configuración Yaml tiene dos secciones diferenciadas, una para la configuración del código que se genera en el fichero .py y otra compuesta por un conjunto de documentos que identifican a cada una de las clases que estamos definiendo. ¿Difícil? Más sencillo ahora...

Fichero Yaml con todos los parámetros posibles:

# YAML
fichero_python_a_generar:
comentario:
indentacion:
prefijo_clases:
---
clase:
hereda_de:
propiedades:
atributos:
metodos:
comentario:
...

La primera sección, como podemos ver, sirve para configurar el nombre del fichero Python que se generará, un comentario que se incluirá al principio del módulo, el nivel de indentación que se quiere para el código y un prefijo que se puede incluir en el nombre de las clases cuando se generan.

La segunda sección se denomina conjuntos de documentos. Vemos que podemos definir el nombre de la clase, de qué clase ó clases hereda, la lista de propiedades, la lista de atributos, la lista de métodos y un comentario sobre lo que hace dicha clase.

Si queremos definir 2 clases, tendremos que tener dos documentos:

---
clase:
hereda_de:
propiedades:
atributos:
metodos:
comentario:
---
clase:
hereda_de:
propiedades:
atributos:
metodos:
comentario:
...

Fijarse que un documento (definición de clase) se separa de otro mediante tres guiones ---. Además, para terminar se los documentos utilizamos tres puntos ....

Ahora que sabemos la teoría vamos a crear el mismo ejemplo de antes, pero algo más completo. Decir que no es necesario escribir todos los elementos ó campos Yaml, aunque sí que es obligatorio escribir al menos un elemento para definir las dos secciones.

Fichero ejemplo_navegante.yaml:

# YAML
fichero_python_a_generar: ejemplo_navegante.py
comentario: |
  Este es el módulo de prueba de pyLorca, 
  escrito para el blog de El viaje del navegante.
indentacion: 6
prefijo_clases: base
---
clase: padre
propiedades:
  - altura
  - peso
  - edad
  - etnia
comentario: |
  Esta es la clase padre, de la cual
  hereda la clase hijo.
---
clase: hijo
hereda_de: padre
propiedades:
  - color_ojos
comentario: | 
  Este es un ejemplo de una clase que hereda
  de otra clase.
...

Código generado en el fichero ejemplo_navegante.py:

# -*- coding: utf-8 -*-

'''
Código generado por pyLorca v0.0.1
Fecha creación: 2010-12-23
Definición: Este es el módulo de prueba de pyLorca, 
escrito para el blog de El viaje del navegante.
'''

class base_padre(object):
      '''
      Esta es la clase padre, de la cual
      hereda la clase hijo.
      '''

      # Constructor

      def __init__(self):
            self.__altura = None
            self.__peso = None
            self.__edad = None
            self.__etnia = None

      # Getters

      def __getAltura(self):
            return self.__altura

      def __getPeso(self):
            return self.__peso

      def __getEdad(self):
            return self.__edad

      def __getEtnia(self):
            return self.__etnia

      # Setters

      def __setAltura(self, altura):
            self.__altura = altura

      def __setPeso(self, peso):
            self.__peso = peso

      def __setEdad(self, edad):
            self.__edad = edad

      def __setEtnia(self, etnia):
            self.__etnia = etnia

      # Properties

      altura = property(fget = __getAltura,fset = __setAltura)
      peso = property(fget = __getPeso,fset = __setPeso)
      edad = property(fget = __getEdad,fset = __setEdad)
      etnia = property(fget = __getEtnia,fset = __setEtnia)

class base_hijo(base_padre):
      '''
      Este es un ejemplo de una clase que hereda
      de otra clase.
      '''

      # Constructor

      def __init__(self):
            base_padre.__init__(self)

            self.__color_ojos = None

      # Getters

      def __getColor_ojos(self):
            return self.__color_ojos

      # Setters

      def __setColor_ojos(self, color_ojos):
            self.__color_ojos = color_ojos

      # Properties

      color_ojos = property(fget = __getColor_ojos,fset = __setColor_ojos)

Vale... comentamos lo que hemos hecho. En el fichero Yaml hemos indicado que queríamos que el código Python tuviera una indentación de código de 6 espacios en blanco, que las clases tuvieran un prefijo con el vocablo "base", y que al principio del módulo incluyese un comentario: "Este es el módulo de prueba de pyLorca, escrito para el blog de El viaje del navegante." Ver también que se incluye en el fichero .py el tipo de codificación utf-8, así como una reseña que el código ha sido generado por pyLorca y la fecha de generación.

Con respecto a la definición de las clases, hace exactamente lo que hemos indicado. La clase padre tiene una serie de propiedades (get y set) que hemos diseñado y se ha generado todo el código correspondiente para su utilización, además de un comentario al principio de la clase. La clase hijo, tal como se ha especificado, hereda de la clase padre (mira el __init__), además de tener otras propiedades suyas (...decir propiedades propias suena redundante...).

Sintaxis Yaml

La sintaxis Yaml es muy sencilla, es por ello que he escogido este sistema para definir las clases y no otros, como XML, que resulta algo más tedioso de leer y escribir. Con Yaml es como si hiciéramos la lista de la compra. En San Google hay multitud de manuales sobre Yaml, pero aquí os indico cómo definir ficheros Yaml para pyLorca, que de verdad que es muy fácil.

En el inicio del fichero Yaml incluir la etiqueta siguiente:
#YAML

Una cadena de información es una etiqueta, dos puntos, espacio y el valor de esa etiqueta.
etiqueta: información de la etiqueta

Una lista de parámetros es una etiqueta y dos puntos, y en las líneas siguientes, dos espacios, guion, espacio, elemento de la lista:
lista:
  - elemento1
  - elemento2

Un comentario es una etiqueta con dos puntos, espacio, carácter |, y en las líneas siguientes, dos espacios y el comentario, que se puede dividir en varias líneas, pero al iniciar cada línea, siempre con dos espacios en blanco.
comentario: |
  Comentario de El viaje del navegante
  para los usuarios de Python.

Cosas a tener en cuenta. Mucho cuidado con los espacios en blanco, hay que dejarlos tal como comento, ya que de lo contrario Yaml no funciona, es muy estricto en este tema, al igual que Python con la indentación de código.

Bien, en pyLorca, los elementos propiedades, atributos y metodos son listas. Los elementos comentario son comentarios. Los demás son elementos de cadena.

NOTA IMPORTANTE: Para gente que ya conozca Yaml puede resultar extraño cómo me refiero a algunos elementos, pero este post está orientado al público en general. Perdonad mi simpleza...

Más cosas...

En la definición del fichero Yaml hay elementos que puedes obviar ó no. Por ejemplo, si defines una clase que no hereda de nadie, no es necesario incluir hereda_de:. Aunque si o haces no pasa nada. Incluso si escribes hereda_de: object sigue sin pasar nada. Con esto quiero decir que pyLorca es flexible a la hora de buscar información, pero ten en cuenta que lo que escribas tiene que ir en perfecta sintaxis Yaml.

El ejemplo más completo, herencia múltiple, ¡¡¡más madera!!!

Ahora vamos a incluir todos los parámetros de configuración en un ejemplo y además, vamos a incluir otra clase, para comprobar que pyLorca soporta herencia múltiple.

El fichero Yaml tiene el siguiente contenido:

# YAML
fichero_python_a_generar: ejemplo_navegante.py
comentario: |
  Este es el módulo de prueba de pyLorca, 
  escrito para el blog de El viaje del navegante.
indentacion: 6
prefijo_clases: base
---
clase: padre
propiedades:
  - altura
  - peso
  - etnia
atributos:
  - dinero
metodos:
  - jugar_con_peque
comentario: |
  Esta es la clase padre, de la cual
  hereda la clase hijo.
---
clase: madre
hereda_de: object
propiedades:
  - nivel_inteligencia
atributos:
  - saber_estar
  - cordura
metodos:
  - trabajar
  - amar
comentario: |
  Clase que define a la madre
  que es la reina.
---
clase: hijo
hereda_de: padre, madre
propiedades:
  - color_ojos
atributos:
  - edad
metodos:
  - jugar_con_osito 
comentario: | 
  Esta clase hereda de papá y mamá
  y se hará mayor y grande.
...
Y pyLorca lo interpreta y genera dentro del fichero ejemplo_navegante.py lo siguiente:

# -*- coding: utf-8 -*-

'''
Código generado por pyLorca v0.0.1
Fecha creación: 2010-12-23
Definición: Este es el módulo de prueba de pyLorca, 
escrito para el blog de El viaje del navegante.
'''

class base_padre(object):
      '''
      Esta es la clase padre, de la cual
      hereda la clase hijo.
      '''

      # Constructor

      def __init__(self):
            self.__altura = None
            self.__peso = None
            self.__etnia = None

            self.__dinero = None

      # Métodos

      def jugar_con_peque(self): pass

      # Getters

      def __getAltura(self):
            return self.__altura

      def __getPeso(self):
            return self.__peso

      def __getEtnia(self):
            return self.__etnia

      # Setters

      def __setAltura(self, altura):
            self.__altura = altura

      def __setPeso(self, peso):
            self.__peso = peso

      def __setEtnia(self, etnia):
            self.__etnia = etnia

      # Properties

      altura = property(fget = __getAltura,fset = __setAltura)
      peso = property(fget = __getPeso,fset = __setPeso)
      etnia = property(fget = __getEtnia,fset = __setEtnia)

class base_madre(object):
      '''
      Clase que define a la madre
      que es la reina.
      '''

      # Constructor

      def __init__(self):
            self.__nivel_inteligencia = None

            self.__saber_estar = None
            self.__cordura = None

      # Métodos

      def trabajar(self): pass
      def amar(self): pass

      # Getters

      def __getNivel_inteligencia(self):
            return self.__nivel_inteligencia

      # Setters

      def __setNivel_inteligencia(self, nivel_inteligencia):
            self.__nivel_inteligencia = nivel_inteligencia

      # Properties

      nivel_inteligencia = property(fget = __getNivel_inteligencia,fset = __setNivel_inteligencia)

class base_hijo(base_padre,base_madre):
      '''
      Esta clase hereda de papá y mamá
      y se hará mayor y grande.
      '''

      # Constructor

      def __init__(self):
            base_padre.__init__(self)
            base_madre.__init__(self)

            self.__color_ojos = None

            self.__edad = None

      # Métodos

      def jugar_con_osito(self): pass

      # Getters

      def __getColor_ojos(self):
            return self.__color_ojos

      # Setters

      def __setColor_ojos(self, color_ojos):
            self.__color_ojos = color_ojos

      # Properties

      color_ojos = property(fget = __getColor_ojos,fset = __setColor_ojos)

¿Cómo llamar a pyLorca?

pyLorca funciona en modo texto, simplemente escribiendo python pylorca.py -h tendremos la ayuda en línea de cómo funciona. Si ya tenemos creado nuestro fichero Yaml, python pylorca.py fichero.yaml y genera el fichero Python descrito dentro del fichero Yaml. Si queremos que pyLorca nos genere de forma automática una plantilla, python pylorca.py -p fichero.yaml. Más información en la ayuda.






 pyLorca en modo gráfico... 

pyLorca también tiene la posibilidad de trabajar en modo gráfico, si se tiene instalado wxPython 2.8.11 ó versión compatible. Se invoca mediante python pylorca.py -g.


El funcionamiento gráfico es mucho más intuitivo, si cabe, que en modo texto. Podemos cargar ficheros Yaml desde el botón de que vemos abajo (Fichero conf. YAML). Una vez cargado podemos generar el diagrama gráficamente (botón Generar diagrama) y/o generar el código Python (botón Generar código Python), el cual podemos previsualizar una vez generado. También podemos editar el código Yaml haciendo click en el botón Editor. Como se puede observar podemos imprimir los diagramas que hagamos (botones de impresión).




Una última consideración... ó dos...

Hay veces en las cuales el atributo (no propiedad) de una clase es una instanciación de otra clase. pyLorca lo contempla. Es más, se pueden inicializar los atributos ó propiedades en el propio fichero Yaml. Así por ejemplo, para el siguiente fichero Yaml tenemos:

# YAML
fichero_python_a_generar: ejemplo2.py
---
clase: ubicacion
propiedades:
  - direccion
  - poblacion
  - provincia
  - cp
---
clase: persona
atributos:
  - nombre
  - apellidos
  - nif
---
clase: usuario
hereda_de: persona
propiedades:
  - clave_acceso = 123456
atributos:
  - direccion = ubicacion()
...

Darse cuenta del atributo direccion de la clase persona, el cual está inicializado con una instanciación de la clase ubicacion. Gráficamente pyLorca lo representa de la siguiente forma:


El código Python, generado en el fichero ejemplo2.py, es el siguiente:

# -*- coding: utf-8 -*-

'''
Código generado por pyLorca v0.0.1
Fecha creación: 2010-12-23
'''

class ubicacion(object):

    # Constructor

    def __init__(self):
        self.__direccion = None
        self.__poblacion = None
        self.__provincia = None
        self.__cp = None

    # Getters

    def __getDireccion(self):
        return self.__direccion

    def __getPoblacion(self):
        return self.__poblacion

    def __getProvincia(self):
        return self.__provincia

    def __getCp(self):
        return self.__cp

    # Setters

    def __setDireccion(self, direccion):
        self.__direccion = direccion

    def __setPoblacion(self, poblacion):
        self.__poblacion = poblacion

    def __setProvincia(self, provincia):
        self.__provincia = provincia

    def __setCp(self, cp):
        self.__cp = cp

    # Properties

    direccion = property(fget = __getDireccion,fset = __setDireccion)
    poblacion = property(fget = __getPoblacion,fset = __setPoblacion)
    provincia = property(fget = __getProvincia,fset = __setProvincia)
    cp = property(fget = __getCp,fset = __setCp)

class persona(object):

    # Constructor

    def __init__(self):

        self.__nombre = None
        self.__apellidos = None
        self.__nif = None

class usuario(persona):

    # Constructor

    def __init__(self):
        persona.__init__(self)

        self.__clave_acceso = 123456

        self.direccion = ubicacion()

    # Getters

    def __getClave_acceso(self):
        return self.__clave_acceso

    # Setters

    def __setClave_acceso(self, clave_acceso):
        self.__clave_acceso = clave_acceso

    # Properties

    clave_acceso = property(fget = __getClave_acceso,fset = __setClave_acceso)

¿Cómo se ha programado pyLorca?

pyLorca era al inicio un conjunto de ficheros, donde cada módulo desempeñaba su papel: vista, pyyaml,  tratamiento de sintaxis, editor. Lo he juntado todo en un único fichero por simple comodidad por parte del usuario.

La parte gráfica de pyLorca, escrita en wxPython, utiliza OGL para el diseño de las cajas divididas. Las rutinas de impresión las he sacado de la Demo de wxPython. Es más, si entráis en el código veréis algunas partes (muy pocas) comentadas en inglés, y es porque las he sacado iguales a las originales. Lo importante de aprender wxPython es fijarse en el código, ver y comprender cómo funciona, para poder modificarlo y hacer lo que nosotros queremos que haga.

Mejoras...

Esta aplicación debe de tener muchos bugs, que conforme pase el tiempo iré detectando y corrigiendo. Como mejoras para la siguiente versión caben destacar:
- Creación gráfica de clases.
- Creación gráfica de relaciones entre clases.
- Generación de código Yaml a partir de la creación gráfica de clases.
- Wrapper a la impresión de texto y gráficos, ya que no se controlan los márgenes.

CONCLUSIONES

pyLorca es una aplicación escrita en Python, para crear scripts Python, que utiliza ficheros de sintaxis Yaml para generarlos. Sirve para diseñar con muy pocas líneas de código y en una sintaxis muy fácil, clases y relaciones, para generar el consiguiente código Python.

Saludos.

4 comentarios:

  1. realmente me ha gustado!! lo pruebo y te comento! felicitaciones!

    ResponderEliminar
  2. Hola navegante anónimo. Gracias. Prueba, y si encuentras algún bug, por favor, dímelo, para corregirlo, y poder perfeccionar la herramienta. Saludos

    ResponderEliminar
  3. la idea está buena, yo opino que deberías colgarlo en sourceforge así permitís que la gente te ayude ;) .. me hace acordar mucho en cierto aspecto a programas como el BOUML, EA, etc... deberías utilizarlos así incorporas nuevas ideas, para mejorar tu programa.

    Saludos!

    PD: BOUML es de código abierto, asique eso puede ayudar aún más a tu proyecto.

    ResponderEliminar
  4. Hola programando-soft. Gracias por la sugerencia. Estoy haciendo otra herramienta por el estilo, más orientado a BoUML, tal como comentas, que saldrá a la luz en Diciembre de 2011 ó antes, espero. Será la evolución de pyLorca pero con muchas más y mejores características.

    Un saludo.

    ResponderEliminar