Generando un proyecto
Vamos
a generar un "proyecto". Un proyecto es una serie de programas, cada
uno en un fichero .py, que actúan de manera coordinada. Cada fichero o
programa .py se denomina en Python módulo.
Empezamos por crear una carpeta en cualquier lugar denominada proyecto. Dentro de esta carpeta, a su vez, creamos una subcarpeta denominada clases.
Dentro de la carpeta proyecto se encuentra mi módulo principal, denominado miprograma.py
La carpeta clases
es lo que se conoce como paquete en Python. Dentro de ella, se pueden
organizar varios módulos que podré llamar desde otros módulos y, en
particular, desde el programa principal. Dentro de la carpeta clases se encuentra...:
- El módulo misclases.py, que contendrá la clase "fraccion"
- El módulo vacío __init__.py ; este módulo no sirve para nada, está vacío, pero indica a Python que la carpeta donde se encuentra está formando un paquete.
- Archivos .pyc , que no necesitamos tocar, ya que los crea el mismo programa.
- La carpeta funciones, una subcarpeta que se comportará como paquete para el módulo misclases.py. Dentro de ella tenemos:
- El módulo vacío __init__.py que indicará que funciones es un paquete.
- El módulo misfunciones.py
- Archivos .pyc
= = =
¿Qué programa contiene cada módulo? Veámoslos por partes1.- Módulo misfunciones
En este módulo sólo hay una función, la que descompone un número en sus factores devolviendo una lista. Del programa "fraccion" del tema anterior, mejora el que es capaz de detectar si un número es negativo y calcularle sus factores, incluyendo el factor del signo (-1)# -*- coding: utf-8 -*- # ========================= # Funciones # ========================= # Devuelve lista con los factores descompuestos def descomponer(num): lista = [] if num<0: lista.append(-1) num = -1 * num pivote = 2 while pivote<num: if num % pivote != 0: pivote = pivote + 1 else: #introduce el pivote en la lista lista.append(pivote) #divide num entre el pivote num = num / pivote # print num, pivote #Cuando acaba introduce el número lista.append(num) return lista= = =
2.- Módulo misclases
Este módulo, en un nivel superior, define la clase fraccion. Al principio observamos la línea import funciones.misfunciones as fun , como novedad. Esta línea importa el módulo misfunciones del paquete funciones y se referirá a él como fun. De hecho, en las líneas 30 y 31 se utiliza la función descomponer como fun.descomponer(...); se está llamando a la función descomponer del paquete funciones.
# -*- coding: utf-8 -*-
import funciones.misfunciones as fun
# =========================
# Clases
# =========================
class fraccion(object):
def __init__(self, x0 = 1, y0 = 1):
try:
self.numerador = x0
self.denominador = y0
test = x0 / y0
return
except ZeroDivisionError:
raise
def __str__(self):
if self.denominador >1:
return str(self.numerador) + "/" + str(self.denominador)
elif self.denominador == 1:
return str(self.numerador)
else:
return "No es posible representar"
def reducir(self):
# Obtiene componentes
listanumerador = fun.descomponer(self.numerador)
listadenominador = fun.descomponer(self.denominador)
# bucle que empieza a reducir
listaIntermedia = []
for i in listanumerador:
if i in listadenominador:
listadenominador.remove(i)
listaIntermedia.append(i)
# print "Numerador: ",listanumerador
# print "Intermedia: ",listaIntermedia
# print "Denominador: ",listanumerador
for i in listaIntermedia:
listanumerador.remove(i)
# convierte la fracción a la reducida
self.numerador = 1
for i in listanumerador:
self.numerador *= i
self.denominador = 1
for i in listadenominador:
self.denominador *= i
# print listanumerador, listadenominador, listaIntermedia
return
def __add__(self, otra):
numerador = self.numerador*otra.denominador + self.denominador*otra.numerador
denominador = self.denominador * otra.denominador
suma = fraccion(numerador, denominador)
suma.reducir()
return suma
def __mul__(self, otra):
numerador = self.numerador * otra.numerador
denominador = self.denominador * otra.denominador
multiplicar = fraccion(numerador, denominador)
multiplicar.reducir()
return multiplicar
def __sub__(self,otra):
resta = fraccion(1,1)
resta.numerador = -1 * otra.numerador
resta.denominador = otra.denominador
restar = self + resta
restar.reducir()
return restar
def inversa(self):
a = fraccion(self.denominador,self.numerador)
a.reducir()
return a
def __div__(self, otra):
dividir = otra.inversa()
division = self * dividir
division.reducir()
return division
= = =
3.- Módulo o programa principal miprograma
Por último, analizo el programa principal. En él, la primera línea (3) escrita sería import clases.misclases as miclase , llamando al módulo misclases del paquete clases y llamándolo por el alias miclase. En la línea 16, dentro de la función introducir() se llama a la clase fraccion que estaría en miclase. Es suficiente con esta sola llamada ya que declarando los objetos, los métodos están implícitos a ellos.
# -*- coding: utf-8 -*- import clases.misclases as miclase # ========================= # Funciones # ========================= def introducir(): t = 5 while t>0: try: print ("Tienes %d oportunidades de introducir un dato correcto: ") % (t) numerador = int(raw_input("Introduce numerador: ")) denominador = int(raw_input("Introduce numerador: ")) a = miclase.fraccion(numerador, denominador) return a except ZeroDivisionError: print ("El denominador no puede ser cero\n") t = t - 1 except TypeError: print ("No has introducido un tipo correcto. Debe ser entero\n") t = t - 1 except ValueError: print ("No has introducido un valor entero\n") t = t - 1 except Exception, ex: print ("Error producido :") + str(ex) raise raise ValueError, "Has tenido cinco oportunidades de introducir un dato correcto" # ========================= # Programa principal # ========================= print "Introduce fracción primera: " f1 = introducir() print "primera fracción:", f1, "\n" print "Introduce fracción segunda: " f2 = introducir() print "segunda fracción:", f2, "\n" f1.reducir() print "primera fracción reducida:", f1, "\n" f2.reducir() print "segunda fracción reducida:", f2, "\n" print "Inversa 1ª fracción reducida:", f1.inversa(), "\n" print "Inversa 2ª fracción reducida:", f2.inversa(), "\n" print "Suma de las dos: ", f1 + f2, "\n" print "Resta de las dos: ", f1 - f2, "\n" print "Multiplicar las dos: ", f1 * f2, "\n" print "Dividir las dos: ", f1 / f2, "\n"
= = =
4.- Esquema de árbol
= = =
Hablando de módulos
Si
nos paramos un momento a pesar, nos daremos cuenta que escribir
cualquier tipo de programa en un sólo fichero puede ser largo y
sumamente tedioso. Hasta ahora nuestros programas han sido "cortos".
Ocupaban relativamente poco espacio pero... ¿y si se complican?
Necesariamente tenemos que dividirlos por partes y ordenarlos. Para ello
se implementan los paquetes y los módulos; los primeros para mantener una jerarquía o un orden y, los segundos para contener código de programación específico.
El ejemplo anterior nos ha servido para
introducirnos en la estructura de un proyecto en Python. Vamos ahora con
algo de "teoría" que nos ayude a comprender mejor el potencial de estos
conceptos.
Imagina que en mi programa principal tengo el siguiente código
. . . import moduloA # importar un módulo que no pertenece a un paquete import paquete.moduloB # importar un módulo que está dentro de un paquete import paquete.subpaquete.moduloC . . .
Si simplemente escribo import moduloA, el módulo se deberá encontrar en la misma carpeta que nuestro programa principal y no pertenece a un paquete concreto. Si escribo import paquete.moduloB , importaré el moduloB que está dentro de la carpeta paquete y si escribo import paquete.subpaquete.moduloC , estoy accediendo al moduloC que se encuentra dentro del subpaquete que a su vez está dentro del paquete.
Lo importante es, que, encontrándome donde me encuentro, cada vez que accedo dentro de un paquete o carpeta, tengo que ir poniendo la ruta al módulo separando los paquetes por puntos. En el ejemplo anterior, desde el módulo misclases sólo necesitaba indicar funciones.misfunciones para acceder al módulo misfunciones. Es ir buscando la ruta...
Dentro del programa principal... ¿cómo llamo a una función determinada de un módulo importado? Pues por ejemplo, si es la función contar del moduloB
cuenta = paquete.moduloB.contar()
La parte de la ruta a la función paquete.moduloB se denomina namespace. Un namespace es el nombre que se ha indicado tras la palabra import.
Los namespaces pueden denominarse o acortarse por alias...
. . .
import paquete.moduloB as modB
. . .
cuenta = modB.contar()
Esto favorece las llamadas a las distintas funciones / clases / variables de los módulos. En el ejemplo, modB es un alias.
= = =
También es posible llamar solamente una función o una clase por separado, con el método from. Del ejemplo anterior de fracciones, en el programa principal podría haber escrito
# antes estaba: import clases.misclases as miclase from clases.misclases import fraccion
Lo cual hubiera simplemente importado la clase "fraccion". En la línea 16 del código, ya sólo hubiese tenido que escribir a = fraccion(numerador, denominador) sin la referencia del alias anterior.
También podría haber usado un alias en este caso...
from clases.misclases import fraccion as fra
Lo cual es conveniente si desde varios módulos llamamos elementos que podrían tener el mismo nombre. Por ejemplo...
from clases.misclases import fraccion as fra, sumar as sum1 from operaciones.misoperaciones import logaritmo as log, sumar as sum2
Muy poco recomendado, pero posible, es llamar a todos los elementos de un módulo con la expresión "*"
from paquete.modulo1 import *
Recuperándose cada uno con su nombre original, ya que no hay posibilidad de alias.
No hay comentarios:
Publicar un comentario