Programar Juegos Arcade
con Python y PygameChapter 14: Bibliotecas y Módulos
Una biblioteca es un conjunto de definiciones de variables, funciones y clases. Habitualmente, estás librerías han sido desarrolladas por otra persona que las ha incluido en un proyecto, de forma que cualquier otro programador no tenga que “reinventar la rueda.” En Python usamos el término módulo para describir el código de una biblioteca determinada.
Al escribir import pygame e import random al principio de los programas que hemos usado hasta ahora, lo que hemos hecho ha sido llamar a esos módulos. Una biblioteca puede estar compuesta de múltiples módulos que pueden importarse. Frecuentemente, una biblioteca tiene un solo módulo, por lo que ambos términos se pueden usar indistintamente.
Generalmente, los módulos se organizan en grupos con funcionalidades similares. Los programas que hemos visto en clase han hecho uso de funciones que provenían de los módulos math y random, así como de la biblioteca pygame. Los módulos se pueden organizar de forma que módulos individuales contengan otros módulos. Por ejemplo, el módulo pygame contiene los submódulos pygame.draw, pygame.image, y pygame.mouse.
Los módulos no se cargan en el programa hasta que éste los necesita. Esto ahorra tiempo y memoria. En este capítulo veremos cómo crear un módulo, importarlo y usarlo.
14.1 ¿Por Qué Crearnos Una Biblioteca?
Existen tres razones principales para que un(a) programador(a) cree sus propias bibliotecas:
- Trocear el código en unidades más pequeñas y fáciles de usar.
- Permite que varias personas trabajen al mismo tiempo en un programa.
- El código escrito puede compartirse fácilmente con otros programadores.
Algunos de los programas que hemos creado en este libro han empezado a extenderse demasiado. Si troceáramos un programa grande, en programas más pequeños, sería más fácil gestionar ese código. Por ejemplo, en el capítulo anterior de los sprites, un programador podría mover la clase sprite a un archivo separado. En un programa más complejo, cada sprite debería estar contenido en su propio archivo individual.
Si varios programadores trabajaran en un mismo proyecto, sería prácticamente imposible hacerlo si todo el código estuviera contenido en un único archivo. Si embargo, todo sería más fácil si troceamos el programa en múltiples partes. Podríamos tener a un programador desarrollando una clase sprite “Orco” y a otro trabajando con el sprite “Goblin”. Como los sprites se encuentran en archivos separados, los programadores no entrarían nunca en conflicto.
En la actualidad, los programadores raramente construyen programas desde cero. Generalmente, los programas son construidos con partes de otros programas con los que comparten la misma funcionalidad. Si por ejemplo, un programador crea un programa para gestionar un préstamo hipotecario, ese código sería un candidato ideal para ir dentro de una biblioteca. Entonces, cualquier otro programa bancario que necesite usar esa aplicación de cálculo de hipotecas, lo único que tendría que hacer es llamar a la biblioteca correspondiente.
14.2 Creamos Nuestro Archivo Módulo/Biblioteca:
En este ejemplo trocearemos, en varios archivos, un pequeño programa. Tenemos una función y su correspondiente llamada, dentro de un archivo llamado test.py:
# Función Foo def foo(): print("foo!") # Llamada a la función Foo foo()
Lo sé, este programa no es tan largo como para tener que trocearlo. Pero si tanto la función, como el programa fueran extensos, la cuestión sería diferente. Si tuviéramos varias funciones, cada una con 100 líneas de código, nos llevaría mucho tiempo gestionar ese archivo. Pero, de momento trabajaremos con este pequeño archivo por cuestiones de claridad.
Podemos sacar del archivo a la función foo. De esta forma, en el archivo solo nos quedaríamos con el código principal del programa. (Repito, en este caso no sería necesario todo esto, solo lo hacemos por cuestiones didácticas.)
Creamos ahora un archivo nuevo, y copiamos la función foo en él. Guardamos este archivo con el nombre mis_funciones.py. Debemos guardarlo en la misma carpeta que test.py.
# Función Foo def foo(): print("foo!")
# Llamada a la función Foo que no se ejecuta foo()
Lamentablemente no es tan sencillo como esto. El archivo test.py desconoce dónde tiene que buscar al archivo mis_funciones.py para importarlo. Tenemos que añadir el comando que lo importe:
# Importa el archivo mis_funciones.py import mis_funciones # Llamada a la función Foo que aún no se ejecuta foo()
Esto sigue sin funcionar. ¿Qué es lo que hemos olvidado? Pues tal como hicimos cuando importábamos pygame, tenemos que poner el nombre del paquete por delante de la función. Tal como esto:
# Importa el archivo mis_funciones.py import mis_funciones # Llamada a la función Foo que ya se ejecuta mis_funciones.foo()
Esto funciona debido a que hemos antepuesto mis_funciones a la llamada de la función.
14.3 Namespace
Es muy probable que un programa deba utilizar dos bibliotecas. ¿Pero qué sucede si ambas bibliotecas tienen funciones con nombres idénticos? Digamos que hay dos funciones llamadas imprimir_informe, una de ellas imprime notas y la otra imprime un estado de cuentas. Por ejemplo
def imprimir_informe(): print ("Informe de Notas del Estudiante:" )
def imprimir_informe(): print ("Informe Financiero: " )
¿Cómo especificamos a cuál de las funciones debe llamar el programa? Pues bien, es bastante fácil. Especificas el namespace. En el siguiente código, el namespace es el texto que antecede al nombre de la función:
import funciones_alumnos import funciones_financieras funciones_alumnos.imprimir_informe() funciones_financieras.imprimir_informe()
Podemos ver ahora el por qué esto era necesario. ¿Pero qué pasa si no hay conflicto con los nombres? Escribir el namespace una y otra vez podría resultar molesto. Esto lo podemos evitar importando la biblioteca como un namespace local. El namespace local es una lista de funciones, variables y clases a las que no necesitas anteponerles un namespace. Volviendo al ejemplo anterior foo, vamos a eliminar el import original y sustituirlo con otro tipo de import:
# import foo from mis_funciones import * foo()
Esto funciona sin tener que anteponer mis_funciones. a la llamada de la función. El asterisco es un metacaracter que importará todas las funciones de mis_funciones. El programador podría importar individualmente unas determinadas funciones, especificando el nombre que le interesa.
14.4 Bibliotecas de Terceros
Cuando trabajamos en Python, tenemos muchas bibliotecas disponibles que pertenecen
a la propia estructura de Python. Mira en el siguiente enlace todas
las bibliotecas que están disponibles:
http://docs.python.org/3/py-modindex.html
También es posible descargar e instalar otras bibliotecas. Existen bibliotecas para trabajar con la web, números complejos, bases de datos, y muchas más.
- Pygame: La biblioteca que usamos para construir juegos.
http://www.pygame.org/docs/ - wxPython: Crea programas GUI; con ventanas, menús, etc.
http://www.wxpython.org/ - pydot: Genera gráficos complejos; dirigidos y no dirigidos
http://code.google.com/p/pydot/ - NumPy: Una sofisticada biblioteca para trabajar con matrices.
http://numpy.scipy.org/
Una lista de estupendas bibliotecas para Python, con sus enlaces correspondientes, se encuentra disponible aquí:
http://www.lfd.uci.edu/~gohlke/pythonlibs/
Hacer un recorrido por la lista de bibliotecas disponibles, te puede ayudar a expandir tus horizontes sobre qué tipo de programas puedes llegar a crear. La mayor parte de programas consisten en el ensamblado de distintas piezas ya construidas, en lugar de tener que empezar desde cero.
14.4.1 Ejemplos: Biblioteca OpenPyXL
En este ejemplo usaremos la biblioteca OpenPyXL para escribir en un archivo Excel. También es muy fácil leer un archivo Excel.
Desde un terminal Windows (símbolo del sistema) podemos instalar OpenPyXL escribiendo pip install openpyxl. Si lo haces desde una máquina Mac o Linux, escribirías desde un terminal pip3 install openpyxl.
""" Uso de OpenPyXL para crear una hoja de cálculo Excel """ from openpyxl import Workbook import random # Creamos una hoja Excel wb = Workbook() # Tomamos la hoja activa ws = wb.active # Podemos introducir datos directamente en las celdas ws['A1'] = "Esto es una prueba" # Podemos añadir también filas for i in range(200): ws.append([random.randrange(1000)]) # Guardamos el archivo wb.save("ejemplo.xlsx")
14.4.2 Ejemplos: Biblioteca Beautiful Soup
En este ejemplo extraeremos información de una página web.
Desde un terminal Windows (símbolo del sistema) podemos instalar OpenPyXL escribiendo pip install bs4. Si lo haces desde una máquina Mac o Linux, escribirías desde un terminal pip3 install bs4.
""" Mostramos cómo leer desde una página web """ from bs4 import BeautifulSoup import urllib.request # Leemos el archivo url= "http://www.espnfc.com/spi/rankings/_/view/fifa/teamId/203/mexico?cc=5901" pagina = urllib.request.urlopen(url) sopa = BeautifulSoup(page.read(), "html.parser") # Encontramos la tabla con los datos rango = sopa.find("table").tbody # Obtenemos una lista con las filas de la tabla filas = rank.findAll("tr") # Iteramos a través de cada fila for fila in filas: # Obtenmos una lista con las celdas de la fila celdas = fila.findAll("td") # Iteramos a través de cada celda for celda in celdas: print(celda.text, end=", ") # Listo, hemos terminado con esa fila. Imprimos una línea en blanco y vamos a la # siguiente. print()
14.4.3 Ejemplo: Biblioteca Matplotlib
Aquí tienes un ejemplo de lo que puedes hacer con la biblioteca de terceros “Matplotlib.” Puedes saltarte esta sección si solo te interesan los juegos, pero mi esperanza es que viendo esto te puedas hacer una idea de lo que es posible hacer utilizando bibliotecas.
Desde un terminal Windows (símbolo del sistema) podemos instalar OpenPyXL escribiendo pip install matplotlib. Si lo haces desde una máquina Mac o Linux, escribirías desde un terminal pip3 install matplotlib.
Para empezar, aquí tienes el código para crear la gráfica de unas rectas para cuatro valores:
""" Recta con cuatro valores. Por defecto, el eje x empieza en cero. """ import matplotlib.pyplot as plt y = [1, 3, 8, 4] plt.plot(y) plt.ylabel('Valor del Elemento') plt.xlabel('Número del Elemento') plt.show()
Observa que puedes hacer zoom, desplazar y guardar el gráfico. Incluso, puedes guardar la gráfica en un formato vectorial como el ps y svg, los cuales se importan en los documentos sin pérdida de calidad, tal como le pasaría a un gráfico raster (mapa de bits).
El valor de x para el Ejemplo 1, Figura 14.1, empieza por defecto en cero. Esto lo puedes cambiar y especificar tus propios valores de x para que vayan con los valores para y. Mira debajo el Ejemplo 2.
""" Recta con cuatro valores. Ahora especificamos unos valores para el eje x también. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.plot(x, y) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Es bastante fácil añadir otra serie de datos a la gráfica.
""" En este ejemplo se muestra como dibujar dos series diferentes de datos sobre la misma gráfica. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] plt.plot(x, y1) plt.plot(x, y2) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
También puedes añadirle una leyenda a la gráfica:
import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] plt.plot(x, y1, label = "Serie 1") plt.plot(x, y2, label = "Serie 2") leyenda = plt.legend(loc = 'upper center', shadow = True, fontsize = 'x-large') leyenda.get_frame().set_facecolor('#00FFCC') plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Puedes añadir también anotaciones:
""" Anotando sobre la gráfica """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.annotate('Aquí', xy = (2, 3), xycoords = 'data', xytext = (-40, 20), textcoords = 'offset points', arrowprops = dict(arrowstyle = "->", connectionstyle = "arc, angleA = 0,armA = 30,rad = 10"), ) plt.plot(x, y) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
¿No te gusta el estilo de líneas por defecto de tu gráfica? Lo puedes cambiar añadiendo un tercer parámetro al comando plot.
""" Aquí mostramos cómo establecer el estilo de línea y los símbolos. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y1 = [1, 3, 8, 4] y2 = [2, 2, 3, 3] # Primer carácter: Estilo de línea # Elegir entre '-', '--', '-.', ':', 'None', ' ', ” # Segundo carácter: color # http://matplotlib.org/1.4.2/api/colors_api.html # Tercer carácter: forma del símbolo # http://matplotlib.org/1.4.2/api/markers_api.html plt.plot(x, y1, '-ro') plt.plot(x, y2, '--g^') plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Pasar a una gráfica de barras es tan fácil como cambiar plot por bar.
""" Cómo construir una gráfica de barras. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.bar(x, y) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Puedes añadirle etiquetas a los valores de los ejes.
""" Cómo añadir etiquetas a los valores del eje x. """ import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 3, 8, 4] plt.plot(x, y) labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs'] plt.xticks(x, labels) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
También puedes dibujar funciones. Para dibujar la función seno, necesitamos una biblioteca distinta llamada numpy.
""" Uso de la biblioteca numpy para dibujar una función para un determinado rango de valores. """ import numpy import matplotlib.pyplot as plt x = numpy.arange(0.0, 2.0, 0.001) y = numpy.sin(2 * numpy.pi * x) plt.plot(x, y) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Si quieres, puedes también rellenar la gráfica.
""" Uso de 'fill' para rellenar una gráfica """ import numpy import matplotlib.pyplot as plt x = numpy.arange(0.0, 2.0, 0.001) y = numpy.sin(2 * numpy.pi * x) plt.plot(x, y) # 'b' significa azul. 'alpha' es la transparencia. plt.fill(x, y, 'b', alpha = 0.3) plt.ylabel('Valor del Elemento') plt.xlabel('Elemento') plt.show()
Crear una gráfica de tarta.
""" Crear una gráfica de tarta """ import matplotlib.pyplot as plt # Etiquetas para la gráfica de tarta etiquetas = ['C', 'Java', 'Objective-C', 'C++', 'C#', 'PHP', 'Python'] # Tamaños para cada etiqueta. Lo usamos para crear un porcentaje dimensiones = [17, 14, 9, 6, 5, 3, 2.5] # Para una lista de colores, ver: # https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/colors.py colores = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral', 'darkcyan', 'darksage', 'rosybrown'] # ¿Cuán separada ha de estar una porción? Normalmente cero. separar = (0, 0.0, 0, 0, 0, 0, 0.2) # Definimos la ratio de aspecto al valor 'equal' para que la tarta dibujada sea circular. plt.axis('equal') # Finalmente, dibujamos la tarta plt.pie(dimensiones, explode = separar, labels = etiquetas, colors = colores, autopct = '%1.1f%%', shadow = True, startangle = 90) plt.show()
Podemos hacer cosas realmente interesantes, como por ejemplo traer datos de cotizaciones desde la web y crear una gráfica de caja y bigotes para las acciones de Apple Computer:
""" Crear una gráfica de caja y bigotes para la cotización de un valor en bolsa """ import matplotlib.pyplot as plt from matplotlib.dates import DateFormatter, WeekdayLocator,\ DayLocator, MONDAY from matplotlib.finance import quotes_historical_yahoo_ohlc, candlestick_ohlc # Extraemos las cotizaciones para las fechas siguientes fecha1 = (2014, 10, 13) fecha2 = (2014, 11, 13) # Vamos a la web y nos traemos la información de la cotización valores = quotes_historical_yahoo_ohlc('AAPL', fecha1, fecha2) if len(valores) == 0: raise SystemExit # Definimos el aspecto de la gráfica fig, ax = plt.subplots() fig.subplots_adjust(bottom=0.2) # Ponemos marcas mayores para los lunes lunes = WeekdayLocator(MONDAY) ax.xaxis.set_major_locator(lunes) # Ponemos marcas menores para el resto de días resto_dias = DayLocator() ax.xaxis.set_minor_locator(resto_dias) # Damos formato a los días formato_semana = DateFormatter('%b %d') # e.g., Ene 12 ax.xaxis.set_major_formatter(formato_semana) ax.xaxis_date() candlestick_ohlc(ax, valores, width=0.6) ax.autoscale_view() plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right') plt.show()
Hay muchas más cosas que podemos hacer con matplotlib. Mira esta galería de imágenes:
http://matplotlib.org/gallery.html
14.5 Repaso
14.5.1 Test
14.5.2 Ejercicios
Haz click para ir a los Ejercicios.
14.5.3 Taller
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin