Sie sind auf Seite 1von 501

Programar Juegos Arcade

con Python y Pygame


Chapter 1: Crear una Calculadora
Personal
(¡Hola! Si aún no tienes una máquina con Python y
Pygame instalado, entonces regresa a la sección
“anterior” para descargar e instalarlos para que puedas
empezar.)

1.1 Introducción

Una de las cosas más simples que se pueden hacer con


Python es usarlo como una sofisticada calculadora. Pero
espera, una calculadora no es un juego. ¿Por qué
estamos hablando de calculadoras? Me aburro....
Ten paciencia, para calcular objetos cayendo y balas
volando, así como las puntuaciones, necesitamos
cálculos. Además, ¡cualquier geek auténtico considera
una calculadora más como un juguete que como un
dispositivo de tortura! Vamos a empezar nuestro
aprendizaje de juegos con calculadoras. No te
preocupes, comenzaremos con los gráficos en el capítulo
5.

Un sencillo programa calculadora puede ser usado para


pedir información al usuario, y luego calcular cosas
aburridas como pagos de hipotecas, o cosas más
excitantes como la trayectoria de bolas de barro al ser
lanzadas al aire.

Como primer ejemplo calcularemos la energía cinética,


algo que necesitaremos hacer como parte de un motor
de juego de física.
Figure 1.1: Usando Python Para Calcular La Energía Cinética

Lo mejor de hacer esto como un programa, es la


habilidad de ocultar las complejidades de una ecuación.
Todo lo que el usuario necesita hacer es dar la
información y él o ella, podrá obtener el resultado en un
formato fácil de entender. Cualquier aplicación del tipo
calculadora podría funcionar en un smartphone,
permitiendo a cualquier persona, llevar a cabo
fácilmente cálculos en cualquier lugar.

1.2 Imprimir
1.2.1 Imprimir Texto

¿Cómo imprime un programa algo en pantalla?


print("Hola Mundo.")
Este programa imprime “Hola Mundo” en pantalla.
Continúa e introdúcelo en el prompt del IDLE, y mira
como funciona. Intenta imprimir otras palabras y frases.
Por lo general, el ordenador imprimirá cualquier cosa
que quieras, sea verdad o no.

¿Cómo sería el programa “Hola Mundo” en otros


lenguajes de programación? Compruébalo en la
Wikipedia. En ella se muestran una buena cantidad de
programas de “Hola Mundo” escritos en varios
lenguajes de programación distintos:
http://es.wikipedia.org/wiki/Hola_mundo

Es interesante ver cuántos lenguajes de programación


distintos existen. Puedes hacerte una idea de lo
complejo que es un lenguaje viendo la forma de
programar “Hola Mundo”.
Vídeo: La función print
Recuerda, el comando para imprimir en Python es fácil.
Simplemente escribe print. Después del
comando print hay un par de paréntesis ( ). Dentro de
estos paréntesis está lo que debería ser imprimido por
pantalla. El usar paréntesis para pasar información a
una función es una práctica habitual en matemáticas y
lenguajes de programación.

Los estudiantes de matemáticas aprenden a usar


paréntesis evaluando expresiones como . y son
funciones. Los datos pasados a estas funciones están
entre los paréntesis. Nuestro caso difiere en que la
información pasada es texto.

Fíjate que hay comillas dobles rodeando el texto a ser


imprimido. Si una declaración de print tiene comillas
rodeando el texto, el ordenador lo imprimirá tal cual
está escrito. Por ejemplo, este programa imprimirá 2+3:
print("2 + 3")

1.2.2 Imprimir Resultados de Operaciones


El siguiente programa no tiene comillas rodeando , y el
ordenador lo evaluará como una expresión matemática.
Imprimirá 5 en vez de 2+3.
print(2 + 3)
El código siguiente provocará un error porque el
ordenador intentará evaluar “Hola Mundo” como una
expresión matemática y eso no funcionará:
print(Hola Mundo)

El código anterior imprimirá un error del tipo SyntaxError:


invalid syntax, que es la manera en que expresa el ordenador

su desconocimiento de “Hola” y “Mundo”.

Ten en cuenta, también, que esto es una comilla


simple: ' y esto una doble: " Si pido una comilla doble es
un error común escribir "" que realmente es una doble
doble comilla.

1.2.3 Imprimir Múltiples Elementos

Una declaración print puede sacar múltiples cosas a la


vez, cada elemento separado por una coma. Por
ejemplo, este código imprimirá Tu nueva puntuación es 1040
print("Tu nueva puntuación es ", 1030 + 10)

La próxima línea de código imprimirá Tu nueva puntuación es


1030+10. Los números no se suman porque están entre

comillas. Cualquier cosa entre comillas es tratada por el


ordenador como texto. El ordenador piensa que
cualquier cosa fuera de ellas es una declaración
matemática o código.
print("Tu nueva puntuación es ", "1030 + 10")

La coma, ¿va dentro o fuera de las comillas?

El siguiente código de ejemplo no funciona de ninguna


manera. Esto es porque no hay una coma separando el
texto que está entre comillas y el 1030+10. A primera
vista puede parecer que hay una coma, pero la coma
está dentro de las comillas. La coma que separa los
elementos a imprimir debe estar fuera de las comillas. Si
el programador quiere imprimir una coma, ésta debe
estar entre las comillas:
print("Tu nueva puntuación es," 1030 + 10)

El próximo ejemplo sí funciona, porque hay una coma


separando los elementos. Imprime:
Tu nueva puntuación es, 1040

Fíjate que solo imprime una coma. Las comas fuera de


las comillas separan elementos, las comas dentro de las
comillas son imprimidas. La primera coma es
imprimida, la segunda es usada para separar los
elementos.
print("Tu nueva puntuación es,", 1030 + 10)
1.3 Códigos de Escape
Si se usan comillas para indicarle al ordenador el
comienzo y el final de la cadena de texto que deseas
imprimir, ¿cómo conseguimos que un programa
imprima un conjunto de comillas dobles? Por ejemplo:

print("Quiero imprimir comillas dobles " por alguna razón.")

Este código no funcionará. El ordenador se fija en las


comillas del centro de la cadena y piensa que es el final
del texto. Por ello no sabe qué hacer con el comando por
alguna razón y las comillas del final de la cadena confunden

al ordenador todavía más.

Es necesario decirle al ordenador que queremos tratar


las comillas dobles del medio como texto, no como las
comillas finales de una cadena. Esto es fácil,
simplemente coloca una contrabarra (o barra inversa)
delante de las comillas para decirle al ordenador que
son parte de una cadena y no un caracter que finaliza
una cadena. Por ejemplo:
print("Quiero imprimir una comilla doble \" por alguna razón.")
Esta combinación de dos caracteres \" se conoce
como código de escape. Casi todos los lenguajes los tienen.
Debido a que la contrabarra se usa como código de
escape, la propia contrabarra debe ser precedida por un
código de escape. Por ejemplo, este código no
funcionará correctamente:
print("El archivo está guardado en C:\nueva carpeta")

¿Por qué? Porque \n es un código de escape. Para


imprimir la contrabarra es necesario usar el código de
escape de la siguiente manera:
print("El archivo está guardado en C:\\nueva carpeta")

Tenemos que aprender unos cuantos códigos de escape


importantes más. Esta es la tabla de los más
importantes:

Código de escape Descripción

\' Comilla simple

\" Comilla doble

\t Tabulador

\r CR: Retorno de Carro (mover a la izquierda)


Código de escape Descripción

\n LF: Salto de línea (mover abajo)

¿Qué es un “Retorno de Carro” y qué un “Salto de


línea”? Intenta este ejemplo:
print("Este\nes\nmi\nejemplo.")

La salida de este comando es:

Este
es
mi
ejemplo.

La \n es un salto de línea. Mueve el “cursor” una línea


más abajo y ahí será donde el ordenador imprimirá. El
ordenador almacena todo el texto en una gran línea. El
ordenador entiende cómo mostrar el texto en diferentes
líneas debido al uso de los caracteres \n.

Para complicar las cosas, cada sistema operativo tiene


diferentes estándares para acabar una línea.

Códigos de escape Descripción

\r\n CR+LF: Microsoft Windows


Códigos de escape Descripción

\n LF: sistemas basados en UNIX, y los Macs más modernos.

\r CR: sistemas basados en Mac antiguos

Normalmente tu editor de texto se encargará de esto por


ti. El Bloc de Notas de Microsoft no lo hace, y los
archivos de UNIX abiertos en el bloc de notas aparecen
horribles porque los finales de línea no se muestran o
aparecen como cajas negras.

1.4 Comentarios
Los comentarios son importantes (incluso si los ignora el
ordenador)

A veces, el código necesita algunas explicaciones


adicionales para la persona que lo lee. Por esto
añadimos “comentarios” al código. Los comentarios son
para que los lean los humanos y no el ordenador.

Hay dos maneras de poner comentarios. La primera es


usar el simbolo #. El ordenador ignorará cualquier texto
en un programa de Python que esté detras de #. Por
ejemplo:
# Esto es un comentario que comienza con el signo #
# y el compilador lo ignorará.

print("Esto no es un comentario, el ordenador")

print("ejecutará esto y lo imprimirá.")

El signo # entre comillas no es tratado como un


comentario. Un programador puede deshabilitar una
línea de código poniendo # delante de ella. También es
posible poner un comentario al final de una línea.
print("Un signo # entre comillas no es un comentario.")

# print("Esto es un comentario, incluso si es código.")

print("Hola") # Esto es un comentario al final de una línea

Es posible comentar múltiples líneas de código usando


tres comillas simples en una fila para delimitar los
comentarios.
print("Hola")

'''

Esto es

un

comentario

de

múltiples

líneas. Nada

de lo que esté

entre estas comillas

se ejecutará.

print("Allí")

'''

print("Hecho")
La mayoría de los programadores profesionales de
Python solo usan este tipo de comentarios multilínea en
los llamados docstrings. Los docstrings permiten escribir
la documentación a lo largo del código y luego extraerla
automáticamente como documentación impresa,
páginas web, y en Entornos de Desarrollo Integrados
(IDEs). Para los comentarios genéricos es mejor usar el
símbolo #.

Incluso si tú eres el único que va a leer el código que


escribes, los comentarios pueden ayudarte a ahorrar
tiempo. Añadiendo un comentario como “Manejar
bombas alien” te permitirá recordar rápidamente qué
hace esa sección de código sin tener que leerla y
descifrarla.

1.5 Operadores de Asignación


Vídeo: El Operador de Asignación

¿Cómo almacenamos la puntuación en nuestro juego?


¿Cómo controlamos la resistencia del enemigo? Lo que
tenemos que hacer es usar un operador de asignación.
(Un operador es un símbolo como + o -.) Esto almacena
un valor en una variable para ser usado luego. El código
de abajo asignará 10 a la variable x, y luego imprimirá el
valor almacenado en x.
Observa el siguiente ejemplo. Haz click en el botón
“Inicio” para ver cómo funciona el código.
Assigning and using variables

1
2
3
# Crea la variable x
4 # Almacena el valor de 10 en ella. Variables
x = 10 :
5
# Imprime el valor almacenado en x. x=
6 Salida:
print(x)
7 # Imprime la letra x, pero no el valor contenido en x
10
8 print("x")
x
# Imprime "x= 10"
9 x= 10
print("x=", x)
10
11
12
Inicio

Las variables van fuera de las comillas.

Observación: El listado de arriba también demuestra la


diferencia entre imprimir una x dentro de comillas y
una x fuera de ellas. Si una xestá entre comillas, entonces
el ordenador imprime x. Si una x está fuera de las
comillas, entonces el ordenador imprimirá el valor de x.
La confusión entre “dentro o fuera de las comillas” es
muy común para aquellos que empiezan a programar.
Una sentencia de asignación (una línea de código
usando el operador =) es diferente a la igualdad
algebráica que aprendiste en matemáticas. No te
confundas, no pienses que son la misma cosa. En la
parte de la izquierda de un operador de asignación sólo
debe haber una variable. ¡Y nada más!

A la derecha del símbolo igual/operador de asignación


hay una expresión. Una expresión es algo que evalúa un
valor. Examina el siguiente código:
x =x +1

Obviamente, el código anterior no puede ser una


igualdad algebraica. Pero para el ordenador es válido ya
que es una sentencia de asignación. Las ecuaciones
matemáticas son diferentes a las sentencias de
asignación, incluso si tienen variables, números y un
signo de igual.

El código anterior toma el valor actual de x, le añade una


unidad y almacena el nuevo resultado en x.
Ampliando nuestro ejemplo, la sentencia siguiente
imprimirá el número 6.
x =5

x =x +1

print(x)

Las sentencias son ejecutadas secuencialmente. El


ordenador no “ve el futuro.” En el código siguiente, el
ordenador imprimirá un 5 en la línea 2 y un 6 en la línea
4. Esto es porque en la línea 2, el código para añadir una
unidad a x aún no se ha ejecutado.

1 x =5
2 print(x) # Imprime 5

3 x =x +1

print(x) # Imprime 6
4

La siguiente sentencia es válida y se ejecutará, pero no


tiene sentido. El ordenador añadirá una unidad a x, pero
el resultado nunca se almacenará o imprimirá.
x +1

El código de abajo imprimirá 5 en vez de 6 porque el


programador olvidó almacenar el resultado de x + 1 en la
variable x.
x =5

x +1

print(x)

La siguiente sentencia no es válida porque a la izquierda


del signo igual hay más de una variable:
x +1=x

Python tiene otros tipos de operadores de asignación,


permiten al programador modificar fácilmente una
variable. Por ejemplo:
x += 1

La sentencia anterior es equivalente a escribir el


siguiente código:
x =x +1

También hay operadores de asignación para restar,


multiplicar y dividir.

1.6 Variables
Las variables empiezan con una letra minúscula.

Las variables deberían empezar siempre con una letra


minúscula. Las variables pueden empezar con una letra
mayúscula o un guión bajo, pero esos son casos
especiales y no deberían usarse de forma habitual.
Después de la primera letra minúscula, la variable
puede incluir mayúsculas y minúsculas. Las variables
no pueden contener espacios.

Las variables son sensibles a las minúsculas y


mayúsculas. Esto puede ser confuso si el programador
no lo espera. En el siguiente código, la salida será 6 en
lugar de 5 porque hay dos variables distintas, x y X.
x =6

X =5

print(x)

La guía de estilo de Python (sí, los programadores


escribieron un libro de estilo) dice que las variables con
nombres formados por varias palabras en Python
deberían estar separadas por guiones bajos. Por
ejemplo, usaríamos estilo_peinado en lugar de estiloPeinado.
Personalmente, si eres uno de mis alumnos, no me
importa mucho esta regla porque el siguiente lenguaje
que damos, Java, tiene exactamente la regla de estilo
opuesta. Yo solía enseñar reglas de estilo de Java en esta
clase pero comencé a recibir cartas de odio de los
amantes de Python. Estas personas visitaron mi página
web y quedaron conmocionados, tal como te lo
digo, conmocionados, con mi pobre estilo.
Renuncié y ahora intento usar guías de estilo
apropiadas.

Aquí hay algunos ejemplos de nombres de variables que


son correctos y otros que no:

Nombres de variables legales Nombres de variables ilegales Legales pero

primer_nombre primer nombre PrimerNombre

distancia 9ds primerNombre

ds9 %correcto X

Todos los nombres de variables en mayúsculas


como MAX_VELOCIDAD están permitidas solo en circunstancias
donde el valor de la variable nunca cambia. Una variable
que no cambia se llama constante.

1.7 Operadores
Para operaciones matemáticas más complejas contamos
con los operadores matemáticos comunes, junto con
otros no tan comunes:

operador operación ejemplo de ecuación ejem

+ suma a=3
operador operación ejemplo de ecuación ejem

- resta a=3

* multiplicación a=3

/ división a = 10

// división parte entera (cociente) N/A a = 10

** potencia a=2

% módulo (resto) N/A a=8

Vídeo: Operadores

“División parte entera” siempre redondeará el resultado


hacia el entero menor más cercano. Por
ejemplo, 11//2 será 5, no 5.5, y 99//100 será igual a 0.

La multiplicación por yuxtaposición no funciona en


Python. Las siguientes dos líneas de código no
funcionarán:
# Esto no funcionará

x = 5y

x = 5(3/2)

Es necesario usar el operador de multiplicación para


conseguir que funcionen estas líneas de código:
# Esto funciona

x =5*y

x = 5 * (3 / 2)

1.7.1 Espacios entre Operadores

Antes y después de un operador puede haber cualquier


cantidad de espacios y el ordenador lo entenderá. Por
ejemplo, cualquiera de estas líneas son equivalentes:

1 x=5*(3/2)

2 x =5*( 3/2)

x =5 *( 3/ 2)
3

La guía oficial de estilo de Python dice que debería


haber un espacio antes y después de cada operador. (A
que te estás muriendo por saberlo, ¿verdad?. De
acuerdo, la guía oficial de estilo de código Python está
aquí: PEP-8.) De las tres líneas de código anteriores, la
más “estilosa” sería la línea 2.

1.8 Orden de Operaciones


Python evaluará las expresiones usando el mismo orden
de operaciones que se esperan en las expresiones
matemáticas estándar. Por ejemplo, esta ecuación no
calcula correctamente la media:
media = 90 + 86 + 71 + 100 + 98 / 5

La primera operación que se hace es 98/5. El ordenador calcula:

en lugar del que necesitamos:

Este problema se resuelve usando paréntesis:


media = (90 + 86 + 71 + 100 + 98) / 5

1.9 Funciones Trigonométricas


Las funciones trigonométricas se usan para calcular el
seno y coseno en ecuaciones. Por defecto, Python no
sabe calcular el seno y coseno, pero lo hace una vez que
se ha importado la biblioteca correspondiente. Las
unidades están en radianes.
# Importar la biblioteca math

# Esta linea se usa una sola vez y al principio del

# del programa.

from math import *

# Calcular x usando seno y coseno

x = sin(0) + cos(0)
1.10 Cálculo de Ecuaciones
Personales
Un programa puede usar Python para calcular el
consumo por unidad distancia de un coche que recorrió
294 millas con 10.5 galones de gasolina.
m = 294 / 10.5

print(m)

Podemos mejorar este programa usando variables. Esto


nos permitirá cambiar fácilmente los valores en el
código sin modificar la ecuación.
m = 294

g = 10.5

m2 = m/ g # Aquí empleamos variables

print(m2)

Es importante nombrar bien las variables

Por sí solo, este programa es difícil de entender. Las


variables m y g no significan nada sin algún contexto. El
programa puede ser más fácil de entender usando
variables nombradas apropiadamente:
millas_recorridas = 294

galones_usados = 10.5

mpg = millas_recorridas / galones_usados

print(mpg)
Con esto, incluso una persona que no sea
programadora, puede ver el programa y hacerse una
idea de qué es lo que hace. Otro ejemplo de un uso
correcto de los nombres para una variable, frente a uno
malo, sería:
# Difícil de entender

ir = 0.12

b = 12123.34

i = ir * b

# Fácil de entender

tasa_interes = 0.12

balance_cuenta = 12123.34

capital_final = tasa_interes * balance_cuenta

Vídeo: Creando un programa de cálculo personalizado

En el editor IDLE es posible editar una línea anterior sin


reescribirla. Sitúa el cursor en la línea a editar y pulsa la
tecla “enter”. La línea en cuestión será copiada a la línea
actual.

Introducir código en el prompt >>> es lento y solamente


podemos hacer una línea cada vez. Tampoco es posible
guardar el código para que otra persona pueda
ejecutarlo. Por suerte hay otra manera mejor de
introducir código en Python.

El código Python se puede introducir usando un script.


Un script es una serie de líneas de código de Python que
se ejecutarán todas a la vez. Para crear un script, abre
una nueva ventana como se muestra en la Figura 1.2.

Figure 1.2: Introduciendo un script

Introduce el programa de Python para calcular el


consumo de gasolina por millas y guarda el archivo.
Guarda el archivo en una unidad flash, unidad de red,
o en cualquier otro dispositivo de tu elección. Los
programas en Python deberían acabar siempre con la
extensión .py. Ver Figura 1.3.
Figure 1.3: Guardando un script

Ejecuta el programa escrito haciendo click sobre el menu


“Run” y seleccionando “Run Module”. Prueba a
actualizar el programa con distintos valores para las
millas recorridas y los galones consumidos.

Cuidado, ¡Error habitual!

De ahora en adelante, casi todo el código debería ser


introducido en un script/módulo. No escribas tu
programa en el prompt >>> del IDLE. El código escrito
aquí no se guarda. Si haces eso, será necesario comenzar
de nuevo. Es un error muy habitual en los
programadores novatos.

Este programa sería más útil si interactuase con el


usuario y le preguntase por las millas recorridas y los
galones consumidos. Esto puede hacerse con la
sentencia input. Mira el siguiente código:
# Este código casi funciona

millas_recorridas = input("Introduce las millas recorridas:")

galones_usados = input("Introduce los galones usados:")

mpg = millas_recorridas / galones_usados

print("Millas por galón:", mpg)

Al ejecutar este programa, se le preguntará al usuario


por las millas y los galones, pero generará un extraño
error tal y como se muestra en la Figura 1.4.

Figure
1.4: Error ejecutando programa KPL

El motivo de este error puede verse cambiando un poco


el programa:
millas_recorridas = input("Introduce las millas recorridas:")

galones_usados = input("Introduce los galones usados:")

x = millas_recorridas + galones_usados

print("Suma de m + g:", x)

Al ejecutar el programa anterior se observa el resultado


que muestra la Figura 1.5.

Figure 1.5: Suma


incorrecta

El programa no suma los dos números, los pone uno a


continuación del otro. Esto es porque no sabe que el
usuario esta introduciendo números. El usuario podría
introducir “Bob” y “Mary”, sumar esas dos variables,
obteniendo “BobMary”, lo que tendría más sentido.

La entrada debe ser transformada a números.

Para decirle al ordenador que son números, es necesario


envolver la función de input con un int( ) o un float( ).
Usamos la primera opción para números enteros y la
segunda para números con decimales.

El programa definitivo será:


calculate_miles_per_gallon.py

1
2
3
4
Variable
5 # Sample Python/Pygame Programs
s:
# Simpson College Computer Science
6 millasRec
# http://programarcadegames.com/ orridas=
7 galonesUs
# http://simpson.edu/computer-science/
ados=
8 # Vídeo explicativo: http://youtu.be/JK5ht5_m6Mk mpg=
9 # Calcular las millas por galon Salida:
print("Este programa calcula mpg.")
10 Este prog
# Obtener del usuario las millas recorridas rama calc
11 ula las m
millas_recorridas = input("Introduce las millas recorridas: ")
illas por
12 # Convertimos el texto introducido a galón.

# número en coma flotante (número real) Introduci


13 r las mil
millas_recorridas = float(millas_recorridas) las recor
14 ridas: 28
#Obtener del usuario los galones consumidos
8
15 galones_usados = input("Introduce los galones usados: ")
Introduci
16 # Convertimos el texto introducido a r los gal
# número en coma flotante (número real)
ones cons
17 umidos: 1
galones_usados = float(galones_usados) 5
18
# Calculamos e imprimimos la respuesta Millas po
19 r galón:
mpg = millas_recorridas / galones_usados
19.2
20 print ("Millas por galon:",mpg)

21
22
23
24
25
Inicio

Otro ejemplo, calcular la energía cinética de un objeto:


calculate_kinetic_energy.py

1
2
3
# Sample Python/Pygame Programs
4
# Simpson College Computer Science
5 # http://programarcadegames.com/

6 # http://simpson.edu/computer-science/

# Calcula la Energía Cinética de un objeto


7
print("Este programa calcula la energía cinética para un objeto en movimiento.")
8
m_string = input("Introduce la masa del objeto en kilogramos: ")
9 m = float(m_string)

10 v_string = input("Introduce la velocidad en metros por segundo: ")

v = float(v_string)
11
e = 0.5 * m * v * v
12 print("El objeto tiene " + str(e) + " joules de energía.")
13
14
15

Para simplificar un programa es posible anidar la


sentencia input dentro de la sentencia float. Por ejemplo,
estas líneas de código:
millas_recorridas = input("Introduce las millas recorridas:")

millas_recorridas = float(millas_recorridas)

Hacen lo mismo que esta línea:


millas_recorridas = float(input("Introduce las millas recorridas:"))

En este caso, la salida de la función input se introduce


directamente en la función float. Ambas funcionan y es
cuestión del programador decidir cuáles de las dos
opciones elegir. Sin embargo, sí que es importante
entender ambas formas.

1.11 Repaso
1.11.1 Test

Haz click para ir al Test.

1.11.2 Ejercicios

Haz click para ir a los Ejercicios.

1.11.3 Taller

Haz click para ir al Taller.


Chapter 2: ¿Qué es un Lenguaje
de Programación?
Vídeo: ¿Qué es un lenguaje de programación?

¿Qué hace un lenguaje de programación? ¿Por qué lo


tienen los ordenadores? ¿Por qué hay tantos lenguajes
de programación distintos?

De la misma forma que para conducir un coche no hay


que entender cómo funciona un motor, tampoco es
necesario conocer la respuesta a estas preguntas para
hacer programación básica. Sin embargo, para
progresar a un nivel avanzado, sí que es necesario
entender cómo funciona un ordenador. Lo siguiente es
una breve introducción.

2.1 Pequeña Historia de la


Programación
Los ordenadores son electrónica y son digitales. Para un
ordenador todo se basa en tener, o no tener, voltaje a
través de un cable. No tener voltaje significa un cero, y
algo de voltaje significa un uno. Los ordenadores no
pueden contar más alto que eso sin tener que combinar
múltiples unos y ceros.
Al principio se usaban interruptores para simular unos
o ceros en la memoria del ordenador. La
Figura 2.1, cortesía de Wikimedia Commons, muestra
un Altair 8800. Los interruptores del panel frontal eran
usados para cargar el programa. Las luces mostraban la
salida. No existía un monitor.

Figure 2.1: Altair 8800

Cada conjunto de interruptores on/off representa un


número. Cada número podía representar datos o una
instrucción para que la ejecutase el ordenador. Este
sistema de usar unos y ceros para representar números
es llamado sistema numérico binario. Este tipo de
lenguaje de programación es llamado 1GL o lenguaje de
programación de primera generación. 1Gl es lo mismo
que decir lenguaje máquina donde los números
representan los comandos y los datos para el programa.
Los números binarios son representados normalmente
en grupos de cuatro. Por ejemplo:

1010 0010 0011

Tanto los datos, como las instrucciones del ordenador,


se guardan en binario. El lenguaje máquina son los
números binarios representando instrucciones que
interpreta el ordenador. Sin embargo, no todo el código
binario es lenguaje máquina. Los datos, como los
documentos, bases de datos o cifras financieras, son
también guardados en el ordenador en código binario.
Por supuesto, estos datos no son para ser ejecutados por
el ordenador.

Una mejora en la introducción de programas, a través


de interruptores, fue el código hexadecimal. Los
números decimales usados por la mayoría de la gente
son dígitos del 0 al 9. El sistema hexadecimal usa los
números del 0 al 9 y las letras de la A a la F para
representar conjuntos de cuatro interruptores, o los
números del 0 al 15. Observa en la siguiente tabla como
se relacionan los números binarios, decimales y
hexadecimales
BinarioDecimalHexadecimal

0 0 0

1 1 1

10 2 2

11 3 3

100 4 4

101 5 5

110 6 6

111 7 7

1000 8 8

1001 9 9

1010 10 A

1011 11 B

1100 12 C

1101 13 D

1110 14 E

1111 15 F

1 0000 16 10

1 0001 17 11
Vídeo: Sistemas decimal, binario, y hexadecimal

Para facilitar la creación de programas, los ordenadores


posteriores permitieron a los usuarios introducir
programas usando lenguaje ensamblador. Cada
comando usaba un mnemotécnico. Luego, un programa
llamado compilador lo sustituía por los números que
representaban esos comandos. Este tipo de lenguaje se
llama lenguaje 2GL o lenguaje de segunda generación.

La Figura 2.2 muestra parte de un programa en lenguaje


ensamblador (cortesía de la Wikimedia Commons.)
Figure 2.2: Ejemplo de lenguaje ensamblador

A pesar de que esto fue una mejora, todavía no era nada


fácil programar. La siguiente generación de lenguajes
permitiría abstracciones de alto nivel. Los primeros
lenguajes de la tercera generación
(COBOL,FORTRAN and LISP) eran un poco más fáciles
de entender y programar.

Los lenguajes de segunda y tercera generación usaban


un programa llamado compilador. Un compilador toma
el programa escrito por el usuario (llamado código
fuente) y lo convierte en código máquina. Entonces, el
programador ejecuta el código máquina. No se ejecuta
el código fuente original.

Si un programa está compuesto por varias piezas de


código fuente, éstas pueden unirse en un solo programa
utilizando un linker. Para generar el programa final, el
linker es ejecutado sobre el código máquina expulsado
por el compilador. Este programa final es lo que el
usuario ejecuta, por lo que el código fuente original ya
no es necesario.
Figure 2.3: Compiladores y Linkers

Una desventaja de compilar a lenguaje máquina, es que


el programa solo funcionará en ese tipo particular de
máquina. Los programas compilados para ordenadores
Windows no funcionan en ordenadores Apple
Macintosh o Linux.

Debido a que las fases de compilación y unión pueden


ser difíciles para los programadores noveles, algunos
lenguajes se ejecutan usando intérpretes. Estos
programas ven el código fuente y lo interpretan como
instrucciones de lenguaje máquina al vuelo. También
permiten que los mismos programas se ejecuten en
ordenadores Windows, Mac y Unix, con un intérprete
disponible para cada plataforma.

La desventaja de usar intérpretes es que es más lento


operar a través de ellos, en lugar de hacerlo en el
lenguaje máquina nativo.
Figure 2.4: Intérprete

Python es un ejemplo de lenguaje interpretado. Es más


fácil desarrollar en Python que en C, pero Python
funciona más lento y debe tener un intérprete de Python
para funcionar.

Los lenguajes como Java usan un sistema donde los


programas son compilados a lenguaje máquina que
posteriormente correrán sobre una Máquina Virtual de
Java (JVM), en lugar de hacerlo sobre la máquina real.
Otro lenguaje popular que hace esto es C#, un lenguaje
del tipo: Infraestructura de Lenguaje Común (CLI) que
se ejecuta sobre una máquina del tipo: Sistema de
Ejecución Virtual(VES). Un tratamiento completo de
todos ellos está más allá del ámbito de este libro. De
todas formas, te animo a leer más sobre el asunto.

Hoy en día existen muchos lenguajes distintos. Debido


a que los ordenadores realizan diferentes tipos de
tareas, se han desarrollado lenguajes especializados en
distintos ámbitos. Lenguajes como C están bien para
sistemas operativos y pequeños ordenadores
embebidos. Otros lenguajes como PHP están
especializados en páginas web. Python es un lenguaje
de propósito general que esta especializado en ser fácil
de usar.

La empresa Tiobe realiza un seguimiento sobre la


popularidad de varios lenguajes de programación en
un índice que se actualiza mensualmente. Es
recomendable echar un vistazo aquí y a los boletines de
ofertas laborales como DICE, para estar al día de los
lenguajes que demanda la industria.

Por suerte, casi todos los lenguajes comparten los


mismos elementos comunes, y una vez que has
aprendido uno de ellos, los mismos conceptos se aplican
al resto.

Si quieres ver una divertida historia de la informática, te


recomiendo: Triumph of the Nerds (El triunfo de los
Nerds) de Robert X Cringely, una serie en tres capítulos
sobre los inicios de la informática. La película es lo
bastante entretenida como para que tu familia al
completo la disfrute.(La puedes encontrar subtitulada
en español en YouTube) También te recomiendo el
libro Accidental Empires por si te gusta más la lectura
que la tele.

• Triumph of the Nerds Part 1


• Triumph of the Nerds Part 2
• Triumph of the Nerds Part 3

¿Qué ocurre después de esos vídeos? ¡Ni si quiera tratan


del nacimiento de Internet! Para eso puedes mirar la
serie de vídeos Nerds 2.0.1 también de Robert X
Cringely.

2.2 Repaso
2.2.1 Test

Haz click para ir al Test.

2.2.2 Ejercicios

Haz click para ir a los Ejercicios.

2.2.3 Taller

Haz click para ir al Taller.


Chapter 3: Juegos de Preguntas y
Sentencias If
¿Cómo le decimos a una jugadora que ha superado el
mayor puntaje? ¿Cómo le contamos que se ha quedado
sin 'vidas'? ¿Cómo le explicamos que ella tiene la llave
para abrir la puerta cerrada?
Vídeo: Introducción a las sentencias if

Lo que necesitamos es la sentencia if. La sentencia if es


conocida también como sentencia condicional. (Puedes
usar el término 'sentencia condicional' para cuando
quieras impresionar a tu público con lo listo que eres.)
La sentencia if permite al ordenador tomar una
decisión. ¿Hace calor en el exterior? ¿Ha llegado al
borde de la pantalla la nave espacial? ¿Has retirado
mucho dinero de tu cuenta? Un programa puede
analizar estas condiciones mediante la sentencia if.

3.1 Comparaciones Básicas


Aquí tenemos algunos ejemplos de estas sentencias if.
La primera sección establece dos variables (a y b) para
ser usadas con las sentencias if. Entonces, dos
sentencias if nos muestran cómo comparar las variables
y observar si una de ellas es mayor que la otra. Presionar
el botón “Inicio” y observa cómo el ordenador evalúa el
código.

# Variables usadas en el ejemplo de sentenc


ia if
a = 4
b = 5

Variables:
# Comparaciones básicas
a=4
if a < b: b=5
print ("a es menor que b") Resultado:

a es menor que b
if a > b:
Hecho
print ("a es mayor que b")

print("Hecho")

Inicio

Como a es menor que b, la primera sentencia imprimirá


ese código cuando se ejecute. Si ambas variables, a y b,
fueran iguales a 4, entonces, ninguna de las
sentencias if anteriores imprimiría nada. El número 4
no es mayor a 4, por lo que la sentencia if fallaría.

Para mostrar el desarrollo de un programa, podemos


usar un diagrama de flujo. En general, la mayoría de
personas son capaces de leer un diagrama de flujo, aun
cuando no tengan experiencia alguna en programación.
Observa cuán fácilmente puedes comprender la
Figura 3.1.

Figure 3.1: Diagrama de Flujo

Este libro no trata en profundidad los diagramas de flujo


ya que son un poco tediosos. Pero si lo que quieres es ser
un programador estrella, te recomiendo que leas más
sobre ello en:
http://es.wikipedia.org/wiki/Diagrama_de_flujo

El ejemplo anterior buscaba algo que fuera mayor que o


menor que. Los números que hubieran sido iguales no
habrían superado la prueba. Para comprobar valores
mayores que o iguales que, los siguientes ejemplos nos
muestran cómo hacerlo:

if a <= b:
print ("a es menor o igual que b")

if a >= b:
print ("a es mayor o igual que b")

Los símbolos <= y >= deben usarse en orden, no debiendo


haber espacios entre ellos. Por ejemplo, =<no
funcionaría, ni tampoco < =.

Cuando escribimos estas sentencias en una prueba,


algunos estudiantes prefieren usar el símbolo ≤. Por
ejemplo:
if a ≤ b:

Este símbolo ≤ realmente no funciona en un programa.


Es más, la mayoría ni siquiera sabe cómo escribirlo con
el teclado (En el caso que tengan la curiosidad, para
escribirlo hay que presionar la tecla 'alt' junto con
escribir 243 en el teclado numérico) O sea, cuando
escriban su código, recuerden que deben escribir <= y no
≤. Mucha gente pierde puntos en un examen por esta
razón; no sean de esta clase personas, por favor.
El siguiente trozo de código comprueba si dos objetos
son iguales o no. El operador para comprobar 'es igual
a' es == y el operador para 'no igual a' es !=. Aquí se
muestran en acción:

# Igual
if a == b:
print ("a es igual que b")

# Distinto
if a != b:
print ("a y b no son iguales")

¡Atención! Un error común.

ATENCIÓN: Es muy fácil mezclar el uso de == y =. Debes


usar == si la pregunta es si son iguales, y usar = si estás
asignando un valor.

Los dos errores más comunes con los


operadores = y == se muestran aquí debajo:

# Esto está mal


a == 1

# Esto también está mal


if a = 1:
print ("A es uno")
¡Stop! Por favor, tómate un momento, vuelve, y estudia
con cuidado los últimos dos ejemplos de código.
Ahorrarás tiempo en el futuro si comprendes cuándo
usar cada uno, = y ==. No lo adivines.

3.2 Indentación
La indentación importa. Cada línea indentada debajo de
la sentencia if solo se ejecutará si la sentencia if es cierta:

if a == 1:
print("Si a es uno, esto se imprimirá.")
print("Lo mismo que esto.")
print("Y que esto.")

print("Esto siempre se imprimirá ya que no está indentado.")

La indentación debe ser la misma. Este trozo de código


no funciona.

if a == 1:
print("Indentado dos espacios.")
print("Indentado cuatro. Esto generará un error.")
print("El ordenador te pedirá que te decidas.")

Una vez que una sentencia if ha terminado, no es


posible re-indentar para volver a ella. La prueba debe
volver a realizarse otra vez.
if a == 1:
print("Si a es uno, esto se imprimirá.")
print("Lo mismo que esto.")

print("Esto siempre se imprimirá ya que no está indentado.")


print("Esto generará un error.¿Por qué se ha indentado?")

3.3 Uso de Y(And) / O(Or)


Una sentencia if puede comprobar múltiples
condiciones encadenando comparaciones con and y or.
Estos también son considerados operadores de la misma
forma que + o - lo son.

# Y
if a < b y a < c:
print ("a es menor que b y c")

# O no exclusiva
if a < b or a < c:
print ("a es menor que b o c (o ambos)")

Nos repetimos.

Un error común es omitir una variable cuando la


comparamos frente a condiciones múltiples. El
siguiente código no funciona porque el ordenador no
sabe contra quién debe comparar la variable c. Por si
solo no asumirá que debe compararla con a.

# Esto no está bien


if a < b or < c:
print ("a es menor que b y c")

3.4 Variables Booleanas


Python posee variables booleanas. ¿Pero qué son las
variables booleanas? Las variables booleanas pueden
almacenar, tanto un valor cierto True, como uno
falso False. El Álgebra Booleana fue desarrollada
por George Boole allá por 1854. ¡Si hubiera llegado a
saber lo importante que su trabajo ha sido para la lógica
de los ordenadores!

Una sentencia if requiere de una expresión que evaluar


a True o False. Lo que puede parecer extraño, es que
realmente no necesita hacer ninguna comparación si
una variable ya se evalúa como True o False.

# Tipo de dato booleano. Esto sí está aceptado!


a = True
if a:
print ("a es cierto")
Tiempo atrás, cuando estaba en la escuela, era popular
hacer alguna declaración falsa. Esperabas tres segundos,
entonces gritabas “¡NO!”. Bueno, aun cuando tu
ordenador piense que esto es poco convincente, si vas a
hacerlo, debes empezar con un operador not. El siguiente
código usa el not para cambiar el valor de a, de cierto a
falso.

# Como usar la función not


if not(a):
print ("a es falso")

Debido a que not es un operador y no una función, los


paréntesis son innecesarios. Esto es correcto también:

# Como usar la función not


if not a:
print ("a es falso")

También es posible usar variables booleanas con los


operadores and y or.

a = True
b = False

if a and b:
print ("a y b son ambos ciertos")

¿Alguien sabía que cierto/falso podría ser difícil?


También es posible asignar a una variable el resultado
de una comparación. En el siguiente código, las
variables a y b son comparadas. Si son
iguales, c será True, de otra forma c será False.

a = 3
b = 3
# La siguiente línea parece extraña, pero es correcta.
# c será cierta o falsa, dependiendo si
# a y b son iguales.
c = a == b
# Imprime el valor de c, en este caso True
print(c)

Cero significa falso (False). Cualquier otra cosa es cierto (True).

Es posible crear un sentencia if con una condición que


no se evalúa como cierta o falsa. No es lo comúnmente
deseado, pero es importante comprender cómo maneja
el ordenador estos valores durante las búsqueda de
problemas. La siguiente estructura es correcta y
provocará que se imprima el texto debido a que los
valores en la sentencia if no son cero:

if 1:
print ("1")
if "A":
print ("A")
El siguiente código no imprimirá nada, debido a que el
valor en la sentencia if es cero y es tratado como False.
Cualquier otro valor distinto a cero es considerado
como True.

if 0:
print ("Cero")

En el código siguiente, la primera sentencia if parece


que funciona. El problema es que siempre se evaluará
como cierta aun cuando la variable a no sea igual a b.
Esto se debe a que b es considerada por sí misma como
True.

a = "c"
if a == "B" or "b":
print ("a es igual que b. A lo mejor.")

# Esta es una mejor forma de escribir la sentencia if.


if a == "B" or a == "b":
print ("a es igual que b.")

3.5 Else y Else If


Debajo está el código que obtiene la temperatura del
usuario e imprime si hace calor.

temperatura = int(input("¿Cuál es la temperatura en Fahrenheit?"))


if temperatura > 90:
print("Fuera no hace calor")
print ("Hecho")

Si la programadora quiere un código que se ejecute


cuando no hace calor, puede usar la sentencia else.
Observa cómo else está alineada con la i en la
sentencia if, y cómo le siguen dos puntos, tal como en la
sentencia if.

En el caso de una estructura de control del tipo if...else,


un bloque de código siempre será ejecutado. El primer
bloque se ejecutará si la estructura se evalúa como True,
y en caso de que se evalúe como False, se ejecuta el
segundo bloque.

temperatura = int(input("¿Cuál es la temperatura en Fahrenheit? "))


if temperatura > 90:
print("Hace calor fuera")
else:
print("Afuera no hace calor")
print ("Hecho")

Es posible encadenar varias sentencias if usando la


estructura de control else...if. En Python esto abrevia
como elif.

temperatura = int(input("¿Cuál es la temperatura en Fahrenheit? "))


if temperatura > 90:
print("Hace calor fuera")
elif temperatura < 30:
print("Hace frío fuera")
else:
print("Afuera no hace calor")
print ("Hecho")

Vídeo: El orden en la lógica de las sentencias if

En el siguiente código, el programa imprimirá “Hace


calor fuera” aun cuando el usuario introduzca 120
grados. ¿Por qué? ¿De qué forma podemos corregir el
código? Si no consigues resolverlo, mira el vídeo.

temperatura = int(input("¿Cuál es la temperatura en Fahrenheit? "))


if temperatura > 90:
print("Hace calor fuera")
elif temperatura > 110:
print("¡Caramba, casi podemos freír un huevo sobre el asfalto!")
elif temperatura < 30:
print("Hace frío fuera")
else:
print("Se está bien afuera")
print ("Hecho")

3.6 Comparaciones de textos


Es posible usar una sentencia if para comprobar texto.
(Observación: si usamos Eclipse en lugar del
recomendado IDLE, el ejemplo no funcionará debido a
que la entrada contendrá un retorno de carro extra al
final.)

nombre_usuario = input("¿Cuál es tu nombre? ")


if nombre_usuario == "Paul":
print("Tienes un nombre bonito.")
else:
print("Tu nombre está bien.")

El ejemplo anterior solo funciona si el usuario introduce


“Paul”. No funcionará con “paul” o “PAUL”.

Un error muy común es olvidarse de las comillas


alrededor de la cadena de texto que va a compararse. En
el siguiente ejemplo, el ordenador pensará que Paul es
una variable que almacena un valor. Mostrará un error
debido a que no sabe qué se ha almacenado en esa
variable Paul.

nombre_usuario = input("¿Cuál es tu nombre? ")


if nombre_usuario == Paul: # Esto no funciona, ya que olvidamos las comil
las
print("¿Cuál es tu nombre?")
else:
print("Tu nombre está bien.")
3.6.1 Textos múltiples

Cuando comparamos una variable con múltiples


cadenas de texto posibles, es importante recordar que la
comparación debe incluir a la variable. Por ejemplo:

# Esto no funciona! Siempre será cierto


if nombre_usuario == "Paul" or "Mary":

En todo caso, el código sería:

# Esto sí funciona
if nombre_usuario == "Paul" or nombre_usuario == "Mary":

Esto se debe a que con cualquier valor distinto de cero,


el ordenador asume que es True. Así, para el
ordenador, "Mary" es la misma cosa que True y por lo tanto
ejecutará el código de la sentencia if.

3.6.2 Comparaciones sin tener en cuenta la


capitalización

Si el programa necesita emparejar el texto entrante sin


tener en cuenta la capitalización, la forma más fácil de
conseguirlo es convertir todo a minúsculas. Esto se
consigue con el comando lower.
El siguiente ejemplo tomará todo lo que el usuario
introduzca, lo convertirá a minúsculas, y entonces,
realizará la comparación. Importante: no compararlo
frente a una cadena de texto que contenga mayúsculas.
Si la entrada es convertida a minúsculas, y luego
pretendemos compararla con una cadena con
mayúsculas, no hay forma de emparejarlas.

nombre_usuario = input(¿Cuál es tu nombre? ")


if nombre_usuario.lower() == "paul":
print("Tienes un nombre bonito.")
else:
print("Tu nombre está bien.")

3.7 Ejemplo de Sentencias if


Vídeo: Ejemplo de Sentencias If

El siguiente conjunto de ejemplos tratan todos los


conceptos tratados. El vídeo avanza sobre cada línea de
código, explicando cómo funciona.

El vídeo usa un editor integrado llamado Eclipse. La


versión por defecto de Eclipse no trabaja con Python,
pero la versión PyDev sí lo hace. El editor PyDev se
puede obtener gratuitamente en:
http://pydev.org/
El editor es complejo, pero posee muchas opciones y
puede ser un entorno de desarrollo potente. Algunos
programadores prefieren usar entornos como PyDev
que tienen plug-ins para casi todo, excepto traerte un
café. Otros, por el contrario prefieren entornos más
minimalistas que 'distraigan menos'.

# Sample Python/Pygame Programs


# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Vídeo explicativo: http://youtu.be/pDpNSck2aXQ

# Variables usadas para el ejemplo de declaraciones ”if”

a = 4
b = 5
c = 6

# Comparaciones básicas
if a < b:
print ("a es menor que b")

if a > b:
print ("a es mayor que b")

if a <= b:
print ("a es menor o igual a b")
if a >= b:
print ("a es mayor o igual a b")

# OBSERVACIÓN: Es muy fácil confundir el uso de == y =.


# Utiliza == si estás preguntando si son iguales.
# Utiliza = si estás asignando un valor a una variable.
if a == b:
print ("a es igual a b")

# Not equal
if a != b:
print ("a y b no son iguales")

# And
if a < b and a < c:
print ("a es menor que b y c")

# Non-exclusive or
if a < b or a < c:
print ("a es menor que a o b (o ambos)")

# Tipos Booleanos de datos. Esto es correcto!


a = True
if a:
print ("a es cierto")

if not(a):
print ("a es falso")

a = True
b = False

if a and b:
print ("a y b, ambos son ciertos")

a = 3
b = 3
c = a == b
print(c)

# Esto también es correcto y será cierto debido


# a que los valores no son cero:
if 1:
print ("1")
if "A":
print ("A")

# No se comportará como cierto ya que es cero.


if 0:
print ("Cero")

# Comparar variables con valores múltiples.


# El primer if parece que funciona, pero siempre se comporta como cierto
# aún cuando la variable 'a' no sea igual a 'b'.
# Esto se debe a que 'b' se considera cierta por sí misma.
a = "c"
if a == "B" or "b":
print ("a es igual a b. A lo mejor.")

# Esta es la manera más adecuada para hacer un if.


if a == "B" or a == "b":
print ("a es igual a b.")
# Ejemplo 1: Sentencia If
temperatura = int(input("¿Cuál es la temperatura en grados Fahrenheit? ")
)
if temperatura > 90:
print ("Hace calor fuera")
print ("Hecho")

# Ejemplo 2: Sentencia Else


temperatura = int(input("¿Cuál es la temperatura en Fahrenheit? "))
if temperatura > 90:
print ("Hace calor fuera")
else:
print ("Hace fresco fuera")
print ("Hecho")

#Ejemplo 4: Sentencia Else if


temperatura = int(input("¿Cuál es la temperatura en Fahrenheit? "))
if temperatura > 90:
print ("Hace calor fuera")
elif temperatura < 30:
print ("Hace fresco fuera")
else:
print ("No hace calor fuera")
print ("Hecho")

# Ejemplo 5: Orden de las declaraciones


# Algo está mal. ¿El qué?
temperatura = int(input("¿Cuál es la temperatura en grados Fahrenheit? ")
)
if temperature > 90:
print ("Hace calor fuera")
elif temperatura > 110:
print ("Vamos hombre, podría freír huevos sobre el asfalto!")
elif temperatura < 30:
print ("Hace fresco fuera")
else:
print ("Se está bien fuera")
print ("Hecho")

# Comparaciones usando strings/cadenas de texto


nombre_usuario = input("¿Cuál es tu nombre? ")
if nombre_usuario == "Pablo":
print ("Me gusta tu nombre.")
else:
print ("Ok.")

3.8 Repaso
3.8.1 Test

Haz click para ir al Test.

3.8.2 Ejercicios

Haz click para ir a los Ejercicios.

3.8.3 Taller

Haz click para ir al Taller.


Chapter 4: Adivinanzas con
Números Aleatorios y Bucles
El último paso antes de comenzar con los gráficos, es
aprender cómo iterar a través de una sección de código.
La mayoría de los juegos 'iteran'. Repiten el mismo
código una y otra vez. Por ejemplo, el siguiente juego de
adivinar un número, itera cada vez que el usuario hace
un intento:
Vídeo: El bucle for

Hey! Estoy pensando en un número al azar entre 1 y 100.


---- Intento 1
Adivina en qué número estoy pensando: 50
Demasiado alto.
--- Intento 2
Adivina en qué número estoy pensando: 25
Demasiado alto.
--- Intento 3
Adivina en qué número estoy pensando: 17
Muy alto.
--- Attempt 4
Adivina en qué número estoy pensando: 9
Muy bajo.
---- Intento 5
Adivina en qué número estoy pensando: 14
Muy alto.
---- Intento 6
Adivina en qué número estoy pensando: 12
Muy alto.
---- Intento 7
Adivina en qué número estoy pensando: 10
Muy bajo.
Ups! Te has quedado sin opciones. El número era el 11.

¿Pero espera, qué tiene que ver todo esto con gráficos y
videojuegos? Pues un montón. Cada frame
(fotograma) que el juego muestra, es uno, de una serie a
través del bucle. Probablemente estés habituado a las
estadísticas de frame-per-second (FPS) que muestran los
juegos. El FPS representa el número de veces que el
ordenador actualiza la pantalla por cada segundo. A
mayor tasa de refresco, más fluido es el juego. De todas
formas, una tasa de FPS superior a 60, es más rápida de
lo que la mayoría de monitores puede actualizar, por lo
que no tiene mucho sentido sobrepasar ese valor. La
Figura 4.1 muestra el juego Eve Online y una gráfica
donde se ve cuántos fotogramas por segundo (frames
per second) es capaz de resolver el ordenador.
Figure 4.1: FPS en videojuegos

El bucle de estos juegos funciona como el diagrama de


flujo de la Figura 4.2. A pesar de la complejidad de los
modernos videojuegos, el interior de este bucle es
parecido al programa calculadora que hicimos en el
Capítulo 1; Obtener una entrada del usuario -> Realizar
cálculos -> Expulsar un resultado. En un videojuego
intentamos repetir esto hasta 60 veces por segundo.
Figure 4.2: Bucle de juego

Es muy posible que existan bucles dentro de otros


bucles. Un auténtico 'rizar el rizo'. Observa el bloque
'Dibujar Todo' de la Figura 4.2. Establece el código del
bucle que itera repetidamente para dibujar cada objeto
del juego. Este bucle está contenido dentro de uno
mayor que dibuja cada fotograma del juego, tal como el
de la Figura 4.3.
Figure 4.3: Bucle dibuja todo

Existen dos grandes clases de bucles en Python; los


bucles for y los bucles while. Si lo que quieres es repetir
algo cierto número de veces, utiliza un bucle for. Y si lo
que quieres es repetir algo hasta que se cumpla cierta
condición (por ejemplo, cuando el usuario hace click
sobre el botón de cierre), entonces usa un bucle while.

Por ejemplo, podemos usar un bucle for para imprimir


las notas de todos los estudiantes, ya que el ordenador
sabe cuántos estudiantes hay. Sin embargo, haría falta
un bucle while para comprobar cuándo el usuario pulsa
sobre el botón del ratón, ya que el ordenador desconoce
cuánto tiempo tiene que esperar hasta que eso suceda.
4.1 Bucles For
El siguiente ejemplo de bucle for ejecuta la
función print cinco veces. Sería muy fácil ejecutarlo 100 o
1,000,000 veces con tan solo cambiar el 5 por el valor que
necesitemos repetir el bucle. Observa las similitudes
entre cómo escribimos el bucle for y cómo la sentencia if.
Ambas finalizan con dos puntos (:), y ambas usan
indentación para especificar qué líneas se ven afectadas
por la sentencia.

Salida:

No volveré a mascar chicle en


clase.
for i in range(5):
No volveré a mascar chicle en
print ("No volveré a mascar chicle en clase.
clase.")
No volveré a mascar chicle en
clase.
No volveré a mascar chicle en
clase.
No volveré a mascar chicle en
clase.

La i en la línea 1 es la variable que lleva la cuenta de las


veces que hemos iterado el programa. Es una variable
nueva y la podemos llamar con cualquier nombre legal.
Los Programadores suelen usar i como nombre de
variable, ya que la 'i' abrevia la palabra incremento. Esta
variable nos ayuda a saber cuándo debe parar el bucle.
La función range(rango) controla cuántas veces el código del
bucle debe ejecutarse. En este caso, cinco veces.

El siguiente ejemplo imprimirá 'Por favor,' cinco veces,


y '¿Puedo ir al centro comercial?' solo una. '¿Puedo ir al
centro comercial?' no ha sido indentada por lo que no es
parte del bucle for y no se imprimirá hasta que el
bucle for finalice.

Salida:

Por favor,
for i in range(5):
Por favor,
print ("Por favor,")
Por favor,
print ("¿Puedo ir al centro comercial?")
Por favor,
Inicio Por favor,
¿Puedo ir al centro comercial
?

El siguiente código toma el ejemplo anterior e indenta la


línea 3. Este cambio provocará que el programa
imprima 'Por favor,' y '¿Puedo ir al centro comercial?'
cinco veces. Como ahora la sentencia ha sido indentada,
'¿Puedo ir al centro comercial?' es ahora parte del
bucle for y la repetirá cinco veces, al igual que 'Por
favor,'.

Salida:
for i in range(5):
print ("Por favor,") Por favor,
print ("¿Puedo ir al centro comercial?" ¿Puedo ir al centro comercial
) ?
Por favor,
Inicio
¿Puedo ir al centro comercial
?
Por favor,
¿Puedo ir al centro comercial
?
Por favor,
¿Puedo ir al centro comercial
?
Por favor,
¿Puedo ir al centro comercial
?

El código siguiente imprimirá los números del 0 al 9.


Observa que el bucle empieza en 0 y no incluye al 10. Es
natural asumir que range(10) incluiría al 10, pero se detiene
justo antes de él.

Salida:

0
1
2
for i in range(10):
3
print(i)
4
5
Inicio
6
7
8
9
Un programa no está obligado a usar una variable
llamada i, podría ser cualquier otra cosa. Por ejemplo,
una programadora podría usar linea_numero si ella estuviera
procesando un archivo de texto.

Si un programador quisiera ir desde el 1 hasta el 10, en


lugar del 0 al 9, existen un par de formas para
conseguirlo. La primera es enviar a range dos números en
lugar de uno solo. El primer número es el valor inicial,
y el segundo sería el que está justo después del valor
final.

Se necesita cierta práctica para acostumbrarse a la idea


de que el bucle for incluirá al primer número pero no al
segundo. El siguiente ejemplo especifica un rango
de (1,11), donde se imprimen los números del 1 al 10. El
número inicial 1 es incluido, pero no el final, 11.

Salida:

1
for i in range(1,11): 2
print(i) 3
4
5
6
7
8
9
10

Otra forma de imprimir los números del 1 al 10 es seguir


usando range(10) y hacer que la variable ivaya de 0 a 9,
pero justo antes de imprimirla, el programador le añada
1. Cualquiera de los dos métodos son igual de válidos.

# Imprime los números del 1 al 10.


for i in range(10):
print(i + 1)

4.1.1 Contando por Números Distintos al Uno

Si el programa necesita contar por 2 o cualquier otro


incremento, la cosa es bastante fácil. Tal como en los
ejemplos anteriores, hay dos formas de hacerlo. La más
fácil es añadir un tercer número a la función range que
le diga que cuente por 2. La segunda forma es hacer que
cuente por uno, pero multiplicando la variable por 2. El
siguiente código muestra los dos métodos.

# Dos formas de imprimir los números pares Salida:


del 2 al 10
for i in range(2,12,2): 2

print(i) 4
6

for i in range(5): 8
print ((i + 1) * 2) 10
2
4
6
8
10

También es posible hacer cuentas hacia atrás, es decir


hacia cero, dándole a la función range un incremento
negativo. En el siguiente ejemplo, empezamos en 10 y
descendemos hacia cero, pero sin llegar a él, usando
incrementos de -1. La parte más ardua al crear esta clase
de bucles es que accidentalmente confundamos los
números del principio y del final. El programa empieza
con el número mayor, por lo que éste debe ir hacia el
principio. Habitualmente, los bucles for empiezan a
contar a partir del valor más pequeño que aparece
dentro de la función range.

Salida:

10
9
for i in range(10, 0, -1):
8
print(i)
7
6
5
4
3
2
1

Si los números sobre los cuales el programa debe iterar


no forman un patrón sencillo, es posible extraerlos de la
lista. (Las listas se tratan con más detalle en el Capítulo
7. Esto es tan solo un avance de lo que podemos hacer.)

for i in [2,6,4,2,4,6,7,4]:
print(i)

Esto imprimirá:

2
6
4
2
4
6
7
4

4.1.2 Bucles Anidados

Intenta predecir qué imprimirá el siguiente código.


Luego escríbelo y observa el resultado.

# ¿Qué es lo que esto imprime? ¿Por qué?


for i in range(3):
print ("a")
for j in range(3):
print ("b")

Vídeo: Bucles for anidados

El siguiente bloque es casi idéntico al anterior. Esta vez,


el segundo bucle for ha sido indentado una vez, de
forma que ahora está anidado dentro del primer bucle
for. Esto cambia significativamente su comportamiento.
Ejecútalo y observa la diferencia.

# ¿Qué es lo que esto imprime? ¿Por qué?


for i in range(3):
print("a")
for j in range(3):
print("b")

print("Hecho")

No te voy a contar lo que este código imprime, ve a un


ordenador y averígualo.
Vídeo: Animación del buclefor

4.1.3 Mantener un Total Acumulado

Una operación común a muchos bucles es la de


mantener un total acumulado en tiempo de ejecución.
Este “total acumulado” es usado con frecuencia en este
libro. Por ejemplo, para obtener el total de un marcador,
el total de las transacciones bancarias de una persona,
buscar un total para luego hallar un promedio, etc.
Probablemente quieras señalar esta parte del libro ya
que nos referiremos a ella muchas veces. En el ejemplo
siguiente, el usuario ingresa 5 números y el programa
calcula la suma total de los 5.

total = 0
for i in range(5):
nuevo_numero = int(input("Introduce un número: " ))
total += nuevo_numero
print("El total es: ",total)

Vídeo: Bucle para mantener un total acumulado

Observa que en la primera línea se crea una


variable total, y se le asigna un valor inicial de cero. Es
fácil olvidarse el crear e inicializar la variable a cero. Sin
él, el código producirá un error al llegar a la línea 4. No
sabe cómo añadir nuevo_numero a total, ya que total no tiene
ningún valor aún.

Un error común es usar i para total en lugar de nuevo_numero.


Recuerda que estamos almacenando un total para los
valores introducidos por el usuario y no un total
acumulado que lleve la cuenta de las veces que se ejecuta
el bucle.
Si hablamos de llevar la cuenta del bucle en cada
instante, podemos usar ese valor para algunas
operaciones matemáticas. Por ejemplo:

Si no estás familiarizado con este tipo de fórmulas, ésta


es una bonita manera de comenzar:

Este código suma todos los números del 1 al 100.


Demuestra un patrón común en el que se mantiene un
total acumulado dentro del bucle. Además, usa otra
variable, suma, para llevar la cuenta de la suma total.

# ¿Cuál es el valor de suma?


suma = 0
for i in range(1,101):
suma = suma + i
print(suma)

Esta es una versión diferente. Toma 5 números del


usuario y cuenta las veces que éste introduce un cero:

total = 0
for i in range(5):
nuevo_numero = int(input( "Introduce un número: "))
if nuevo_numero == 0:
total += 1
print("Has introducido ",total," ceros")
Un programador que comprenda los bucles
anidados for y los totales acumulados, debería ser capaz
de predecir el resultado para el código siguiente.

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a=a+1
print(a)

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a = a + 1
for j in range(10):
a = a + 1
print(a)

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a = a + 1
for j in range(10):
a = a + 1
print(a)

No pases de esta sección demasiado deprisa. Dale una


oportunidad y predice la salida para el código anterior.
Cuando lo tengas, cópialo en un programa Python, lo
ejecutas y mira si estabas en lo cierto. Si no fuera así,
pregúntate el por qué no.

4.2 Ejemplos de Bucles for


Este ejemplo trata de los bucles for más comunes y nos
muestra cómo funcionan.

# Sample Python/Pygame Programs


# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Imprime 'Hola' 10 veces


for i in range(10):
print("Hola")

# Imprime 'Hola' 5 veces y 'Allí' una


for i in range(5):
print("Hola")
print("Allí")

# Imprime 'Hola' 'Allí' 5 veces


for i in range(5):
print("Hola")
print("Allí")

# Imprime los números del 0 al 9


for i in range(10):
print(i)
# Dos formas de imprimir los números del 1 al 10
for i in range(1, 11):
print(i)

for i in range(10):
print(i + 1)

# Dos formas de imprimir los números del 2 al 10


for i in range(2, 12, 2):
print(i)

for i in range(5):
print((i + 1) * 2)

# Cuenta atrás desde 10 hasta 1 (el cero no)


for i in range(10, 0, -1):
print(i)

# Imprime números desde una lista


for i in [2, 6, 4, 2, 4, 6, 7, 4]:
print(i)

# ¿Qué es lo que imprime éste código? ¿Por qué?


for i in range(3):
print("a")
for j in range(3):
print("b")

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a = a + 1
print(a)

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a = a + 1
for j in range(10):
a = a + 1
print(a)

# ¿Cuál es el valor de a?
a = 0
for i in range(10):
a = a + 1
for j in range(10):
a = a + 1
print(a)

# ¿Cuál es el valor de la suma?


sum = 0
for i in range(1, 101):
sum = sum + i

4.3 Bucles While


Vídeo: El bucle while

Un bucle for se usa cuando el programa sabe que


necesita repetir un bloque de código durante un cierto
número de veces. Un bucle while se usa cuando el
programa debe iterarse hasta que se alcanza una
determinada condición.

Aunque bastante extraño, un bucle while puede usarse


para sustituir a cualquier bucle for. Se puede utilizar
para iterar una variable incremental hasta que alcance
cierto valor. Entonces, ¿por qué usar bucles for cuando
el while puede hacer todo?. Porque el bucle for es mucho
más simple de usar y codificar. Un bucle for como el
siguiente:

for i in range(10):
print(i)

...puede hacerse cono un bucle while que tenga este


aspecto:

i = 0
while i < 10:
print(i)
i = i + 1

En la línea 1 del bucle while se establece una variable


“centinela” (en realidad, contador) que se usará para
contar las veces que el bucle ha sido ejecutado. Esto
sucede automáticamente con el bucle for , eliminando la
necesidad de ese centinela. En la línea 2 empieza
realmente el bucle while con un formato similar a una
sentencia if. Si se mantiene la condición inicial, el código
del bucle se seguirá repitiendo. La línea 4 sirve para
incrementar el contador inicial del bucle. Esto sucede
automáticamente con el bucle 'for', eliminando esta
necesidad (una línea menos de código). Tal como se
puede observar, el bucle for es más compacto y fácil de
leer que un bucle while. De otra forma, los programas lo
harían todo con un bucle while.

Un error común es confundir ambos bucles. El siguiente


ejemplo muestra a un programador que no ha sido
capaz de resolver la confusión entre un bucle for y
otro while:

while range(10):
print(i)

No uses range con un bucle while!

La función range solo funciona con los bucles for. No vayas


a usarla con un bucle while!

4.3.1 Uso de Operadores de Incremento

Los operadores incrementales se usan frecuentemente


con los bucles while. Es posible abreviar el código:

i = i + 1
Con esto:

i += 1

En el bucle while se vería así:

i = 0
while i < 10:
print(i)
i += 1

Esto se puede hacer con la resta y el producto también.


Por ejemplo:

i *= 2

Es lo mismo que:

i = i * 2

Mira a ver si puedes adivinar que imprimirá esto:

i = 1
while i <= 2 ** 32:
print(i)
i *= 2
4.3.2 Iterando Hasta Que El Usuario Decide
Salir

Una operación muy habitual es iterar hasta que el


usuario realiza una petición de salida:

salir = "n"
while salir == "n":
salir = input ("¿Quieres salir? ")

Pueden existir diferentes formas de usar un bucle para


salir. Usar una variable Boolena que dispare el evento,
es una forma de manejarlo. Este es un ejemplo:

hecho = False
while not hecho:
salir = input ("¿Quieres salir? ")
if salir == "y" :
hecho = True;

ataque = input ("Es que tu elfo ha atacado al dragón? ")


if ataque == "y":
print ("Pésima opción, estás muerto.")
hecho = True;

Esto no es perfecto, ya que si ella pide salir del juego, el


código volverá a preguntar si quiere atacar al dragón.
¿Cómo podríamos arreglarlo?
Este es un ejemplo del uso del bucle while donde el código
se repite hasta que el valor se acerca lo suficiente a cero:

valor = 0
incremento = 0.5
while valor < 0.999:
valor += incremento
incremento *= 0.5
print(valor)

4.3.3 Problemas Habituales Con Los


Bucles while

La programadora quiere hacer una cuenta atrás desde


10. ¿Qué es lo que está mal y cómo se puede arreglar?

i = 10
while i == 0:
print (i)
i -= 1

¿Qué es lo que está mal en este código que pretende


contar hasta 10? ¿Qué pasará si se ejecuta? ¿Cómo se
puede arreglar?

i = 1
while i < 10:
print (i)
4.4 Ejemplo de Bucles while
El siguiente programa trata de los diferentes usos de los
que hemos hablado sobre el bucle while.

# Sample Python/Pygame Programs


# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Podemos emplear un bucle while allí donde, también, podríamos usar un b


ucle for:
i = 0
while i < 10:
print(i)
i = i + 1

# Es lo mismo que:
for i in range(10):
print(i)

# Es posible simplificar el código:


# i=i+1
# Con lo siguiente:
# i += 1
# Esto lo podemos hacer también con la resta y la multiplicación.
i = 0
while i < 10:
print(i)
i += 1
# ¿Qué imprimiremos aquí?
i = 1
while i <= 2 ** 32:
print(i)
i *= 2

# Una tarea muy habitual, es iterar hasta que el usuario realiza una peti
ción de salida.

salir = "n"
while salir == "n":
salir = input ("¿Quieres salir? ")

# Existen diversas formas para salir de un bucle. Un operador booleano qu


e dispare el evento es una
# forma de conseguirlo.
hecho = False
while not hecho:
salir = input ("¿Quieres salir? ")
if salir == "s":
hecho = True;

ataca = input ("¿El elfo atacó al dragón? ")


if ataca == "s":
print("Mala elección, estás muerto.")
hecho = True;

valor = 0
incremento = 0.5
while valor < 0.999:
valor += incremento
incremento *= 0.5
print(valor)

# -- Problemas habituales con los bucles while --

# El programador quiere hacer una cuenta atrás empezando en 10.


# ¿Qué es lo que está mal, y cómo lo podemos arreglar?
i = 10
while i == 0:
print (i)
i -= 1

# ¿Qué es lo que está mal en este bucle que intenta contar hasta 10?
# ¿Qué sucederá cuando lo ejecutemos?
i = 1
while i < 10:
print (i)

4.5 Números Aleatorios


Los números aleatorios son usados intensivamente en
computación para programas que incorporan juegos o
simulaciones.

4.5.1 La Función randrange


Vídeo: Números Aleatorios
Python, por defecto, no sabe cómo construir números
aleatorios. Python necesita importar una biblioteca de
códigos que permitan crear este tipo de números. Es
decir, para usar números aleatorios, lo primero que
debería aparecer encima de cualquier línea de código
del programa es una instrucción import:

import random

Tal como hicimos con pygame, es importante no crear


ningún archivo que lleve el mismo nombre que la
biblioteca importada. La creación de un archivo
llamado random.py provocaría que Python empezara por
importarlo en primer lugar, en vez de hacerlo con la
biblioteca que crea los números aleatorios.

Luego de esto, podemos crear números aleatorios con la


función randrange. Por ejemplo, este código genera
números aleatorios entre 0 y 49. Por defecto, el límite
inferior es 0.

mi_numero = random.randrange(50)

El siguiente ejemplo genera números aleatorios entre


100 y 200. Tal como pasa con la función range, el segundo
parámetro especifica el límite superior, el cual no está
incluido. Por lo tanto, si quieres números aleatorios
hasta 200, incluido, especifica 201.

mi_numero = random.randrange(100,201)

¿Pero qué sucede si lo que quieres es un objeto aleatorio


en lugar de un número? Eso necesita una lista. No
trataremos en detalle las listas hasta el Capítulo 7, pero
para tener una idea de cómo sería elegir al azar un objeto
de una lista, veamos lo siguiente:

mi_lista = ["piedra", "papel", "tijera"]


random_index = random.randrange(3)
print(mi_lista[random_index])

4.5.2 La Función random

Todo el código anterior genera números enteros. Si


necesitamos un número real, el programador puede
usar la función random.

El siguiente código genera un número aleatorio entre 0


y 1, como por ejemplo 0.4355991106620656.

mi_numero = random.random()
Con algo de matemáticas básicas, este número puede ser
adaptado. Por ejemplo, el código siguiente genera un
número aleatorio real entre 10 y 15:

mi_numero = random.random() * 5 + 10

4.6 Repaso
4.6.1 Test

Haz click para ir al Test.

4.6.2 Ejercicios

Haz click para ir a los Ejercicios.

4.6.3 Taller

Haz click para ir al Taller.


Chapter 5: Introducción a los
Gráficos
Bueno, ahora que ya puedes crear bucles, es momento
de avanzar en el aprendizaje de cómo crear gráficos.
Este capítulo trata de:

• El manejo de las coordenadas x, y por el


ordenador. No es como el sistema de coordenadas
que aprendimos en clase de matemáticas.
• Cómo especificar los colores. Con los millones de
colores que hay por escoger, explicarle al
ordenador cuál usar no es tan fácil como decirle
simplemente “rojo”
• Cómo abrir una ventana en blanco para dibujar.
Todo artista necesita un lienzo.
• Cómo dibujar líneas, rectángulos, elipses y arcos.

5.1 Sistema de Coordenadas de la


Computadora
Vídeo: Sistemas de coordenadas para gráficos de ordenador

El sistema de coordenadas Cartesiano mostrado en la


figura 5.1(Wikimedia Commons), es el que la mayoría
usa a la hora de dibujar gráficas. Es el sistema que nos
enseñaron en la escuela. El ordenador usa uno similar,
pero distinto. Entender por qué es diferente, requiere
conocer un poco de la historia de la computación.

Figure 5.1: Sistema de Coordenadas Cartesianas

Durante los primeros 80, la mayoría de sistemas de


computación estaban basados en texto, y no permitían
trabajar con gráficos. La Figura 5.2(Wikimedia
Commons) muestra una hoja de cálculo básica que se
ejecutaba en un ordenador Apple ][ muy popular en los
80. Cuando se colocaba texto sobre la pantalla, los
programadores empezaban por la parte superior,
llamándola línea 1. La pantalla continuaba hacia abajo
durante otras 24 líneas y por 40 caracteres a través.

Figure 5.2: Pantalla de texto de un Apple en sus inicios

Aun cuando se usara texto plano, era posible realizar


rudimentarios gráficos, tan solo empleando los
caracteres del teclado. Mira este gatito de la Figura 5.3 y
observa con atención cómo ha sido dibujado. A la hora
de hacer este tipo de arte, los caracteres se colocaban
empezando desde la línea 1 en la parte superior de la
pantalla.

Figure 5.3: Pantalla de texto

Más tarde, el conjunto de caracteres de amplió para


incluir cajas y otras formas básicas de dibujo. Los
caracteres se podían escribir en diferentes colores. Tal
como se muestra en la Figura 5.4 los dibujos
evolucionaron. Si buscas en la web por “ASCII art”
encontrarás muchos más ejemplos de todo esto.
Figure 5.4: Pantalla de texto espacial

Una vez que los ordenadores evolucionaron para poder


controlar individualmente los píxeles para realizar
gráficos, el sistema de coordenadas basado en texto se
estancó.
Figure
5.5: Sistema de coordenada del ordenador

Las coordenadas funcionan de la misma forma que en


el sistema Cartesiano. Pero las coordenadas lo hacen al
revés. En lugar de que la coordenada cero se encuentre
en la base del gráfico, como en las gráficas Cartesianas,
está en la parte superior de la pantalla. A medida que
los valores de aumentan, el sistema de coordenadas del
ordenador se mueve hacia abajo, de la misma forma que
las líneas de un texto, en lugar de hacerlo como en un
gráfico Cartesiano estándar. Ver la Figura 5.5.

Además, se observa que la pantalla muestra el


cuadrante inferior derecho del plano Cartesiano,
mientras que en el sistema de coordenadas estándar, lo
hace habitualmente en el cuadrante superior derecho. Es
posible crear objetos en posiciones que tengan
coordenadas negativas, pero se dibujarán fuera de
pantalla. Esto también puede ser útil. El ordenador se da
cuenta de qué es lo que está fuera de pantalla, con lo que
el programador no se debe preocupar mucho por ello.

5.2 Biblioteca Pygame


Vídeo: Abriendo una Ventana

Para poder hacer gráficos fácilmente, usaremos


Pygame. Pygame es una biblioteca con códigos
desarrollados por otras personas que hacen más fácil:

• Dibujar formas gráficas


• Mostrar imágenes de mapas de bits
• Realizar animaciones
• Interactuar con el teclado, ratón, joystick.
• Reproducir sonidos
• Detectar cuando colisionan objetos

La primera tarea que un programa Pygame necesita


hacer es cargar e inicializar la biblioteca Pygame. Todo
programa que use Pygame debería empezar con estas
líneas:
Importar e inicializar Pygame
# Importa la libraría de funciones llamada 'pygame'
import pygame

# Inicializa el motor de juegos

pygame.init()

Si aún no has instalado Pygame, las instrucciones para


hacerlo se encuentran en la sección antes de empezar .
Si Pygame no está instalado en el ordenador, obtendrás
un mensaje de error al intentar ejecutar import pygame.

No llames “pygame.py” a ningún archivo

Importante: El comando import pygame busca un archivo de


biblioteca llamado pygame. Si un programador crea un
programa nuevo llamándolo pygame.py, provocará que el
ordenador lo importe, en lugar de la biblioteca
requerida. Esto impedirá que cualquier programa
pueda ejecutarse a menos que borremos ese
archivo pygame.py

5.3 Colores
Los siguiente que necesitamos es añadir unas variables
que definan los colores que usaremos en nuestro
programa. Los colores se definen mediante una lista:
rojo, verde y azul (red, green, blue). ¿Has oído alguna
vez hablar de un monitor RGB? De ahí viene el término;
Red-Green-Blue. Con los antiguos monitores, podías
sentarte realmente cerca a él y construir los colores RGB
individuales. Al menos hasta que tu madre te llamara y
te dijera que no te sentaras demasiado cerca de la tele.
Esto es muy difícil hacerlo en la actualidad debido a la
alta resolución de los monitores.

Cada elemento de la triada RGB es un número en el


rango de 0 a 255. Cero significa que no hay color, y 255
le dice al monitor que muestre todo el color posible. Los
colores se combinan de forma aditiva, es decir si los tres
son especificados a 255, el color del monitor será blanco.

Las listas en Python puedes estar rodeadas tanto por


corchetes como por paréntesis (El Capítulo 7 cubre el
tema de listas con detalle y las diferencias entre los dos
tipos). En las listas, los números se separan
individualmente mediante comas. Debajo hay un
ejemplo que crea unas variables y las inicializa mediante
listas de tres números. Estas listas las usaremos más
adelante para especificar colores
Definir colores
# Definir algunos colores

NEGRO = ( 0, 0, 0)

BLANCO = (255, 255, 255)

VERDE = (0, 255, 0)

ROJO = (255, 0, 0)
¿Por qué están en mayúsculas estas variables? Recuerda
el Capítulo 1 en que una variable que no cambia se
llama constante. No esperamos que el color negro vaya a
cambiar; es una constante. Destacamos las constantes
por mayúsculas. Si esperamos que un color vaya a
cambiar, algo así como color_cielo, el cual cambia a medida
que el sol se oculta, entonces, sería una variable en
minúsculas.

Usando la shell interactiva de IDLE, intenta definir estas


variables e imprimir su salida. Si los colores de arriba no
son los que buscas, puedes tú mismo definirlos. Para
escoger un color busca en internet un “selector de
colores” tal como el que se muestra en la Figura 5.6. Este
selector de colores se encuentra en:
http://www.colorpicker.com/

Figure 5.6: Selector de Colores


Extra: Algunos selectores de colores los especifican en
hexadecimal. Puedes introducir números
hexadecimales si los empiezas con 0x. Por ejemplo:
BLANCO = (0xFF, 0xFF, 0xFF)

Es posible que el programa necesite usar el valor


de cuando tenga que dibujar arcos, por lo que será un
buen momento para definir una variable que contenga
el valor de . (También es posible importarlo desde la
biblioteca math como math.pi.)
PI = 3.141592653

5.4 Abrir una Ventana


Hasta ahora, los programas que hemos creado solo
imprimen texto por pantalla. Estos programas no abren
ninguna ventana, tal como lo hacen los programas
modernos. El código para abrir una ventana no es
complicado. Debajo se puede leer el código necesario, el
cual crea una ventana de 700 píxeles de largo por 500 de
alto:
Abrir y establecer las dimensiones de una ventana
dimensiones = (700, 500)

pantalla = pygame.display.set_mode(dimensiones)

¿Por qué set_mode? ¿Por qué no open_window? La razón es que


este comando puede hacer muchas más cosas que solo
abrir una ventana. También crea juegos que se ejecutan
en modo pantalla completa. Esto borra el menú de
inicio, el título de las barras y le da al juego, control
sobre toda la pantalla. Debido a que esta opción es
ligeramente más compleja de usar, y la mayoría de
personas prefieren, de todas formas, juegos con
ventanas, nos saltaremos las discusión sobre juegos a
pantalla completa. Pero si lo que quieres es profundizar
sobre juegos a pantalla completa, revisa esta
documentación sobre el comandodisplay de pygame .

También, ¿por qué dimensiones = (700, 500) y no dimensiones = 700,


500? Es la misma razón por la que pusimos paréntesis

alrededor de las definiciones de los colores. Python,


normalmente, no puede almacenar dos números (alto y
largo) en una sola variable. La única forma de
conseguirlo es si los números se almacenan en una lista.
Las listas necesitan paréntesis o corchetes.
(Técnicamente, unos paréntesis que rodean a un
conjunto de números se llaman una tupla o lista
inmutable. Las listas rodeadas de corchetes son,
simplemente, listas. Un experimentado programador en
Python se revolvería al llamar lista a unos números
rodeados por paréntesis, en lugar de llamarla tupla. Las
listas se explican con detalle en el Capítulo 7.
Para establecer el título de una ventana (el cual se
muestra en la barra de título) usamos la siguiente línea
de código:
Establecer el título de la ventana
pygame.display.set_caption("Super Juego del Profesor Craven")

5.5 Interactuando Con el Usuario


Con el código escrito hasta ahora, el programa crearía
una ventana que se colgaría inmediatamente. El usuario
no podría interactuar con ella, incluso no podría
cerrarla. Todo eso necesita ser programado.
Necesitamos añadir un código mediante el cual el
programa espere en un bucle hasta que el usuario haga
click sobre “salida.”

Esta es la parte más complicada del programa y una


comprensión completa del mismo no es necesaria
todavía. Pero necesitamos una cierta idea de qué es lo
que hace. Por ello, dedícale un tiempo a estudiarla y a
hacer preguntas.
Establecer el bucle principal del programa
#Itera hasta que el usuario pincha sobre el botón de cierre.
1 hecho = False

2 # Se usa para gestionar cuan rápido se actualiza la pantalla

reloj = pygame.time.Clock()
3
# -------- Bucle Principal del Programa -----------
4 while not hecho:

# TODOS LOS EVENTOS DE PROCESAMIENTO DEBERÍAN IR DEBAJO DE ESTE COMENTARIO


5
for evento in pygame.event.get(): # El usuario hizo algo
6 if evento.type == pygame.QUIT: # Si el usuario pincha sobre cerrar

7 hecho = True # Esto que indica que hemos acabado y sale de este bucle

# TODOS LOS EVENTOS DE PROCESAMIENTO DEBERÍAN IR ENCIMA DE ESTE COMENTARIO


8
# TODA LA LÓGICA DEL JUEGO DEBERÍA IR DEBAJO DE ESTE COMENTARIO
9
# TODA LA LÓGICA DEL JUEGO DEBERÍA IR ENCIMA DE ESTE COMENTARIO
10 # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR DEBAJO DE ESTE COMENTARIO

11 # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO

# Limita a 20 fotogramas por segundo (frames per second)


12
reloj.tick(20)
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Es probable que necesitemos añadir código que maneje
las entradas del teclado y ratón. Ese código irá entre los
comentarios de los eventos de procesamiento. El código
que determina cuándo disparar las balas y el cómo se
mueven los objetos irá entre los comentarios de la lógica
del juego. Hablaremos de esto en capítulos posteriores.
El código de dibujo irá dentro de su respectivo
comentario.

5.5.1 Bucle para el Procesamiento de Eventos


Presta Atención!

¡Cuidado! Uno de los problemas más frustrantes que


tienen los programadores, es estropear el bucle de
procesamiento de eventos. Este código “procesador de
eventos” maneja todos las pulsaciones del teclado, los
clicks del ratón y algunos otros tipos de eventos. Por
ejemplo, tu código podría verse así:
Muestra de bucle de eventos - Correcto
for evento in pygame.event.get():
1
if evento.type == pygame.QUIT:
2 print("El usuario solicitó salir.")
3 elif evento.type == pygame.KEYDOWN:

4 print("El usuario presionó una tecla.")

elif evento.type == pygame.KEYUP:


5
print("El usuario soltó una tecla.")
6 elif evento.type == pygame.MOUSEBUTTONDOWN:
7 print("El usuario presionó un botón del ratón")

8
9

Los eventos (como presionar teclas) van todos en una


lista. El programa usa un bucle for para iterar a través de
cada evento. Usando una cadena de instrucciones if el
código reconoce qué clase de evento ocurrió, y el código
para manejarlo va en la declaración if.

Todas las instrucciones if deberían ir juntas en un solo


bucle for. Un error común cuando copiamos y pegamos
código, no es mezclar bucles de dos programas, sino
tener dos bucles de eventos.
Ejemplo de un bucle de eventos INCORRECTO
# Este es un bucle de eventos
1
for evento in pygame.event.get():
2
if evento.type == pygame.QUIT:
3 print("El usuario solicitó salir.")

4 elif evento.type == pygame.KEYDOWN:

print("El usuario presionó una tecla.")


5
elif evento.type == pygame.KEYUP:
6 print("El usuario soltó una tecla.")

7 # Aquí el programador ha pegado otro bucle de eventos

# en el programa. Esto es INCORRECTO. Los eventos ya han sido


8
# procesados.
9
for evento in pygame.event.get():
10 if evento.type == pygame.QUIT:
11 print("El usuario solicitó salir.")

elif evento.type == pygame.MOUSEBUTTONDOWN:


12
print("El usuario presionó un botón del ratón")
13
14
15
16
17

El bucle for de la línea 2 ya recogió todos los eventos del


usuario. El bucle for en la línea 13 no recogerá nada ya
que fueron procesados en el bucle anterior.

Otro problema típico es empezar a dibujar y, entonces,


intentar finalizar el bucle de eventos:
Ejemplo de un bucle de eventos INCORRECTO

1 for evento in pygame.event.get():

2 if evento.type == pygame.QUIT:

print("El usuario solicitó salir.")


3
elif evento.type == pygame.KEYDOWN:
4 print("El usuario presionó una tecla.")
5 pygame.rect.draw(pantalla, VERDE, [50,50,100,100])

6 # Este es el código que procesa los eventos. Pero no está en el bucle

# 'for' que procesa los eventos. No trabajará de forma fiable.


7
if evento.type == pygame.KEYUP:
8 print("El usuario soltó una tecla.")

9 elif evento.type == pygame.MOUSEBUTTONDOWN:

print("El usuario presionó un botón del ratón")


10
11
12
13
14
15

Esto provocará que el programa ignore algunos


comandos del teclado y del ratón. ¿Por qué? El
bucle forprocesa todos los eventos en una lista, por lo
que si hay dos teclas que son pulsadas, el
bucle forprocesará ambas. En el ejemplo anterior, las
instrucciones if no se encuentran en el bucle for. Si son
múltiples eventos, las instrucciones if solo se ejecutarán
para el último evento, en lugar de para todos los
eventos.

5.5.2 Procesando Cada Fotograma

La lógica básica y el orden de cada fotograma del juego


se sigue por:

• While not hecho:


o Para cada evento (pulsar tecla, click del ratón,

etc.):
▪ Usa una cadena de instrucciones if para

ejecutar el código que maneje cada


evento.
o Realiza los cálculos que determinan a dónde
se mueven los objetos, qué sucede cuando
colisionan, etc.
o Limpiar la pantalla
o Dibujar todo

El no mezclar estos pasos, hace que el programa sea más


fácil de leer y comprender. No escribas algunos cálculos,
algunos dibujos, y luego, otra vez, otros cálculos y
dibujos. Observa, también, cómo esto se parece a la
calculadora que hicimos en el primer capítulo.
Obtenemos una entrada del usuario, hacemos los
cálculos necesarios y producimos una respuesta. Aquí
aplicamos ese mismo patrón.

El código que dibujará la imagen sobre la pantalla reside


dentro del bucle while. Con el ciclo de reloj establecido
en 10, los contenidos de la ventana se dibujan 10 veces
por segundo. Si esto sucede más rápido, el ordenador se
ralentiza, ya que todo su tiempo lo dedica a actualizar la
pantalla. Si el código estuviera fuera de este bucle, la
pantalla no se dibujaría adecuadamente. Si esto
sucediera, se mostrarían inicialmente los gráficos, pero
éstos no volverían a aparecer, si por ejemplo la
minimizamos u otra venta es situada por encima.
5.6 Finalización del Programa
En estos momentos, si pinchamos el botón de “cierre”
de la ventana, mientras ejecutamos nuestro programa
Pygame dentro del IDLE, conseguiremos que se
bloquee. Esto es un inconveniente, ya que cerrar un
programa bloqueado requiere de muchos clicks para
conseguirlo.

El problema es que aunque hayamos salido del bucle, el


programa no le ha dicho al ordenador que cierre la
ventana. Llamando al siguiente comando, el programa
cerrará cualquier ventana abierta y saldrá de la manera
que esperamos.
Cierre correcto de un programa Pygame
pygame.quit()

5.7 Limpiando la Pantalla


El siguiente código limpia cualquier cosa que hubiera en
la ventana con un fondo blanco. Recuerda que ya
definimos anteriormente la variable BLANCO como una lista
de 3 valores RGB.
Limpiar Ventana
# Limpia la ventana y establece el color del fondo

pantalla.fill(BLANCO)
Vídeo: Representando Líneas

Esto debería hacerse previamente a emitir cualquier


comando de dibujo. Limpiar la ventana después de que
el programa dibuje los gráficos, consigue que el usuario
obtenga una pantalla en blanco.

Cuando se crea por primera vez una ventana, tiene un


fondo negro. Es importante, aún, limpiarla, ya que
podrían ocurrir una serie de cosas que impedirían que
se iniciase adecuadamente. Ningún programa debe
asumir que ya posee un lienzo limpio para poder
dibujar.

5.8 Actualizar la Pantalla


¡Muy importante! Debes actualizar la pantalla por
completo después de dibujar en ella. El ordenador no
muestra tus gráficos a medida que los vas dibujando.
Esto provocaría que se pusiera a parpadear. Espera a
mostrarlos hasta que el programa haya terminado de
dibujarlos. El siguiente comando actualiza por completo
los gráficos en pantalla.

No incluir este comando significa que el programa solo


mostrará una pantalla en blanco. Cualquier código de
dibujo posterior a él no aparecerá.
Actualizar por completo nuestra pantalla Pygame
# Avanza y actualiza la pantalla con lo que hemos dibujado.

pygame.display.flip()

5.9 Abre Una Ventana En Blanco


Ahora vamos a poner en un solo programa todo de lo
que hemos estado hablando. Este código puede usarse
como plantilla general para cualquier programa
Pygame. Abre una ventana en blanco y espera a que el
usuario presione el botón de cierre.

En la página web, si haces click sobre “Ejemplos”


puedes elegir “ejemplos gráficos” y encontrar éste
archivo como pygame_base_template.py.
pygame_base_template.py
"""
1
Mostramos como usar un sprite respaldado por un gráfico.
2
Sample Python/Pygame Programs
3 Simpson College Computer Science

4 http://programarcadegames.com/

http://simpson.edu/computer-science/
5
Vídeo explicativo: http://youtu.be/vRB_983kUMc
6 """

7 import pygame

# Definimos algunos colores


8
NEGRO = (0, 0 ,0)
9
BLANCO = (255, 255, 255)
10 VERDE = (0, 255, 0)
11 ROJO = (255, 0, 0)

AZUL = (0, 0, 255)


12
VIOLETA = (98, 0, 255)
13 pygame.init()

14 # Establecemos las dimensiones de la pantalla [largo,altura]

dimensiones = [700,500]
15
pantalla = pygame.display.set_mode(dimensiones)
16
pygame.display.set_caption("Mi Primer juego en Informática")
17 #El bucle se ejecuta hasta que el usuario hace click sobre el botón de cierre.

18 hecho = False

# Se usa para establecer cuan rápido se actualiza la pantalla


19
reloj = pygame.time.Clock()
20 # -------- Bucle principal del Programa -----------
21 while not hecho:

22 # --- Bucle principal de eventos

for evento in pygame.event.get():


23
if evento.type == pygame.QUIT:
24 hecho = True

25 # --- LA LÓGICA DEL JUEGO DEBERÍA IR AQUÍ

# --- EL CÓDIGO DE DIBUJO DEBERÍA IR AQUÍ


26
# Primero, limpia la pantalla con blanco. No vayas a poner otros comandos de dibujo
27
# de esto, de otra forma serán borrados por este comando:
28 pantalla.fill(BLANCO)

29 # --- Avanzamos y actualizamos la pantalla con lo que hemos dibujado.

pygame.display.flip()
30
# --- Limitamos a 60 fotogramas por segundo (frames per second)
31 reloj.tick(60)

32 # Cerramos la ventana y salimos.

# Si te olvidas de esta última línea, el programa se 'colgará'


33
# al salir si lo hemos estado ejecutando desde el IDLE.
34
pygame.quit()
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

5.10 Introducción al Dibujo


Esta es una lista de las cosas que puedes dibujar:
http://www.pygame.org/docs/ref/draw.html
Un programa puede dibujar cosas como rectángulos,
polígonos, círculos, elipses, arcos y líneas. También
incluiremos cómo mostrar texto en los gráficos. La
inclusión de imágenes de mapas de bits es tratada en el
Capítulo 12. Si decides echar un vistazo a la guía de
referencia de pygame, verás la definición de una
función como ésta:
pygame.draw.rect(Surface, color, Rect, width=0): return Rect

Una fuente frecuente de confusión es la parte de la línea


que dice width=0. Significa que si no provees el grosor, éste
por defecto será cero. Así, una llamada de función como:
pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5])

Será lo mismo que esta otra:


pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5], 0)
El : return Rect dice que tu función devuelve un rectángulo,
lo mismo que ya le hemos pasado. Puedes ignorar por
completo esta parte.

Lo que no funcionará es tratar de copiar la línea y


poner width=0 dentro.
# Esto falla y el tipo de error que te devuelve el ordenador es

# realmente difícil de entender

pygame.draw.rect(pantalla, ROJO, [55, 500, 10, 5], width=0)

5.11 Representar Líneas


El siguiente código muestra cómo dibujar una línea
sobre la pantalla. La dibujará de color verde, de 5 píxeles
de grosor, y desde las coordenadas (0,0) hasta (100,100).
Recuerda que VERDE es una variable que ya definimos
anteriormente como una lista de 3 valores RGB.
Representar una sola línea

1 # Dibuja en la pantalla una línea verde desde (0, 0) hasta (100, 100)

2 # y 5 píxeles de grosor.

pygame.draw.line(pantalla, VERDE, [0, 0], [100, 100], 5)


3

Usa la plantilla del ejemplo anterior y añade el código


para dibujar las líneas. Lee los comentarios para saber
exactamente dónde colocar el código. Intenta dibujar
líneas con grosores, colores y localizaciones diferentes.
Dibuja varias de ellas.

5.12 Representar líneas Mediante


Bucles y Desplazamientos
Los programas pueden repetir cosas una y otra vez. El
siguiente código dibuja, repetidamente, una línea
mediante el uso de un bucle. Los programas pueden
usar esta técnica para dibujar múltiples líneas, incluso
dibujar un vehículo entero.

Colocar un comando que dibuje una línea dentro de un


bucle, causaría que se dibujaran múltiples líneas en la
pantalla. Pero, hay un problema. Si cada línea tiene las
mismas coordenadas de origen y final, entonces, se
dibujarían una encima de la otra. Parecería como si solo
hubiéramos dibujado una sola.

Para evitarlo es necesario desplazar las coordenadas


cada vez que iteramos el bucle. La primera vez que la
variable desplazar_y pasa por el bucle, su valor es cero. La
línea se dibuja desde (0,10) hasta (100,110). La siguiente
vez, el bucle incrementa la variable desplazar_y en 10. Esto
causa que la siguiente línea se dibuje desde las
coordenadas (0,20) hasta (100,120). Esto continua una y
otra vez en el bucle, desplazando hacia abajo las
coordenadas de cada línea en 10 píxeles.
Representar un grupo de líneas

1
# Dibuja sobre la pantalla varias líneas desde (0, 10) hasta (100, 110)
2 # de 5 píxeles de grosor usando un bucle while
3 desplazar_y = 0

4 while desplazar_y < 100:

pygame.draw.line(pantalla, ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)


5
desplazar_y = desplazar_y + 10
6

Este mismo código se podría haber simplificado usando


un bucle for:
Representar un grupo de líneas

1 # Dibuja sobre la pantalla varias líneas desde (0, 10) hasta (100, 110)
2 # de 5 píxeles de grosor usando un bucle for

3 for desplazar_y in range(0, 100, 10):

pygame.draw.line(pantalla,ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)


4

Ejecuta este código, y prueba a usar diferentes valores


para el desplazamiento. Experimenta hasta que
entiendas cómo funciona.

Por ejemplo, este es un bucle que emplea el seno y


coseno para crear un conjunto de desplazamientos más
complejos, produciendo la imagen de la Figura 5.7.
Desplazamientos complejos

1
2
3 for i in range(200):

radianes_x = i / 20
4
radianes_y = i / 6
5
x = int( 75 * math.sin(radianes_x)) + 200
6 y = int( 75 * math.cos(radianes_y)) + 200

7 pygame.draw.line(pantalla, NEGRO, [x, y], [x + 5, y], 5)

8
9

Figure 5.7: Desplazamientos Complejos

Se pueden dibujar múltiples elementos dentro de un


bucle for, tal como el siguiente que dibuja múltiples X's.
Figura 5.8.
Desplazamientos complejos

1 for desplazamiento_x in range(30, 300, 30):

2 pygame.draw.line(pantalla, NEGRO, [desplazamiento_x, 100], [desplazamiento_x-10, 90]

pygame.draw.line(pantalla, NEGRO, [desplazamiento_x, 90], [desplazamiento_x-10, 100]


3

Figure 5.8: Múltiples X


5.13 Dibujar un Rectángulo
Vídeo: Cómo representar Rectángulos y Elipses

Cuando dibujamos un rectángulo, el ordenador necesita


las coordenadas de la esquina superior izquierda (el
origen), así como su altura y longitud.

La Figura 5.9 muestra un rectángulo (y una elipse, lo


explicaremos luego) con el origen en (20,20), una
longitud de 200 y una altura de 100. Cuando
especificamos un rectángulo, el ordenador necesita la
lista de estos cuatro números en el orden siguiente: x, y,
longitud, altura.

Figure 5.9: Representar una


Elipse

El ejemplo siguiente dibuja un rectángulo. Los dos


primeros números de la lista definen la esquina superior
izquierda en el punto (20, 20). Los dos números
siguientes indican una longitud de 250 y una altura de
100 píxeles, respectivamente.

El número 2 al final de la línea indica un grosor de 2


píxeles. Cuanto más grande es este número, más gruesa
será la línea alrededor de la figura. Si el número fuera 0,
no habría borde, sino que todo el rectángulo aparecería
relleno por el color especificado.
Representar un rectángulo

1 # Dibuja un rectángulo.

2 pygame.draw.rect(pantalla, NEGRO, [20, 20, 250, 100], 2)

5.14 Representar una Elipse


Una elipse es representada como si fuera un rectángulo.
Especificamos los bordes del rectángulo, y el ordenador
dibuja una elipse dentro de ellos.

El error más común al trabajar con elipses, es el pensar


que los puntos de partida especifican el centro de la
figura. En realidad, nada se dibuja en ese punto. Es la
región superior izquierda del rectángulo la que contiene
a la elipse.

Volviendo a la Figura 5.9, uno puede ver una elipse de


250 píxeles de largo por 100 píxeles de alto. La esquina
superior izquierda del rectángulo de 250 x 100 se
encuentra en las coordenadas (20, 20). Observa cómo,
realmente, nada ha sido dibujado en (20, 20). Si
dibujamos ambas figuras, una encima de la otra, es fácil
observar este detalle.
Representar una elipse

1 # Representa una elipse, usando un rectángulo como perímetro exterior

2 pygame.draw.ellipse(pantalla, NEGRO, [20, 20, 250, 100], 2)

5.15 Representar un Arco


Vídeo: Cómo Representar Arcos

¿Qué sucede si el programa solo necesita dibujar parte


de una elipse? Esto se consigue con el comando arc. Este
comando es similar al comando ellipse , solo que incluye
los puntos de partida y final para el arco a representar.
Los ángulos están en radianes.

Figure 5.10: Arcos

El siguiente código representa cuatro arcos que


muestran los cuatro cuadrantes de un círculo. Cada
cuadrante tiene un color distinto para facilitar la
visualización de cada sección de arco. El resultado de
aplicar este código lo podemos ver en la Figura 5.10.
Representar arcos

1
# Representa un arco como parte de una elipse. Usamos radianes para determinar qué
2 # ángulo dibujar.
3 pygame.draw.arc(pantalla, VERDE, [100, 100, 250, 200], pi/2, pi, 2)

4 pygame.draw.arc(pantalla, NEGRO, [100, 100, 250, 200], 0, pi/2, 2)

pygame.draw.arc(pantalla, ROJO, [100, 100, 250, 200], 3*pi/2, 2*pi, 2)


5
pygame.draw.arc(pantalla, AZUL, [100, 100, 250, 200], pi, 3*pi/2, 2)
6

5.16 Representar un Polígono


Vídeo: Cómo Representar Polígonos

La siguiente línea de código representa un polígono. La


forma del triángulo se define mediante tres puntos en
las posiciones (100, 100), (0, 200) y (200, 200). Es posible
hacer una lista con todos los puntos necesarios. Observa
cómo se construye cada punto. Cada uno es una lista
con dos números, y los tres puntos, a su vez, se anidan
en otra lista que los incluye a todos. Este código dibuja
lo que podemos observar en la Figura 5.11.
Figure 5.11: Polígono

Representar un polígono

1 # Esto dibuja un triángulo usando el comando polygon

2 pygame.draw.polygon(pantalla, NEGRO, [[100, 100],[0, 200],[200, 200]], 5)

5.17 Representar Texto


Vídeo: Cómo Representar Texto

Representar texto resulta un poco más complicado. Hay


tres cosas que tenemos que hacer en primer lugar.
Primero, crear un variable que contenga la información
sobre la fuente, tal como el tipo de letra y su tamaño.
Segundo, el programa crea una imagen del texto. Una
forma de verlo sería como si el programa tallara un
molde con las letras necesarias, listo para ser sumergido
en tinta y luego estampado sobre el papel.

Tercero, el programa dice dónde debe 'estamparse' esta


imagen sobre la pantalla.

Por ejemplo:
Representar texto sobre la pantalla

1
2
3 # Selecciona la fuente. Fuente Default, tamaño 25 pt.

fuente = pygame.font.Font(None, 25)


4
# Reproduce el texto. "True" significa texto suavizado(anti-aliased).
5 # El color es Negro. Recordemos que ya hemos definido anteriormente la variable NEG
6 # como una lista de (0, 0, 0)

7 # Observación: Esta línea crea una imagen de las letras,

# Pero aún no la pone sobre la pantalla.


8
texto = fuente.render("Mi texto", True, NEGRO)
9 # Coloca la imagen del texto sobre la pantalla en 250 x 250

10 pantalla.blit(texto, [250, 250])

11
12

¿Quieres mostrar en pantalla un número? Pues es un


poco más complicado. Lo siguiente no funciona:
texto = fuente.render("Número: ", numero, True, NEGRO)

¿Por qué? Un programa no puede añadir elementos


extra a font.render de la misma forma que lo hace print. Solo
podemos enviar una cadena de texto al comando, por lo
que el valor real de 'numero' necesita añadirse a la
cadena “Número: ”. Bueno, pero tampoco esto funciona:
texto = fuente.render("Número: " + numero, True, NEGRO)

Si 'numero' es una variable entera, el comando no sabe


cómo sumarle una cadena de texto. Eres tú, el
programador, quien debe convertir el 'numero' a
cadena. Entonces, sí podremos unir ambas cadenas de
esta manera:
texto = fuente.render("Número: " + str(numero), True, NEGRO)

Ahora ya sabes cómo imprimir el 'numero'. Si lo que


quieres es imprimir un temporizador, eso requiere
formateo de impresión, algo que veremos en un capítulo
posterior. Comprueba en el ejemplo de
códigopara timer.py:
ProgramArcadeGames.com/python_examples/f.php?fil
e=timer.py&lang=es
5.18 Programa Completo
Este es el código completo para el programa que hemos
tratado en este capítulo. Este programa, así como el
resto de ellos, puede ser descargado desde:
http://ProgramArcadeGames.com/index.php?chapter=
example_code&lang=es

Figure 5.12: Resultado del


programa ejemplo

simple_graphics_demo.py
"""
1
Sencilla demo gráfica
2
Sample Python/Pygame Programs
3 Simpson College Computer Science

4 http://programarcadegames.com/

http://simpson.edu/computer-science/
5
"""
6
# Importamos una biblioteca de funciones llamada 'pygame'
7 import pygame

8 # Inicializamos el motor de juegos

pygame.init()
9
# Definimos algunos colores
10 NEGRO = (0, 0, 0)

11 BLANCO = (255, 255, 255)

AZUL = (0, 0, 255)


12
VERDE = (0, 255, 0)
13
ROJO = (255, 0, 0)
14 PI = 3.141592653

15 # Establecemos el largo y alto de la pantalla

dimensiones = (400, 500)


16
pantalla = pygame.display.set_mode(dimensiones)
17 pygame.display.set_caption("Divertido Juego del Profesor Craven")
18 #Iteramos hasta que el usuario haga click sobre el botón de salida.

19 hecho = False

reloj = pygame.time.Clock()
20
# Iteramos en el bucle hasta que hecho == False
21 while not hecho:

22 for evento in pygame.event.get(): # El usuario realizó alguna acción

if evento.type == pygame.QUIT: # Si el usuario hizo click sobre salir


23
hecho = True # Marcamos que hemos acabado y abandonamos este bucle
24
# Todo el código de dibujo sucede después del bucle for y dentro del bucle
25 # while not hecho.
26 # Limpiamos la pantalla y establecemos su fondo.

pantalla.fill(BLANCO)
27
# Dibujamos sobre la pantalla una línea verde de 5 píxeles de ancho
28 # que vaya desde (0,0) hasta (100,100).

29 pygame.draw.line(pantalla, VERDE, [0, 0], [100, 100], 5)

# Usando un bucle for, dibujamos sobre la pantalla varias líneas rojas de 5 píxeles
30
# de ancho, que vayan desde (0,10) hasta (100,110).
31
for desplazar_y in range(0, 100, 10):
32 pygame.draw.line(pantalla, ROJO, [0, 10 + desplazar_y], [100, 110 + desplazar_y], 5)

33 # Dibujamos un rectángulo

pygame.draw.rect(pantalla, NEGRO, [20, 20, 250, 100], 2)


34
# Dibujamos una elipse, usando un rectángulo para fijar sus bordes exteriores
35 pygame.draw.ellipse(pantalla, NEGRO, [20, 20, 250, 100], 2)
36 # Dibujamos un arco como parte de una elipse.

37 # Usamos radianes para determinar qué ángulo tenemos que dibujar.

pygame.draw.arc(pantalla, NEGRO, [20, 220, 250, 200], 0, PI / 2, 2)


38
pygame.draw.arc(pantalla, VERDE, [20, 220, 250, 200], PI / 2, PI, 2)
39 pygame.draw.arc(pantalla, AZUL, [20, 220, 250, 200], PI, 3 * PI / 2, 2)

40 pygame.draw.arc(pantalla, ROJO, [20, 220, 250, 200], 3 * PI / 2, 2 * PI, 2)

# Aquí dibujamos un triángulo empleando el comando polygon.


41
pygame.draw.polygon(pantalla, NEGRO, [[100, 100], [0, 200], [200, 200]], 5)
42
# Elegimos que tipo de fuente usar; fuente por defecto, y de 25 puntos.
43 fuente = pygame.font.Font(None, 25)

44 # Creamos el texto. "True" significa texto con antialiasing.

# El color es negro. Esto nos crea una imagen de las letras, pero no las coloca
45
# sobre la pantalla.
46 texto = fuente.render("Mi texto", True, NEGRO)

47 # Coloca la imagen del texto en pantalla sobre las coordenadas 250x250.

pantalla.blit(texto, [250, 250])


48
# Avanzamos y actualizamos la pantalla con lo que hemos dibujado.
49
# Esto DEBE suceder después del resto de comandos de dibujo.
50 pygame.display.flip()
51 # Aquí limitamos el bucle while a un máximo de 60 veces por segundo.

#Lo dejamos aquí y usamos toda la CPU que podamos.


52
reloj.tick(60)
53 # Pórtate bien con el IDLE

54 pygame.quit()

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
5.19 Repaso
5.19.1 Test

Haz click para ir al Test.

5.19.2 Ejercicios

Haz click para ir a los Ejercicios.

5.19.3 Taller

Haz click para ir al Taller.


Chapter 6: Volvemos A Los Bucles
Los juegos requieren un montón de bucles complicados.
Este es un “capítulo desafiante ” en lo que es aprender
cómo ser un experto en bucles. Si consigues entender los
problemas planteados aquí, al final, podrás asegurar
que te has convertido en todo un experto en bucles.

Si tu objetivo no es convertirte en un experto en bucles,


al menos deberías intentar responder a las preguntas
planteadas en los ocho primeros problemas. Esto te
proporcionará el conocimiento necesario para poder
continuar con este libro. (Nadie ha conseguido una cita
por ser experto en bucles, excepto el protagonista de la
película Atrapado en el Tiempo)

Existen vídeos explicativos para las preguntas online, y


el código de las respuestas se “anima”. Solo presiona el
botón “inicio” para ver como funciona.

6.1 Imprimir Caracteres de Fin de


Línea
Al final de cada instrucción print, ésta imprime por
defecto un retorno de carro. Tal como se explicó en el
primer capítulo, este retorno de carro es un carácter que
traslada la siguiente línea, un renglón abajo. La mayoría
de las veces esto es lo que justamente queremos. Pero
otras no. Si queremos continuar imprimiendo en la
misma línea, podemos cambiar el carácter final impreso
por defecto. En este ejemplo aún no hemos cambiado ese
carácter final:
print("Pulpo")

print("Rosa")

...lo que imprimirá:

Pulpo
Rosa

Pero si lo que queríamos es que todo se imprimiera


sobre la misma línea, podemos conseguirlo usando una
nueva opción; end. Por ejemplo:
print("Pulpo", end="")

print("Rosa")

Esto imprimirá:

PulpoRosa

También podemos usar un espacio en lugar de que sea


una cadena vacía:
print("Pulpo", end=" ")

print("Rosa")

Y ahora:

Pulpo Rosa

Este es otro ejemplo, empleando una variable:


i =0

print(i, end=" ")

i =1

print(i, end=" ")

Esto imprimirá:

0 1

6.2 Problemas Avanzados de


Bucles
1. Escribe el código para imprimir diez asteriscos (*)
de la siguiente manera:
2. * * * * * * * * * *

Lo puedes hacer usando un bucle for, que imprima


individualmente cada asterisco, en lugar de
imprimir diez asteriscos con una sola
instrucción print. Esto se puede lograr con dos líneas
de código; un bucle for y una instrucción print.

Cuando lo hayas averiguado, o te hayas rendido,


aquí encontrarás la respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/problem_1_es.php

3. Escribe el código para imprimir esto:


4. * * * * * * * * * *
5. * * * * *
6. * * * * * * * * * * * * * * * * * * * *

Este es parecido al problema anterior, pero además,


imprime cinco y veinte estrellas. Copia y pega el
código anterior y ajusta el bucle for para lograrlo.

Cuando lo hayas averiguado, o te hayas rendido,


aquí encontrarás la respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/problem_2_es.php

7. Usa dos bucles for, uno anidado en el otro, para


imprimir el siguiente rectángulo de 10 x 10:
8. * * * * * * * * * *
9. * * * * * * * * * *
10. * * * * * * * * * *
11. * * * * * * * * * *
12. * * * * * * * * * *
13. * * * * * * * * * *
14. * * * * * * * * * *
15. * * * * * * * * * *

Para empezar, mira el Problema 1. El código en él


genera una línea de asteriscos. Necesitamos
repetirlo diez veces. Trabaja en este problema al
menos diez minutos antes de mirar la respuesta.

Cuando lo hayas averiguado, o te hayas rendido,


aquí encontrarás la respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/10x10box_es.php

16. Usa dos bucles for, uno de ellos anidado, para


imprimir el siguiente rectángulo de 5 x 10:
17. * * * * *
18. * * * * *
19. * * * * *
20. * * * * *
21. * * * * *
22. * * * * *
23. * * * * *
24. * * * * *
25. * * * * *
26. * * * * *
Este se parece muchísimo al problema anterior.
Experimenta con los rangos de los bucles hasta
encontrar exactamente los números que usará la
función range.

Cuando lo hayas averiguado, o te hayas rendido,


aquí encontrarás la respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/problem_4_es.php

27. Usa dos bucles for, uno de ellos anidado, para


imprimir el siguiente rectángulo de 20x5:
28. * * * * * * * * * * * * * * * * * * * *
29. * * * * * * * * * * * * * * * * * * * *
30. * * * * * * * * * * * * * * * * * * * *
31. * * * * * * * * * * * * * * * * * * * *
32. * * * * * * * * * * * * * * * * * * * *

Una vez más, parecido a los Problemas 3 y 4, pero


con diferentes valores para range.

Cuando lo hayas averiguado, o te hayas rendido,


aquí encontrarás la respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/problem_5_es.php

33. Escribe el código para conseguir:


34. 0 1 2 3 4 5 6 7 8 9
35. 0 1 2 3 4 5 6 7 8 9
36. 0 1 2 3 4 5 6 7 8 9
37. 0 1 2 3 4 5 6 7 8 9
38. 0 1 2 3 4 5 6 7 8 9
39. 0 1 2 3 4 5 6 7 8 9
40. 0 1 2 3 4 5 6 7 8 9
41. 0 1 2 3 4 5 6 7 8 9
42. 0 1 2 3 4 5 6 7 8 9
43. 0 1 2 3 4 5 6 7 8 9

Usa dos bucles anidados. Imprime la primera línea


con un bucle, y no con:
print("0 1 2 3 4 5 6 7 8 9")

Una pista: Primero, crea un bucle que imprima la


primera línea. Luego, envuélvelo en otro bucle que
repita esa línea 10 veces. Usa variables
índice i o j para aquello que imprime el programa.
Este ejemplo y el siguiente, ayudan a reforzar lo que
este tipo de variables hacen.

Trabaja en este problema al menos durante diez


minutos antes de ver la respuesta. El proceso de
pasarse diez minutos trabajando en la respuesta, es
de lejos, más importante que la propia respuesta..
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/number_square_answer_es.php
44. Ajusta el programa anterior para obtener:
45. 0 0 0 0 0 0 0 0 0 0
46. 1 1 1 1 1 1 1 1 1 1
47. 2 2 2 2 2 2 2 2 2 2
48. 3 3 3 3 3 3 3 3 3 3
49. 4 4 4 4 4 4 4 4 4 4
50. 5 5 5 5 5 5 5 5 5 5
51. 6 6 6 6 6 6 6 6 6 6
52. 7 7 7 7 7 7 7 7 7 7
53. 8 8 8 8 8 8 8 8 8 8
54. 9 9 9 9 9 9 9 9 9 9

Respuesta:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/problem_7_es.php

55. Escribe el código que imprimirá esto:


56. 0
57. 0 1
58. 0 1 2
59. 0 1 2 3
60. 0 1 2 3 4
61. 0 1 2 3 4 5
62. 0 1 2 3 4 5 6
63. 0 1 2 3 4 5 6 7
64. 0 1 2 3 4 5 6 7 8
65. 0 1 2 3 4 5 6 7 8 9
Una pista: Esto es como el Problema 6, pero el bucle
interno ya no itera un número fijo de veces. No
uses range(10), en cambio ajusta la dimensión del
rango.

Luego de haber trabajo al menos diez minutos en


este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/top_right_triangle_es.php

Asegúrate de que eres capaz de escribir el código


para el siguiente ejercicio, al igual que para los
anteriores. Sí, esta práctica es laboriosa, pero más
tarde se verá recompensada y ahorrarás mucho
tiempo.

66. Escribe el código para conseguir:


67. 0 1 2 3 4 5 6 7 8 9
68. 0 1 2 3 4 5 6 7 8
69. 0 1 2 3 4 5 6 7
70. 0 1 2 3 4 5 6
71. 0 1 2 3 4 5
72. 0 1 2 3 4
73. 0 1 2 3
74. 0 1 2
75. 0 1
76. 0
Este es difícil. Un pista: necesitamos dos bucles
dentro de otro bucle externo que controle cada fila.
Primero, un bucle imprime espacios, luego el otro
bucle imprime los números. Itera ambos para cada
línea. Para empezar, intenta escribir solamente un
bucle interno que imprima:

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6
0 1 2 3 4 5
0 1 2 3 4
0 1 2 3
0 1 2
0 1
0

Una vez que esté funcionando, añade un bucle


después del comienzo del bucle externo y antes del
ya existente bucle interno. Usa este nuevo bucle
para imprimir los espacios necesarios que
justifiquen por la derecha los otros bucles.

Luego de haber trabajo al menos diez minutos en


este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/bottom_left_triangle_es.php
77. Escribe el código para obtener (Conseguir el
alineamiento es difícil, al menos escribe los
números):
78. 1 2 3 4 5 6 7 8 9
79. 2 4 6 8 10 12 14 16 18
80. 3 6 9 12 15 18 21 24 27
81. 4 8 12 16 20 24 28 32 36
82. 5 10 15 20 25 30 35 40 45
83. 6 12 18 24 30 36 42 48 54
84. 7 14 21 28 35 42 49 56 63
85. 8 16 24 32 40 48 56 64 72
86. 9 18 27 36 45 54 63 72 81

Una pista: Empieza por ajustar el código del


Problema 1 para que imprima:

0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9
0 2 4 6 8 10 12 14 16 18
0 3 6 9 12 15 18 21 24 27
0 4 8 12 16 20 24 28 32 36
0 5 10 15 20 25 30 35 40 45
0 6 12 18 24 30 36 42 48 54
0 7 14 21 28 35 42 49 56 63
0 8 16 24 32 40 48 56 64 72
0 9 18 27 36 45 54 63 72 81

Luego ajusta el código para que haga esto:


1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

Finalmente, si el número ha imprimir es menor a


10, usa una instrucción if que imprima los espacios
.

Luego de haber trabajo al menos diez minutos en


este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/multiplication_table_es.php

87. Construye los siguiente:


88. 1
89. 1 2 1
90. 1 2 3 2 1
91. 1 2 3 4 3 2 1
92. 1 2 3 4 5 4 3 2 1
93. 1 2 3 4 5 6 5 4 3 2 1
94. 1 2 3 4 5 6 7 6 5 4 3 2 1
95. 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
96. 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1

Una pista: primero escribe el código que realice


esto:

1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9

Luego el que realice esto otro:

1
1 2 1
1 2 3 2 1
1 2 3 4 3 2 1
1 2 3 4 5 4 3 2 1
1 2 3 4 5 6 5 4 3 2 1
1 2 3 4 5 6 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1
Por último añade espacios para obtener el resultado
final.

Luego de haber trabajo al menos diez minutos en


este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/top_triangle_es.php

97. Construye:
98. 1
99. 1 2 1
100. 1 2 3 2 1
101. 1 2 3 4 3 2 1
102. 1 2 3 4 5 4 3 2 1
103. 1 2 3 4 5 6 5 4 3 2 1
104. 1 2 3 4 5 6 7 6 5 4 3 2 1
105. 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
106. 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1
107. 1 2 3 4 5 6 7 8
108. 1 2 3 4 5 6 7
109. 1 2 3 4 5 6
110. 1 2 3 4 5
111. 1 2 3 4
112. 1 2 3
113. 1 2
114. 1

Esto se puede lograr combinando los Problemas 11


y 9.
Luego de haber trabajo al menos diez minutos en
este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/three_quarters_es.php

115. Escribe el código para conseguir:


116. 1
117. 1 2 1
118. 1 2 3 2 1
119. 1 2 3 4 3 2 1
120. 1 2 3 4 5 4 3 2 1
121. 1 2 3 4 5 6 5 4 3 2 1
122. 1 2 3 4 5 6 7 6 5 4 3 2 1
123. 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
124. 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1
125. 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
126. 1 2 3 4 5 6 7 6 5 4 3 2 1
127. 1 2 3 4 5 6 5 4 3 2 1
128. 1 2 3 4 5 4 3 2 1
129. 1 2 3 4 3 2 1
130. 1 2 3 2 1
131. 1 2 1
132. 1
133.
134.

Luego de haber trabajo al menos diez minutos en


este problema, aquí tienes la solución:
ProgramArcadeGames.com/chapters/06_back_to_l
ooping/full_diamond_es.php

6.3 Repaso
6.3.1 Test

Haz click para ir al Test.

6.3.2 Ejercicios

Haz click para ir a los Ejercicios.

6.3.3 Taller

Haz click para ir al Taller.


Chapter 7: Introducción a las
Listas
7.1 Tipos de Datos
Vídeo: Introducción a las Listas

Hasta el momento, en este libro hemos mostrado cuatro


tipos de datos:

• Cadena (una cadena es una abreviatura para


“cadena de caracteres,” lo que la gente
habitualmente entiende por texto.)
• Entero
• Real (Coma flotante)
• Booleano

Python puede mostrar la clase a la que pertenece cada


valor mediante la función type.

No obstante, debemos admitir que esta función type no es


muy útil para el resto de lo que haremos en este libro,
pero es bueno que al menos por una vez, mostremos los
tipos de datos que hemos introducido hasta ahora.
Escribe lo siguiente en la shell del IDLE. (No vayas a
abrir una ventana nueva y escribirlo como un programa;
no funcionaría).
Salida:

>>> type(3)
<class 'int'>

type(3)
>>> type(3.145)
type(3.145)
<class 'float'>
type("¡Qué tal!")

type(True)
>>> type("Qué tal")
<class 'str'>

>>> type(True)
<class 'bool'>

También es posible usar la función type en una variable


para ver qué clase de dato hay en ella.
x =3

type(x)

Los dos nuevos tipos de datos que veremos en este


capítulo son las Listas y las Tuplas. Intenta ejecutar los
siguientes comandos sobre la shell de Python y observa
el resultado:
type((2, 3, 4, 5))

type([2, 3, 4, 5])
7.2 Trabajando Con Listas
Seguramente has creado listas de la compra, listas de lo
que queda por hacer y hasta quizás, listas de lo que
tienes que hacer antes de morir. ¿Pero cómo creas listas
en un ordenador?

Figure 7.1: Incluso los ordenadores usan listas

Intenta estos ejercicios usando la línea de comandos del


IDLE. Haz lo siguiente para crear una lista e imprimirla:
>>> x = [1,2]

>>> print(x)

[1, 2]

Para imprimir un elemento (item) individual de un


array:
>>> print(x[0])

Este número con la ubicación del item se llama el índice.


Observa que las posiciones en la lista empiezan por cero.
Por ello, una lista con 10 items, no tiene un item en la
ubicación [10]. Solo ubica desde [0] hasta [9]. Puede
parecer muy confuso crear un array con 10 items y que
no haya un elemento 10, pero la mayoría de lenguajes
de ordenador empiezan a contar desde el 0 en lugar de
hacerlo desde el 1.

Piensa en una lista como en una cubitera de hielo que


guarda números, como la que se ve en la Figura 7.2. Los
valores son guardados dentro de cada cubículo y lo que
está escrito arriba son los números que empezando por
cero identifican la ubicación de cada sitio.

Recuerda, tenemos que considerar dos conjuntos de


números cuando trabajamos con listas de números: la
posición y el valor. La posición, también conocida como
índice, se refiere a dónde se encuentra el valor. El valor
es el número guardado en esa ubicación. Cuando
trabajamos con un array, debemos tener claro si lo que
necesitamos es su ubicación o su valor.

Es fácil obtener el valor conociendo la ubicación, pero es


mucho más difícil obtener la ubicación conociendo el
valor. El Capítulo 16 lo dedicaremos a responder cómo
encontrar la ubicación para un determinado valor.

Figure 7.2: Las Lista son como las


cubiteras de hielo

Un programa puede asignar nuevos valores a items


individuales en una lista. En el caso siguiente, al
primero, ubicado en la posición cero (no uno), se le
asigna el número 22.
>>> x[0] = 22

>>> print(x)

[22, 2]

Un programa también puede crear una “tupla”. Este


tipo de datos trabaja como una lista, pero con dos
diferencias. Primero, la creamos con paréntesis en lugar
de corchetes. Segundo, no es posible cambiar el valor de
una tupla una vez creada:
>>> x = (1, 2)

>>> print(x)

(1, 2)
>>> print(x[0])

>>> x[0] = 22

Traceback (most recent call last):

File "<pyshell#18>", line 1, in <module>

x[0] = 22

TypeError: 'tuple' object does not support item assignment

>>>

Como se puede ver en el código anterior, no podemos


asignar un valor nuevo a un item de la tupla. ¿Pero por
qué necesitamos esta limitación? Primero, el ordenador
irá más rápido si sabe que un valor no cambiará.
Segundo, no queremos que ciertas listas cambien, como
por ejemplo una lista con los colores RGB para el rojo.
El color rojo no cambia, por lo tanto una tupla inmutable
es la mejor opción.

Un array es una lista de objetos. Es una estructura de


datos muy importante en informática. El tipo de dato
“lista” de Python es muy similar a una estructura de
datos del tipo array (colección).

7.3 Iterando En Una Lista


Vídeo: Iterando En Una Lista

Si un programa necesita iterar a través de cada item de


una lista, como por ejemplo, para imprimirlos, existen
dos tipos de bucles for que pueden hacerlo.
El primer método para iterar a través de cada item en un
bucle, es usando el bucle “para-cada”. Este tipo de bucle
toma una colección de items, iterando el código una vez
por item. Hará una copia del item y lo almacenará en una
variable para su procesamiento.

Tiene el siguiente formato:


for variable_item in nombre_lista:

Estos son algunos ejemplos:

Salida:

mi_lista = [101, 20, 10, 50, 60]


101

for item in mi_lista:


20

print(item) 10
50
60

Los Programas también pueden almacenar cadenas de


caracteres en listas:

Salida:
mi_lista = ["Cuchara", "Tenedor", "Cuchillo"]

for item in mi_lista: Cuchara

print(item) Tenedor
Cuchillo
Las listas también pueden contener otras listas. Lo
siguiente itera a través de cada item de la lista principal,
pero no en las sublistas.

Salida:
mi_lista = [[2, 3], [4, 3], [6, 7]]

for item in mi_lista: [2,3]

print(item) [4,3]
[6,7]

La otra forma de iterar a través de una lista es usar


una variable índice que acceda directamente a la lista, en
lugar de hacerlo a través de una copia de cada elemento.
Para usar una variable índice, el programa cuenta desde
0 hasta la longitud de la lista. Si hay diez elementos, el
bucle debe ir desde 0 hasta 9 para el total de los diez
elementos.

La longitud de una lista la podemos hallar usando la


función len. Combinándola con la función range, permite
que el bucle itere a través de la lista completa.

Salida:

mi_lista = [101, 20, 10, 50, 60]


101

for i in range(len(mi_lista)): 20

print(mi_lista[i]) 10
50
60
Este método es más complejo, pero también más
potente. Como estamos trabajando directamente con los
elementos de la lista, en lugar de hacerlo con una copia,
podemos modificarla. El bucle para-cada no permite
modificaciones de la lista original.

7.4 Añadir a una Lista


Podemos añadir nuevos items a una lista (pero no a una
tupla) usando el comando append. Por ejemplo:
mi_lista = [2, 4, 5, 6] Salida:
print(mi_lista)

mi_lista.append(9) [2, 4, 5, 6]

print(mi_lista) [2, 4, 5, 6, 9]

Vídeo: Añadir a una lista

Nota: Si el rendimiento se ve alterado por la función


“append”, es muy importante comprender cómo se
implementa una lista. Por ejemplo, si la lista se
implementa como una colección (array) de datos, añadir
un item a la lista se parece mucho a añadir otro huevo a
un cartón lleno. Por lo que un cartón nuevo debe ser
fabricado con trece depósitos. Los doce huevos son
retirados y, luego, añadimos el decimotercer huevo.
Finalmente, el viejo cartón es reciclado. Ya que esto
sucede entre bastidores de una función, los
programadores podrían olvidarlo y dejar que el
ordenador hiciera todo el trabajo. Sería mucho más
eficiente, decirle simplemente que fabricara un cartón
con los depósitos necesarios para comenzar. Por suerte,
Python no implementa las listas como colecciones de
datos. Pero, es importante prestar atención en el
próximo semestre, a la clase sobre estructura de datos y
aprender cómo funciona todo esto.

Para crear una lista desde cero, es necesario crear


primero una lista vacía y luego usar la función append.
El siguiente ejemplo crea una lista basada en las
entradas de teclado:

Salida:

Introducir un número entero: 4


[4]
Introducir un número entero: 5
[4, 5]
Introducir un número entero: 3
[4, 5, 3]
Introducir un número entero: 1
[4, 5, 3, 1]
Introducir un número entero: 8
[4, 5, 3, 1, 8]

Crear una lista de números desde la entrada de teclado

1 mi_lista = [] # Lista vacía


2 for i in range(5):

entrada_usuario = input("Introducir un número entero: ")


3
entrada_usuario = int(entrada_usuario)
4 mi_lista.append(entrada_usuario)

5 print(mi_lista)

Si un programa necesitara crear un array de una


longitud específica, con todos sus elementos del mismo
valor, un sencillo truco sería usar el siguiente código:
Crear un array con 100 ceros

1 # Crear un array con 100 ceros.

2 mi_lista = [0] * 100

7.5 Sumar o Modificar una Lista


Vídeo: Sumar una Lista

Crear un total acumulado de un array es una operación


rutinaria. Así es como se haría:
Suma acumulada de los valores de una lista v1
# Copia de un array a una suma
1
mi_lista = [5, 76, 8, 5, 3, 3, 56, 5, 23]
2 # La suma inicial debería ser cero
3 total_lista = 0

4 # Itera desde 0 hasta el total del número de elementos

# en el array:
5
for i in range(len(mi_lista)):
6 # Añade el elemento 0, después el 1, luego el 2, etc.
7 total_lista += mi_lista[i]

# Imprime el resultado
8
print(total_lista)
9
10
11
12
13
14

En lugar de contar a través de un rango, podemos


conseguir lo mismo usando un bucle for que itere a
través del array:
Suma acumulada de los valores de una lista v2

1
2
# Copia de un array a una suma
3
mi_lista = [5, 76, 8, 5, 3, 3, 56, 5, 23]
4 # La suma inicial debería ser cero

5 total_lista = 0

# Itera a través del array, copiando cada item del mismo en una
6
# variable llamada item.
7 for item in mi_lista:

8 # Añade cada item

total_lista += item
9
# Imprime el resultado
10
print(total_lista)
11
12
13
14

Los números en un array también pueden cambiarse


usando un bucle for:
Duplicando todos los números en una lista

1
2
# Copia del array a modificar
3
mi_lista = [5, 76, 8, 5, 3, 3, 56, 5, 23]
4
# Itera desde 0 hasta el total del número de elementos
5 # en el array:

6 for i in range(len(mi_lista)):

# Modifica el elemento duplicándolo


7
mi_lista[i] = mi_lista[i] * 2
8
# Imprime el resultado
9 print(mi_lista)

10
11

Sin embargo, la versión siguiente no funciona a la hora


de duplicar los valores del array. ¿Por qué? Debido a
que item es una copia de un elemento del array. El código
siguiente duplica la copia, pero no el elemento original
del array.
Código erróneo. No duplica los números de una lista
1
2
# Copia del array a modificar
3
mi_lista = [5, 76, 8, 5, 3, 3, 56, 5, 23]
4
# Itera a través de cada elemento en mi_lista
5 for item in mi_lista:

6 # Esto duplica el item, pero no modifica el array,

# debido a que item es una copia de un único elemento.


7
item = item * 2
8
# Imprime el resultado
9 print(mi_lista)

10
11

7.6 Dividir Cadenas de Texto


Vídeo: Dividir Cadenas de Texto

Las cadenas de texto son en realidad listas de caracteres.


Pueden tratarse como listas donde cada letra es un item
particular. Ejecuta el código siguiente con las dos
versiones de x:
Acceder a una cadena de texto como una lista
x = "Esta es una cadena de ejemplo"
1
#x = "0123456789"
2 print("x=", x)

3 # Acceso a un carácter individual

print("x[0] = ", x[0])


4
print("x[1] = ", x[1])
5
# Acceso desde la derecha
6 print("x[-1] = ", x[-1])

# Acceso 0 - 5
7
print("x[:6] = ", x[:6])
8 # Acceso 6

9 print("x[6:] = ", x[6:])

# Acceso 6 - 8
10
print("x[6:9] = ", x[6:9])
11
12
13
14
15
16
17
18

En Python podemos aplicar ciertos operadores


matemáticos a las cadenas de texto. Intenta reproducir
el siguiente código:
Añadiendo y multiplicando cadenas de texto

1 a = "Hola"

2 b = "Qué tal"

c = "!"
3
print(a + b)
4
print(a + b + c)
5 print(3 * a)

6 print(a * 3)

print((a * 2) + (b * 2))
7
8

Ya hemos visto que es posible conocer la longitud de


una cadena. También es posible saberlo con cualquier
otro tipo de array.
Obtener la longitud de una cadena o de una lista

1
a = "Hola, qué tal"
2
print(len(a))
3
b = [3, 4, 5, 6, 76, 4, 3, 3]
4 print(len(b))

Como una cadena de texto es un array, un programa


también podría iterar a través de cada uno de sus
elementos:
for caracter in "Esto es una prueba":

print (caracter)

Ejercicio: Empieza con el siguiente código:


meses = "EneFebMarAbrMayJunJulAgoSepOctNovDic"

n = int(input("Introduce un número de mes: "))

Imprime la abreviatura (de tres letras) para el número


de mes que introduzca el usuario. (Calcula la posición
inicial en la cadena, luego, usa la información que
hemos aprendido para imprimir la subcadena correcta)

7.7 Códigos Secretos


Este código imprime individualmente cada letra de una
cadena de texto:
texto_llano = "Esto es una prueba. ABC abc"

for c in texto_llano:

print (c, end=" ")

Vídeo: Códigos Secretos

Los ordenadores realmente no almacenan letras de una


cadena en su memoria; almacenan series de números.
Cada número representa una letra. El sistema que usan
para traducir números a letras se llama Unicode. El
nombre completo es Universal Character Set
Transformation Format, abreviado habitualmente
como UTF-8.

La tabla Unicode cubre el alfabeto Occidental usando


los números 0-127. Cada letra es representada por un
byte de memoria. Otros alfabetos, como el Cirílico,
pueden emplear múltiples bytes para representar cada
letra. Una copia parcial de la tabla Unicode se puede ver
debajo:
ValorCarácterValorCarácterValorCarácterValorCarácter

40 ( 61 = 82 R 103 g

41 ) 62 > 83 S 104 h

42 * 63 ? 84 T 105 i

43 + 64 @ 85 U 106 j

44 , 65 A 86 V 107 k

45 - 66 B 87 W 108 l

46 . 67 C 88 X 109 m

47 / 68 D 89 Y 110 n

48 0 69 E 90 Z 111 o

49 1 70 F 91 [ 112 p

50 2 71 G 92 \ 113 q

51 3 72 H 93 ] 114 r

52 4 73 I 94 ^ 115 s

53 5 74 J 95 _ 116 t

54 6 75 K 96 ` 117 u

55 7 76 L 97 a 118 v

56 8 77 M 98 b 119 w

57 9 78 N 99 c 120 x

58 : 79 O 100 d 121 y

59 ; 80 P 101 e 122 z

60 < 81 Q 102 f
Para una mayor información sobre ASCII (el cual posee
los mismos valores que Unicode para el alfabeto
Occidental) ir a:
http://es.wikipedia.org/wiki/ASCII

Vídeo que explica la elegancia del Unicode:


http://hackaday.com/2013/09/27/utf-8-the-most-
elegant-hack

El siguiente conjunto de líneas convierte cada letra del


ejemplo anterior en su valor ordinal usando UTF-8:
texto_llano = "Esto es una prueba. ABC abc"

for c in texto_llano:

print (ord(c), end=" ")

El siguiente programa toma cada valor UTF-8 y le añade


un uno. Luego, imprime el nuevo valor UTF-8, y por
último, lo vuelve a convertir a una letra
texto_llano = "Esto es una prueba. ABC abc"

for c in texto_llano:

x = ord(c)

x =x +1

c2 = chr(x)

print (c2, end="")

El siguiente programa toma cada valor UTF-8 y le añade


un uno. Luego, lo vuelve a convertir a una letra.
simple_encryption.py

1
"""
2 # Sample Python/Pygame Programs
3 # Simpson College Computer Science

# http://programarcadegames.com/
4
# http://simpson.edu/computer-science/
5
# Vídeo explicativo: http://youtu.be/sxFIxD8Gd3A
6 """

7 texto_plano = "Esto es una prueba. ABC abc"

texto_cifrado = ""
8
for c in texto_plano:
9
x = ord(c)
10 x =x +1

11 c2 = chr(x)

texto_cifrado = texto_cifrado + c2
12
print(texto_cifrado)
13
14
15
16
17
18

Finalmente, el último código, toma cada valor UTF-8 y


le sustrae un uno, luego lo convierte en una letra.
Usando la salida del programa anterior como entrada de
éste, podemos emplearlo como descifrador del texto
cifrado en el anterior ejemplo.

simple_decryption.py
"""
1
# Sample Python/Pygame Programs
2 # Simpson College Computer Science
3 # http://programarcadegames.com/

# http://simpson.edu/computer-science/
4
# Vídeo explicativo: http://youtu.be/sxFIxD8Gd3A
5 """

6 texto_cifrado = "Ftup!ft!vob!qsvfcb/!BCD!bcd"

texto_plano= ""
7
for c in texto_cifrado:
8
x = ord(c)
9 x =x -1

10 c2 = chr(x)

texto_plano = texto_plano + c2
11
print(texto_plano)
12
13
14
15
16
17
18

7.8 Arrays Asociativos


Python no se limita a usar números como índices de
arrays. También es posible usar un array asociativo. Un
array asociativo funciona de la siguiente manera:
# Crea un array asociativo vacío

# (Observar las llaves.)

x = {}

# Añadirle algo
x["fred"] = 2

x["scooby"] = 8

x["wilma"] = 1

# Obtener e imprimir un item

print(x["fred"])

Realmente no necesitas arrays asociativos para este


capítulo, pero creo que es importante señalar que es
posible usarlos.

7.9 Repaso
7.9.1 Test

Haz click para ir al Test.

7.9.2 Ejercicios

Haz click para ir a los Ejercicios.

7.9.3 Taller

Haz click para ir al Taller.


Chapter 8: Introducción a la
Animación
8.1 El Rectángulo Saltarín
Vídeo: Rectángulo Saltarín

Para comenzar con nuestra primera animación,


tomaremos la plantilla básica de programa Pygame del
Capítulo 5, la cual abre una ventana en blanco. Esta
plantillapygame_base_template.py la podemos encontrar en:
ProgramArcadeGames.com/python_examples/f.php?fil
e=pygame_base_template.py

Juntos construiremos un programa que rebote un


rectángulo blanco alrededor de una ventana con fondo
negro. Eres libre de escoger cualquier otro color, eso sí,
asegúrate que el color del fondo sea distinto al del
rectángulo!
El primer paso es comenzar con la plantilla básica y
cambiarle el color de fondo; de blanco a negro. Esa parte
del código se encuentra alrededor de la línea 46.
pantalla.fill(NEGRO)

El paso siguiente es dibujar el rectángulo que


pretendemos animar. Un rectángulo sencillo será
suficiente. El código debería colocarse después de haber
limpiado la pantalla, y antes de actualizarla.
pygame.draw.rect(pantalla, BLANCO, [50, 50, 50, 50])

En cada iteración del bucle, el rectángulo se dibujará en


una ubicación (x, y) con valores (50, 50). Esto es
controlado por los dos primeros números 50 de la lista.
Hasta que estos números no cambien, el cuadrado no se
moverá.

El rectángulo tendrá 50 píxeles de largo por 50 píxeles


de alto. Las dimensiones son controladas por los dos
últimos números de la lista. Podemos también llamar
cuadrado a nuestro rectángulo, ya que tiene las mismas
dimensiones de largo que de alto. Pero yo seguiré
llamándolo rectángulo, porque todos los cuadrados son
rectángulos, y dependiendo del monitor y la resolución
utilizada, los píxeles no siempre son cuadrados. Busca
el término Relación de Aspecto de Píxel si realmente estás
interesado en el tema.

¿Cómo hacemos para ir cambiando nuestra ubicación en


lugar de quedarnos detenidos en (50, 50)? ¡Usando una
variable, por supuesto! El siguiente código es un paso
hacia ese objetivo:
rect_x = 50
pygame.draw.rect(pantalla, BLANCO, [rect_x, 50, 50, 50])

Para mover el rectángulo hacia la derecha, podemos


incrementar x por uno en cada fotograma. El siguiente
código se aproxima a ello, pero no lo logra del todo:
rect_x = 50

pygame.draw.rect(pantalla, BLANCO, [rect_x, 50, 50, 50])

rect_x += 1

El problema con el código anterior es que rect_x se


reinicia a 50 en cada iteración del bucle. Para
solucionarlo, movemos la inicialización de rect_x fuera
del bucle. La próxima parte del código desplazará
correctamente el rectángulo hacia la derecha.
Animación de un rectángulo que se desplaza a la derecha
# Posición x de partida del rectángulo

# Observa que se encuentra fuera del bucle while principal.

rect_x = 50

# -------- Bucle Principal del Programa -----------

while not hecho:

for evento in pygame.event.get(): # El usuario hizo algo

if evento.type == pygame.QUIT: # Si el usuario hace click sobre cerrar

hecho = True # Avisa de que hemos acabado, por lo que salimos de este bucle

# Establece el fondo de pantalla

pantalla.fill(NEGRO)

pygame.draw.rect(pantalla, BLANCO, [rect_x, 50, 50, 50])

rect_x += 1
Para mover más rápido a la caja, en lugar de
incrementar rect_x por 1, hagámoslo por 5:
rect_x += 5

Podemos expandir este código para que tanto x como y


se incrementen, y de esta forma conseguimos que el
cuadrado se mueva tanto hacia abajo como hacia la
derecha:
Animación de un rectángulo que se desplaza hacia abajo y hacia la derecha
# Posición de partida del rectángulo

rect_x = 50

rect_y = 50

# -------- Bucle Principal del Programa -----------

while not hecho:

for evento in pygame.event.get():

if evento.type == pygame.QUIT:

hecho = True

# Establece el fondo de pantalla

pantalla.fill(NEGRO)

# Dibuja el rectángulo

pygame.draw.rect(pantalla, BLANCO, [rect_x, rect_y, 50, 50])

# Desplaza el punto de partida del rectángulo

rect_x += 5

rect_y += 5

El rumbo y la velocidad de las cajas se pueden


almacenar en un vector. Esto facilita que ambos
parámetros se puedan modificar fácilmente. El siguiente
trozo de código nos muestra el uso de variables para
almacenar el cambio en x e y (5, 5).
Uso de variables para almacenar la velocidad de un objeto
# Posición de partida del rectángulo

rect_x = 50

rect_y = 50

# Velocidad y rumbo del rectángulo

rect_cambio_x = 5

rect_cambio_y = 5

# -------- Bucle Principal del Programa-----------

while not hecho:

for evento in pygame.event.get(): # El usuario hizo algo

if evento.type == pygame.QUIT: # Si el usuario hace click sobre cerrar

hecho = True # Avisa de que hemos acabado, por lo que salimos de este bucle

# Establece el fondo de pantalla

pantalla.fill(NEGRO)

# Dibuja el rectángulo

pygame.draw.rect(pantalla, BLANCO, [rect_x, rect_y, 50, 50])

# Desplaza el punto de partida del rectángulo

rect_x += rect_cambio_x

rect_y += rect_cambio_y

Cuando la caja llega al borde de la pantalla, continúa su


camino sin que nada la detenga. Nada consigue que
rebote al llegar a él. Para invertir el rumbo y que el
rectángulo viaje hacia la derecha, necesitamos
que rect_cambio_y cambie de 5 a -5 una vez que la figura llega
al borde inferior de la pantalla. Sabemos que el
rectángulo se encuentra en el fondo, cuando el valor
de rect_y es mayor que la altura de la pantalla. El
siguiente código se encarga de comprobarlo e invertir el
rumbo:
# Rebota el rectángulo si es necesario

if rect_y > 450:

rect_cambio_y = rect_cambio_y * -1

Figure 8.1: Ubicación del


rectángulo basada en la coordenada y

¿Por qué comprobar el valor de rect_y frente a 450? Si la


pantalla tiene 500 píxeles de alto, comprobar su valor
frente a 500 sería una opción lógica a primera vista. Pero
recordemos que el rectángulo empieza a dibujarse
desde la esquina superior izquierda del mismo. Si lo
dibujáramos con inicio en 500, se dibujaría desde 500
hasta 550, saliéndose por completo de la pantalla antes
de rebotar. Ver la Figura 8.1.

Si tenemos en cuenta que el rectángulo tiene 50 píxeles


de alto, la ubicación correcta para que rebote es:
.
El siguiente código rebotará al rectángulo por las cuatro
paredes de una ventana de 700 x 400:
Botando un rectángulo
# Rebota al rectángulo si es necesario

if rect_y > 450 or rect_y < 0:

rect_cambio_y = rect_cambio_y * -1

if rect_x > 650 or rect_x < 0:

rect_cambio_x = rect_cambio_x * -1

Si te interesa algo más que solo un rectángulo, tienes


varios comandos de dibujo que pueden aprovechar las
variables rect_x y rect_y. El siguiente código dibuja un
rectángulo rojo dentro del rectángulo blanco. El
rectángulo rojo está desplazado 10 píxeles en las
direcciones x,y, desde la esquina superior izquierda del
rectángulo blanco. También es, 20 píxeles más pequeño
en ambas direcciones. Esto produce un borde blanco de
10 píxeles de grosor alrededor del rectángulo rojo.
Observa la Figura 8.2.
Figure 8.2: Rectángulo blanco con cuadrado rojo en el centro
# Dibuja un rectángulo rojo dentro del blanco

pygame.draw.rect(pantalla, BLANCO, [rect_x, rect_y, 50, 50])

pygame.draw.rect(pantalla, ROJO, [rect_x + 10, rect_y + 10 , 30, 30])

8.2 Nevando
¿No tienes suficiente con animar un elemento? ¿Quieres
animar más objetos? ¿Qué tal animar cientos de ellos a
la vez? Aquí te mostramos como usar las técnicas de la
sección 8.1 para animar copos de nieve que caen.
8.2.1 Explicación del Código
Vídeo: Nevando

Para empezar, abre la plantilla que crea una ventana en


blanco. Una vez más, el código pygame_base_template.py lo
puedes encontrar aquí:
ProgramArcadeGames.com/python_examples/show_fi
le.php?file=pygame_base_template.py

Es posible crear ubicaciones x,y para cosas como


estrellas, nieve o lluvia usando números aleatorios. La
forma más simple de hacerlo es utilizando un
bucle for para dibujar círculos en posiciones aleatorias
x,y. Intenta el siguiente código dentro del bucle while.
for i in range(50):

x = random.randrange(0, 400)

y = random.randrange(0, 400)

pygame.draw.circle(pantalla, BLANCO, [x, y], 2)

Pruébalo. El programa tiene un problema singular.


Veinte veces por segundo, cada vez que itera a través
del bucle, dibuja la nieve en lugares aleatorios. Intenta
ajustar el contador de copos de nieve para que veas
como eso cambia la imagen.

Obviamente, queremos ubicar aleatoriamente los copos


de nieve y mantenerlos en el mismo sitio. No queremos
generar nuevas ubicaciones, veinte veces por segundo.
Necesitamos mantener en una lista, el lugar donde se
encuentran. Podemos usar una lista Python para
conseguirlo. Lo siguiente debería ir antes del bucle
principal. En caso contrario, el programa añadirá otros
50 copos de nieve a la lista cada 1/20 segundos.
for i in range(50):

x = random.randrange(0, 400)

y = random.randrange(0, 400)

lista_nieve.append([x, y])

Una vez que hemos añadido las ubicaciones de los


copos de nieve, podemos acceder a ellos como en una
lista normal. El código siguiente imprimirá las
coordenadas x, y de la primera ubicación guardadas en
la posición cero:
print(lista_nieve[0])

¿Pero qué sucede si solo queremos una de las dos


coordenadas? Pues tenemos listas dentro de listas. La
lista principal contiene todas las coordenadas. Dentro
de ella, cada coordenada es a su vez, una lista de una x
(posición 0) y de una y (posición 1):
[[34, 10],

[10, 50],

[20, 18]]
Para imprimir la coordenada y en la posición 0,
seleccionamos primero la coordenada 0, y luego, el valor
de y en la posición 1. El código es el siguiente:
print(lista_nieve[0][1])

Para imprimir el valor de x correspondiente a la


coordenada 21 (posición 20), primero seleccionamos la
coordenada 20 y después el valor de x en la posición
cero:
print(lista_nieve[20][0])

En el interior del bucle principal while, podemos usar un


bucle for que dibuje cada elemento de la lista nieve.
Recuerda, len(lista_nieve) devolverá el número de
elementos en la lista de copos de nieve.
# Procesa cada copo de nieve en la lista

for i in range(len(lista_nieve)):

# Dibuja el copo de nieve

pygame.draw.circle(pantalla, BLANCO, lista_nieve[i], 2)

Recuerda que hay dos tipos de bucles for. Podríamos


usar el otro tipo, lo que se vería así:
# Procesa UNA COPIA de la ubicación de cada copo en la lista

for xy_coord in lista_nieve:

# Dibuja el copo de nieve

pygame.draw.circle(pantalla, BLANCO, xy_coord, 2)


Sin embargo, como lo que planeamos es modificar las
ubicaciones de los copos de nieve, no podemos usar éste
ultimo tipo de bucle for, ya que estaríamos modificando
la ubicación de una copia, en lugar de hacerlo con las
ubicaciones reales de los copos.

Si el programa lo que quiere es que todos los objetos del


array se muevan hacia abajo, tal como lo hace la nieve,
expandir el bucle for creado arriba, provocará que
aumente el valor de la coordenada y:
# Procesa cada copo en la lista

for i in range(len(lista_nieve)):

# Dibuja el copo de nieve

pygame.draw.circle(pantalla, BLANCO, lista_nieve[i], 2)

# Mueve el copo un píxel hacia abajo

lista_nieve[i][1] += 1

Esto hace que la nieve caiga, pero una vez que llega al
final de la pantalla, nada nuevo aparece. Añadiendo el
código siguiente, volveremos a colocar nieve
aleatoriamente en la parte superior de la pantalla:
# Si el copo ha salido del fondo de la pantalla

if lista_nieve[i][1] > 400:

# Lo vuelve a situar nuevamente en la parte superior

y = random.randrange(-50, -10)

lista_nieve[i][1] = y

# Le damos una nueva posición x


x = random.randrange(0, 400)

lista_nieve[i][0] = x

También es posible añadir otras cosas a la lista que


tengan tamaños, formas, colores, velocidades y rumbos
distintos. Sin embargo, esto se complica debido a los
múltiples tipos de datos que necesitan ser mantenidos
en la lista. De momento, lo mantendremos así de simple,
pero una vez que aprendamos en el Capítulo 13 sobre
“clases”, será mucho más fácil manejar los diferentes
atributos de muchos objetos.

8.2.2 Listado Completo del Programa


animating_snow.py
”'
1
Animating multiple objects using a list.
2 Sample Python/Pygame Programs

3 Simpson College Computer Science

http://programarcadegames.com/
4
http://simpson.edu/computer-science/
5 Vídeo explicativo: http://youtu.be/Gkhz3FuhGoI
6 ”'

7 # Importamos las bibliotecas llamadas 'pygame' y 'random'.

import pygame
8
import random
9 # Inicializamos el motor de juegos.

10 pygame.init()

NEGRO = [0, 0, 0]
11
BLANCO = [255, 255, 255]
12
# Establecemos el largo y ancho de la pantalla.
13 dimensiones = [400, 400]

pantalla = pygame.display.set_mode(dimensiones)
14
pygame.display.set_caption("Está Nevando")
15 # Creamos un array vacío

16 lista_nieve = []

# Iteramos 50 veces y añadimos un copo de nieve en una ubicación (x,y) aleatoria.


17
for i in range(50):
18
x = random.randrange(0, 400)
19 y = random.randrange(0, 400)

20 lista_nieve.append([x, y])

reloj = pygame.time.Clock()
21
# Iteramos hasta que el usuario haga click sobre le botón de salida.
22 hecho = False
23 while not hecho:

24 for evento in pygame.event.get(): # El usuario realizó alguna acción.

if evento.type == pygame.QUIT: # Si el usuario hizo click sobre salir.


25
hecho = True # Marcamos que hemos acabado y abandonamos este bucle.
26 # Establecemos el color de fondo.

27 pantalla.fill(NEGRO)

# Procesamos cada copo de la lista.


28
for i in range(len(lista_nieve)):
29
# Dibujamos el copo de nieve
30 pygame.draw.circle(pantalla, BLANCO, lista_nieve[i], 2)

31 # Desplazamos un píxel hacia abajo el copo de nieve.

lista_nieve[i][1] += 1
32
# Si el copo se escapa del fondo de la pantalla.
33 if lista_nieve[i][1] > 400:

34 # Lo movemos justo encima del todo

y = random.randrange(-50, -10)
35
lista_nieve[i][1] = y
36
# Le damos una nueva ubicación x
37 x = random.randrange(0, 400)
38 lista_nieve[i][0] = x

# Avanzamos y actualizamos con lo que hemos dibujado.


39
pygame.display.flip()
40 reloj.tick(60)

41 # Pórtate bien con el IDLE. Si nos olvidamos de esta línea, el programa se 'colgará

# en la salida.
42
pygame.quit ()
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

En este ejemplo hemos mostrado a cada copo de nieve


moviéndose en la misma dirección. ¿Y si lo que
necesitáramos fuera que cada objeto se moviera por
separado? Cada uno su propio rumbo. Si es eso lo que
necesitas, mírate el Capítulo 13 donde tratamos el uso
de clases. En el Taller 8 te guiamos para conseguir
cientos de objetos animados diferentes, cada uno
siguiendo su propio rumbo.

8.3 Animación 3D
Vídeo: Demo del Motor de Juegos Blender

Extenderse desde un entorno 2D a otro completamente


3D, usando física de juegos, no es tan difícil como parece
a primera vista. Aunque ello se encuentre fuera del
ámbito de esta clase, vendría bien echarle un vistazo a
la forma de conseguirlo.

Existe un programa 3D gratuito llamado Blender, el cual


posee un “motor de juegos” que permite a los
programadores la creación de juegos 3D. Los objetos 3D
en el juego pueden llevar junto a ellos, código Python
para controlar sus acciones.

Figure 8.3: Ejemplo de Archivo Blender


Observa la Figura 8.3. Nos muestra una bandeja verde
con varios objetos en su interior. El objeto azul está
controlado por un script de Python que lo mueve por la
bandeja, haciendo que rebote contra los otros objetos. El
guión tiene muchos de los rasgos que tienen otros
programas 2D. Hay un bucle principal, una lista de
coordenadas x, y, así como variables que controlan el
vector.

El bucle principal del programa es controlado por


Blender. El código Python mostrado, es llamado por
Blender en cada “fotograma” que el juego reproduce. Es
por esto que no vemos en el código de Python un bucle
principal. De todas formas existe.

El objeto azul posee una ubicación en formato x, y, z.


Podemos acceder a él y modificarlo usando la
variable blueobject.position. La posición 0 del array contiene
la ubicación de x, la posición 1 la de y, y la posición 2 la
de z.

En lugar de las variables change_x y change_y empleadas en los


ejemplos 2D, en el caso del ejemplo con Blender se usa
el array asociativo de ubicaciones:
blue_object["x_change"]
blue_object["y_change"]
La instrucción if comprueba si el objeto azul ha
alcanzado los bordes de la pantalla y que el rumbo debe
revertirse. En lugar de emplear píxeles como en los
juegos 2D, las ubicaciones de los objetos pueden ser
números del tipo real (float point). Ubicar un elemento
entre 5 y 6, por ejemplo en 5.5, es totalmente legal.
Ejemplo de programa Blender Python
# Importamos El Motor de Juegos Blender
1
import bge
2
# Obtenemos una referencia para el objeto azul
3 cont = bge.logic.getCurrentController()

4 blue_object = cont.owner

# Imprimimos las coordenadas x,y donde se encuentra el objeto azul


5
print(blue_object.position[0], blue_object.position[1] )
6 # Cambiamos las coordenadas x,y en función de x_change e

7 # y_change. x_change e y_change son propiedades del juego

# asociadas al objeto azul.


8
blue_object.position[0] += blue_object["x_change"]
9
blue_object.position[1] += blue_object["y_change"]
10 # Comprobar si el objeto ha alcanzado un lado.

11 # Si es así, revertir su rumbo. Hacerlo para los cuatro lados.

if blue_object.position[0] > 6 and blue_object["x_change"] > 0:


12
blue_object["x_change"] *= -1
13 if blue_object.position[0] < -6 and blue_object["x_change"] < 0:
14 blue_object["x_change"] *= -1

15 if blue_object.position[1] > 6 and blue_object["y_change"] > 0:

blue_object["y_change"] *= -1
16
if blue_object.position[1] < -6 and blue_object["y_change"] < 0:
17 blue_object["y_change"] *= -1
18
19
20
21
22
23
24
25
26

Podemos descargar Blender desde:


http://www.blender.org/

Un ejemplo completo con Blender está disponible en:


ProgramArcadeGames.com/chapters/08_intro_to_animation/simple_block_move.blend.

8.4 Repaso
8.4.1 Test

Haz click para ir al Test.

8.4.2 Ejercicios

Haz click para ir a los Ejercicios.

8.4.3 Taller

Haz click para ir al Taller.


Chapter 9: Funciones
9.1 Introducción a Funciones
Vídeo: Por qué Usar Funciones?

Usamos funciones por dos razones. Primero, hacen que


el código sea más fácil de leer y comprender. Segundo,
permiten utilizar el código más de una vez.

Imagina un conjunto de código para dibujar el árbol


mostrado en la Figura 9.1. Para conseguirlo, el
programador ejecuta los siguientes comandos:
pygame.dibujar.rect(pantalla, MARRON, [60, 400, 30, 45])

pygame.dibujar.polygon(pantalla, VERDE, [[150, 400], [75, 250], [0, 400]])

pygame.dibujar.polygon(pantalla, VERDE, [[140, 350], [75, 230], [10, 350]])

Figure 9.1: Simple Tree

Realmente no parece tan obvio que esas tres líneas de


código vayan a dibujar un árbol! Si tenemos varios
árboles u objetos complejos, empieza a ser difícil
comprender qué es lo que se está dibujando.
Cuando definimos una función hacemos que el
programa sea más fácil de leer. Para definir una función
empezamos usando el comando def. A continuación del
comando def viene el nombre la función. En este caso la
vamos a llamar dibuja_arbol. Empleamos las mismas reglas
para los nombres de las funciones que para los nombres
de las variables.

A continuación del nombre de la función vienen unos


paréntesis y dos puntos. Todos los comandos de la
función estarán indentados dentro de ella. Observa el
ejemplo siguiente:
def dibuja_arbol():

pygame.dibujar.rect(pantalla, MARRON, [60, 400, 30, 45])

pygame.dibujar.polygon(pantalla, VERDE, [[150, 400], [75, 250], [0, 400]])

pygame.dibujar.polygon(pantalla, VERDE, [[140, 350], [75, 230], [10, 350]])

Por sí solo este código no conseguirá dibujar el árbol.


Tan sólo le dice al ordenador cómo tiene que dibujar_arbol.
Tú tienes que llamar a la función para que realmente se
ejecute el código contenido en ella y conseguir que el
árbol se dibuje:
dibujar_arbol()
Con una biblioteca completa de funciones que definan
cómo dibujar diferentes cosas, el programa final podría
parecerse a éste:
dibujar_arbol()

dibujar_casa()

dibujar_coche()

dibujar_conejito_asesino()

Recuerda que dibujar_arbol tiene tres líneas de código. Cada


una de las otras funciones, como dibujar_casa, tienen
múltiples líneas de código. Cuando usamos funciones,
podemos repetir los comandos sin necesidad de repetir
todo el código contenido en ellas, consiguiendo un
programa mucho más pequeño.

Los nombres de las funciones son muy importantes. Si


los nombre son descriptivos, incluso una persona no
programadora podría ser capaz de leer un conjunto de
código y obtener una idea de qué es lo que está pasando.
Los nombres de las funciones siguen las mismas reglas
que los de las variables, debiendo empezar siempre por
una minúscula.

9.2 Definiendo una Función


Define todas las funciones al principio de tu programa.
Hazlo a continuación de las importaciones y de
cualquier variable global. No definas ninguna función
después del inicio de tu programa principal.

Para definir la función usa la palabra def seguida por el


nombre de la función. Las funciones deberían empezar
por una letra minúscula. Siempre cierra el nombre de la
función con un par de paréntesis y dos puntos. Mira el
siguiente ejemplo:
def imprimir_instrucciones():

print("Bienvenido a la Batalla del Barro! El objetivo es darle al otro jugado con una bo

print("Introduce el ángulo (en grados) y la cantidad de PSI para cargar tu arma")

Cuando necesites que se impriman las instrucciones,


bastaría con llamar a la función de ésta manera:
imprimir_instrucciones()

9.3 Parámetros de la Función

Las funciones pueden tomar parámetros. Estos pueden


usarse para aumentar la flexibilidad de la función,
alterando lo que puede hacer basándose en los
parámetros que se le pasen. Por ejemplo, nuestra
función dibujar_arbol(), dibuja el árbol en una ubicación
específica. Pero podríamos modificar la función para
que acepte parámetros que especifiquen dónde dibujar el
árbol. Por ejemplo, dibujar_arbol(pantalla, 0, 230), dibujaría el
árbol en unas coordenadas (x, y) con valores (0, 230).

Este ajuste de la función podría tener el siguiente


aspecto:
def dibujar_arbol(pantalla, x, y):

pygame.draw.rect(pantalla, MARRON, [60+x, 170+y, 30, 45])

pygame.draw.polygon(pantalla, VERDE, [[150+x,170+y],[75+x,20+y], [x,170+y]])

pygame.draw.polygon(pantalla, VERDE, [[140+x,120+y], [75+x,y], [10+x,120+y]])

Esto nos permitiría dibujar múltiples árboles


dondequiera que quisiéramos:
dibujar_arbol(pantalla, 0, 230)

dibujar_arbol(pantalla, 200, 230)

dibujar_arbol(pantalla, 400, 230)

La siguiente es una función que puede ejecutarse sin


usar gráficos. Esta función calcula e imprime el volumen
de una esfera:
Función que imprime el volumen de una esfera

1 def volumen_esfera(radio):

2 pi = 3.141592653589
3 volumen = (4 / 3) * pi * radio ** 3

print("El volumen es ", volumen)


4
Vídeo: Crear Una Función

Los parámetros son asignados cuando se llama a la función, no


cuando se la define.

El nombre de la función es volumen_esfera. Los datos que


recogerá la función se almacenarán en una nueva
variable llamada radio. El volumen resultante se imprime
por pantalla. La variable radiono obtiene su valor dentro
de la función. Frecuentemente, los programadores
principiantes se confunden en esto. A las variables de
los parámetros no se les asigna un valor en el momento
de definir la función. A los parámetros se les asigna un
valor cuando la función es llamada.

Llamamos a la función de ésta manera:


volumen_esfera(22)

La variable radio en la función es creada e inicializada con


un valor de 22. El código de la función se ejecuta una vez
que la compilación alcanza la llamada de la función.

¿Pero qué sucede si necesitamos pasar más de un valor?


Pues podemos pasar múltiples parámetros a la función,
cada uno separado por una coma:
Función que imprime el volumen de un cilindro

1 def volumen_cilindro(radio, altura):


2 pi = 3.141592653589

3 volumen = pi * radio ** 2 * altura

print("El volumen es ", volumen)


4

Podemos llamar a la función de esta forma:


volumen_cilindro(12, 3)

Los parámetros se escriben en orden, por lo que a radio le


corresponde el 12 y a la altura el 3.

9.4 Devolviendo y capturando


valores
Lamentablemente, este ejemplo de función es limitado.
¿Por qué? Pues si una persona quisiera usar la
función volumen_cilindro para calcular el volumen de un pack
de seis, no serviría. Sólo imprime el volumen de un solo
cilindro. No podemos usar el resultado de la función
para un cilindro, meterlo luego en una ecuación,
multiplicarlo por seis y así obtener el volumen del pack.
9.4.1 Devolviendo valores

Esto se puede resolver usando la declaración return. Por


ejemplo:
Función que devuelve la suma de dos números

1 # Suma dos números y devuelve el resultado


2 def suma_dos_numeros(a, b):

3 resultado = a + b

return resultado
4

Return no es una función, y no usa paréntesis. No vayas


a escribir return(resultado).

Bien, esto sólo resuelve la mitad del problema, ya que si


llamásemos ahora a la función, no sucedería gran cosa.
Los números se suman. Nos son devueltos. Pero no
hacemos nada con el resultado.

1 # Con esto no adelantamos mucho, ya que no capturamos el resultado

2 suma_dos_numeros(22, 15)

9.4.2 Capturando los valores devueltos

Necesitamos capturar el resultado. Esto lo conseguimos


estableciendo una variable que sea igual al valor
devuelto por la función:
1 # Almacenamos el resultado de la función en un variable

2 mi_resultado = suma_dos_numeros(22, 15)

print(mi_resultado)
3

Ahora ya no hemos desperdiciado el resultado. Está


almacenado en mi_resultado, el cual lo podemos imprimir o
utilizar para alguna otra cosa.
Function that returns the volume of a cylinder

1 def volumen_cilindro(radio, altura):


2 pi = 3.141592653589

3 volumen = pi * radio ** 2 * altura

return volumen
4
Debido al return, la función puede usarse más tarde como parte de una ecuación que
calcule el volumen para el pack de seis:
volumen_pack_seis = volumen_cilindro(2.5, 5) * 6

El valor devuelto por volumen_cilindro va a la ecuación y es


multiplicado por seis.

Hay una enorme diferencia entre una función


que imprime (print) y otra que devuelve (return) un valor.
Observa el código siguiente, luego ejecútalo:
# Función que imprime el resultado

def imprime_suma(a, b):

resultado = a + b

print(resultado)
# Función que devuelve el resultado

def devuelve_suma(a, b):

resultado = a + b

return resultado

# Esto imprime en pantalla la suma de 4+4

imprime_suma(4, 4)

# Este no

devuelve_suma(4, 4)

# Esto no introducirá el resultado de la suma en x1

# Obtiene realmente un valor de 'None'

x1 = imprime_suma(4, 4)

# Esto sí lo hará

x2 = devuelve_suma(4, 4)

Es normal que cuando empezamos a trabajar con


funciones, nos quedemos atascados al observar códigos
de este estilo:
def calcular_promedio(a, b):

""" Calcula el promedio de dos números """

resultado = (a + b) / 2

return resultado

# Supongamos que tenemos algún código aquí

x = 45

y = 56

# Espera, ¿cómo imprimo el resultado de esto?

calcular_promedio(x, y)

¿Cómo imprimimos el resultado para calcular_promedio? El


programa no puede imprimir resultadoporque la variable
solo existe dentro de la función. En su lugar, utilizamos
una variable para capturar el resultado:
def calcular_promedio(a, b):

""" Calcula el promedio de dos números """

resultado = (a * b) / 2

return resultado

# Imagina que aquí hay algo de código

x = 45

y = 56

promedio = calcular_promedio(x, y)

print(promedio)

9.5 Documentar Las Funciones


En Python, las funciones suelen tener un comentario al
principio del todo. Este comentario está delimitado por
triples comillas y se le conoce como docstring. Una
función podría tener este aspecto:
Función documentada que devuelve el volumen de un cilindro

1
def volumen_cilindro(radio, altura):
2 """Devuelve el volumen de un cilindro, conocidos su radio y altura."""

3 pi = 3.141592653589

volumen = pi * radio ** 2 * altura


4
return volumen
5

Una gran cosa al usar docstrings en funciones, es que el


comentario puede ser extraído y colocado en una
website que documente tu código, usando una
herramienta como Sphinx. La mayoría de lenguajes
tienen herramientas similares que ayudan a documentar
tu código en un suspiro. Esto te puede ayudar a ahorrar
tiempo a medida que comiences a trabajar en programas
mucho más largos.

9.6 Ámbito de la Variable


Vídeo: Ámbito de la Variable

El uso de funciones nos introduce en el concepto


de ámbito (scope). El ámbito es el lugar del código donde
la variable está “viva” y podemos acceder a ella.
Observa por ejemplo el código siguiente:
# Define una función elemental que estable que

# x es igual a 22

def f():

x = 22

# Llamada a la función

f()

# Falla, ya que x existe solo en f()

print(x)

La última línea generará un error, porque x solo existe


dentro de la función f(). La variable es creada cuando
llamamos a f(), y el espacio de memoria que ocupa es
liberado tan pronto como f()termina.
Aquí es donde las cosas se complican.

Una regla áun más confusa es el acceder a variables


creadas fuera de la función f(). En el código
siguiente, x se crea antes de la función f(), y así, se puede
leer desde el interior de la función f().
# Crea la variable x y le asigna 44

x = 44

# Definimos una sencilla función que imprime x

def f():

print(x)

# Llamamos a la función

f()

Las variables creadas antes de una función, pueden


leerse dentro de la función sólo si la función no cambia el
valor. Éste código, muy parecido al anterior, fallará. El
ordenador reclamará que desconoce qué es x.
# Crea la variable x y le asigna 44

x = 44

# Definimos una sencilla función que imprime x

def f():

x += 1

print(x)

# Llamamos a la función

f()
Otros lenguajes tienen reglas más complejas en cuanto a
la creación de variables y su ámbito que Python. Debido
a que Python es un lenguaje sencillo, es un buen
lenguaje introductorio.

9.7 Pass-by-copy
Las funciones pasan sus valores, creando copias del
original. Por ejemplo:
# Define una función elemental que imprime x

def f(x):

x += 1

print(x)

# Establece y

y = 10

# Llamada a la función

f(y)

# Imprime y para ver si ha cambiado

print(y)

El valor de y no cambia a pesar de que la


función f() incrementa el valor que le pasan. Cada una
de las variables listada como parámetro en una función,
es una variable totalmente nueva. El valor de la variable
es copiado desde donde se le ha llamado.

En el primer ejemplo esto se ve razonablemente sencillo.


Donde todo empieza a volverse confuso es cuando,
tanto el código que llama a la función, como la propia
función, tienen variables con el mismo nombre. El
siguiente código es idéntico al anterior, pero en lugar de
usar y usa x.
# Define una función elemental que imprime x

def f(x):

x += 1

print(x)

# Establece x

x = 10

# Llamada a la función

f(x)

# Imprime x para ver si ha cambiado

print(x)

La salida es igual a la del programa que usa y. A pesar


de que ambos, la función y el código que la rodea, usan
el nombre de x para la variable, realmente
existen dos variables diferentes. Está la variablex que
existe dentro de la función, y una variable x, diferente,
que existe fuera de la función.
9.8 Funciones Que Llaman
Funciones
Es muy posible que una función llame a otra función.
Por ejemplo, digamos que hemos definido funciones
como las siguientes:
def brazo_fuera(queBrazo, palmasArribaOAbajo):

# el código vendría aquí

def agarrar_mano(mano, brazo):

# el código vendría aquí

Luego podríamos tener otra función que llamara a las


dos anteriores:
def macarena():

brazo_fuera("derecho", "abajo")

brazo_fuera("izquierdo", "abajo")

brazo_fuera("derecho", "arriba")

brazo_fuera("izquierdo", "arriba")

agarrar_mano("derecha", "brazo izquierdo")

agarrar_mano("izquierda", "brazo derecho")

# etc

9.9 Funciones Principales y


Globales
Las variables globales son, sencillamente, diabólicas.
A medida que los programas se hacen más grandes, es
importante mantener organizado el código en
funciones. Python nos permite escribir el código en un
“nivel 0 de indentación.” Esto describe la mayoría del
código que hemos escrito hasta ahora. Nuestro código
está alineado a la izquierda y no se ha colocado dentro
de funciones.

Esta clase de filosofía es similar a la amontonar toda


nuestra ropa en el centro de la habitación, o guardar
todas nuestras herramientas sobre el banco de trabajo.
Solo funciona cuando son pocos los objetos. Pero aun
cuando tengas pocos de ellos, todo resulta bastante
desordenado.

Deberías poner todo tu código y todas tus variables


dentro de funciones. Esto mantendrá organizado el
programa. También te ayudará en el momento que
tengas la necesidad de buscar un fallo en el programa.
Las variables creadas al “nivel 0 de indentación”, se
llaman variables globales. Estas variables globales son
una una muy mala idea. ¿Por qué? Porque cualquier trozo
de código, en cualquier lugar, puede cambiar su valor.
Si tienes 50,000 líneas de programa, cada línea de código
puede cambiar el valor de esa variable global. En
cambio, si mantienes la variable dentro de la función,
entonces, solamente ese código de la función puede
cambiarla. Así, si tienes un valor inesperado en una
variable, solo tienes que buscar a través de,
probablemente, 50 líneas de código. De la otra forma
tendrías que revisar cada línea de código en el programa
entero!

Una buena forma de escribir un programa en Python


sería seguir este patrón:
def main():

print("Hola mundo.")

main()

En este caso, todo el código que normalmente se hubiera


ejecutado en un nivel 0 de indentación, se coloca dentro
de la función main. La última línea del archivo llama a main.

Pero espera! Tenemos otro problema que arreglar. En el


Capítulo 14 hablaremos de cómo partir nuestro
programa en varios archivos. Podemos usar el
comando import para traer funciones desde otros módulos
que hayamos creado anteriormente. Si usamos el
comando import en este módulo, automáticamente
ejecutaría la función main. Pero no queremos esto.
Queremos que el programa que lo importa, controle
cuándo debe llamarse a la función.
Para solucionarlo, podemos hacer que nuestro
programa compruebe una variable global definida
automáticamente por Python. (Lo sé, acabo de decir que
las variables globales son un mala idea, ¿no es cierto?)
La variable es llamada __name__, con dos guiones bajos,
antes y después. Podemos usarlo para probar si éste
código está siendo importado o ejecutado. Si el código está
siendo ejecutado, Python establecerá automáticamente
el valor de esta variable como __main__. Usando una
declaración ifllamaremos a la función main solo si el
código está siendo ejecutado. En caso contrario, el
código solo definirá la función main. El código que lo haya
importado podrá llamar a la función cuando quiera.

Esta es la forma cómo todo tu código


Python debería ejecutarse:
Uso adecuado de la función main
def main():

print("Hola Mundo.")

if __name__ == "__main__":

main()

Una de las razones por las que me encanta Python como


primer lenguaje, es que no estás obligado a usar toda
esta complejidad hasta que realmente la necesitas. Otros
programas, por ejemplo Java, te obligan a toda esta
complejidad sin importar lo pequeño que sea tu
programa.

Para hacer las cosas fáciles en este libro no te


mostraremos ejemplos que sigan este patrón. Pero
después de este libro, tus programas serán,
probablemente, lo suficientemente complejos, que tu
vida será más fácil si no “tiras toda tu ropa sobre un
montón”, por decirlo así.

Si eres un super entusiasta de la programación, intenta


empezar ahora a escribir tus programas de esta forma.
Aunque suponga un poco más de desafío comenzar así,
más adelante te será más fácil escribir tus programas.
También es una buena forma de aprender cómo manejar
adecuadamente tus datos y su ámbito.

Este es un ejemplo que muestra cómo usar la plantilla


Pygame con este patrón:
pygame_base_template_proper.py

No es necesario usar esta plantilla, al menos mientras


sea yo el que dicte el curso. Para mí está bien que
durante tu primer semestre apiles tu ropa en medio de
la habitación. Me contento con que puedas vestirte.
(friquis de pro: podemos 'limpiar' aún más este
programa cuando lleguemos al Capítulo de “clases.”)
9.10 Ejemplos
En cada uno de los siguientes ejemplos, piensa qué es lo
que imprimirán. Comprueba si has acertado. Si no lo has
conseguido, dedícale un poco de tiempo a entender el
por qué no ha sido así.
Ejemplo 1

1
2
3 # Ejemplo 1

4 def a():

print("A")
5
def b():
6
print("B")
7 def c():

8 print("C")

a()
9
10
11
Ejemplo 2

1 # Ejemplo 2

2 def a():

b()
3
print("A")
4 def b():

5 c()

print("B")
6
7 def c():

print("C")
8
a()
9
10
11
12
13
Ejemplo 3

1
2
3
# Ejemplo 3
4 def a():

5 print("A")

b()
6
def b():
7
print("B")
8 c()

9 def c():

print("C")
10
a()
11
12
13
Ejemplo 4
# Ejemplo 4
1
def a():
2
print("A empieza con")
3 b()
4 print("A termina con")

def b():
5
print("B empieza con")
6 c()

7 print("C termina con")

def c():
8
print("C empieza y termina con")
9
a()
10
11
12
13
14
15
Ejemplo 5

1
2 # Ejemplo 5

3 def a(x):

print("A empieza con, x = ",x)


4
b(x + 1)
5 print("A termina con, x = ",x)
6 def b(x):

7 print("B empieza con, x = ",x)

c(x + 1)
8
print("C termina con, x = ",x)
9 def c(x):

10 print("C empieza y termina con, x = ",x)

a(5)
11
12
13
14
15
Ejemplo 6

1
2
# Ejemplo 6
3 def a(x):
4 x =x +1

5 x =3

a(x)
6
print(x)
7
8
Ejemplo 7

1
2
# Ejemplo 7
3
def a(x):
4 x =x +1

5 return x

x =3
6
a(x)
7
print(x)
8
9
Ejemplo 8
# Ejemplo 8
1
def a(x):
2 x =x +1
3 return x

x =3
4
x = a(x)
5 print(x)

6
7
8
9
Ejemplo 9

1
2
# Ejemplo 9
3
def a(x, y):
4
x =x +1
5 y =y +1

6 print(x, y)

x = 10
7
y = 20
8
a(y, x)
9 print(z)

10
11
Ejemplo 10

1 # Ejemplo 10

def a(x, y):


2
x =x +1
3
y =y +1
4 return x

5 return y
6 x = 10

y = 20
7
z = a(x, y)
8 print(z)

9
10
11
12
Ejemplo 11

1
2
# Ejemplo 11
3
def a(x, y):
4
x =x +1
5 y =y +1

6 return x, y

x = 10
7
y = 20
8
z = a(x, y)
9 print(z)

10
11
Ejemplo 12

1 # Ejemplo 12

def a(x, y):


2
x =x +1
3
y =y +1
4 return x, y

5 x = 10
6 y = 20

x2, y2 = a(x, y) # La mayoría de lenguajes de programación no admiten esto


7
print(x2)
8 print(y2)

9
10
11
12
Ejemplo 13

1
2
# Ejemplo 13
3
def a(mis_datos):
4
print("función a, mis_datos = ", mis_datos)
5 mis_datos = 20

6 print("función a, mis_datos = ", mis_datos)

mis_datos = 10
7
print("entorno global, mis_datos = ", mis_datos)
8
a(mis_datos)
9 print("entorno global, mis_datos = ", mis_datos)

10
11
Ejemplo 14

1 # Ejemplo 14

def a(mi_lista):
2
print("función a, lista = ", mi_lista)
3
mi_lista = [10, 20, 30]
4 print("función a, lista = ", mi_lista)

5 mi_lista = [5, 2, 4]
6 print("entorno global, lista = ", mi_lista)

a(mi_lista)
7
print("entorno global, lista = ", mi_lista)
8
9
10
11
Ejemplo 15

1
2
3 # Ejemplo 15

# Concepto nuevo!
4
# Se describe con más detalle en el Capítulo 12
5
def a(mi_lista):
6 print("función a, lista = ", mi_lista)

7 mi_lista[0] = 1000

print("función a, lista = ", mi_lista)


8
mi_lista = [5, 2, 4]
9
print("entorno global, lista = ", mi_lista)
10 a(mi_lista)

11 print("entorno global, lista = ", mi_lista)

12
13
#--

mudball.py
"""
1
Este es un simple juego de texto para mostrar el uso de funciones.
2
El juego se llama "BolaDeBarro", donde los jugadores, por turnos, se
3 lanzan bolas de barro unos contra otros, hasta que alguien es alcanzado.
4 """

import math
5
import random
6 def imprimir_instrucciones():

7 """ Esta función imprimirá las instrucciones. """

# En una declaración print, puedes usar comillas triples para


8
# imprimir varias líneas.
9
print("""
10 Bienvenido a Bolas de Barro! El objetivo es darle al otro jugador con una bola de b

11 Introduce el ángulo (en grados) y la presión en PSI para cargar tu arma.

""")
12
def calcular_distancia(psi, angulo_en_grados):
13 """ Calcula la distancia que vuela la bola de barro. """
14 angulo_en_radianes = math.radians(angulo_en_grados)

15 distancia = psi * math.sin(angulo_en_radianes) * 15

return distancia
16
def obtener_datosdel_usuario(nombre):
17 """ Obtiene del usuario los valores para la presión y el ángulo. Lo devuelve como u

18 números. """

# Más adelante, en el capítulo sobre 'excepciones', aprenderemos como


19
# modificar este código para que no se cuelgue cuando el usuario escriba
20
# algo que no sea un número válido.
21 psi = float(input(nombre + " ¿con cuántos psi cargamos el arma? "))

22 angulo = float(input(nombre + " ¿con qué ángulo quieres apuntar el arma? "))

return psi, angulo


23
def obtener_nombres_jugadores():
24 """ Obtenemos una lista con los nombres de los jugadores. """

25 print("Introduce los nombres de los jugadores. Puedes introducir cuantos quieras.")

hecho = False
26
jugadores = []
27
while not hecho:
28 jugador = input("Introducir jugador (presiona intro para salir): ")
29 if len(jugador) > 0:

jugadores.append(jugador)
30
else:
31 hecho = True

32 print()

return jugadores
33
def procesa_turno_jugador(jugador_nombre, distancia_aparte):
34
""" El código ejecuta el turno para cada jugador.
35 Si devuelve False, continuamos con el juego.

36 Si devuelve True, alguien ha ganado así que paramos. """

psi, angulo = entrada_usuario = obtener_datosdel_usuario(jugador_nombre)


37
distancia_boladebarro = calcular_distancia(psi, angulo)
38 diferencia = distancia_boladebarro - distancia_aparte
39 # Si echamos un vistazo al capítulo de formatos de impresión, estas líneas

40 # podrían imprimir números en un bonito formato.

if diferencia > 1:
41
print("Ha caído", diferencia, "metros muy lejos!")
42 elif diferencia < -1:

43 print("Te has quedado", diferencia * -1, "metros corto!")

else:
44
print("Bingo!", jugador_nombre, "gana!")
45
return True
46 print()

47 return False

def main():
48
""" Programa Principal. """
49 # Comenzamos el juego.

50 imprimir_instrucciones()

jugador_nombres = obtener_nombres_jugadores()
51
distancia_aparte = random.randrange(50, 150)
52
# Se mantiene alerta hasta que alguien gana
53 hecho = False
54 while not hecho:

# Iteramos para cada jugador


55
for jugador_nombre in jugador_nombres:
56 # Procesamos sus turnos

57 hecho = procesa_turno_jugador(jugador_nombre, distancia_aparte)

# Si alguien gana, 'rompemos' el bucle y finalizamos el juego.


58
if hecho:
59
break
60 if __name__ == "__main__":

61 main()

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88

89

90

91

92

93

94

95

9.11 Repaso

9.11.1 Test

Haz click para ir al Test.

9.11.2 Ejercicios

Haz click para ir a los Ejercicios.

9.11.3 Taller

Haz click para ir al Taller.


Chapter 10: Mandos de Juegos y
Gráficos
¿Cómo podemos hacer para que los objetos se muevan
usando el teclado, ratón o mando de juegos?

10.1 Introducción

Hasta aquí hemos visto cómo mover objetos en la


pantalla, pero no cómointeractuar con ellos. ¿De qué
forma podemos usar el ratón, el teclado o el mando de
juegos para controlar las acciones en la pantalla? Por
suerte, es bastante sencillo.

Para comenzar necesitamos un objeto que mover por la


pantalla. La mejor forma de conseguirlo es tener
una función que reciba unas coordenadas x e y, y que
luego dibuje un objeto en ese lugar. Volvamos pues al
Capítulo 9! Veamos cómo escribir una función que
dibuje un objeto.
Vídeo: Dibujar mediante una función

Todas las funciones de dibujo Pygame requieren de un


parámetro pantalla para que Pygame sepa sobre qué
ventana debe dibujar. Esto debemos pasárselo a través
de cualquier función que construyamos para dibujar un
objeto sobre la pantalla.

La función necesita saber también, en qué parte de la


pantalla debe dibujar el objeto. Necesita una x y una y.
La ubicación se la pasamos como un parámetro. A
continuación se puede ver el código que define una
función que, cuando la llamemos, dibujará un hombre
de nieve:
Función para dibujar un hombre de nieve

1
def dibujar_hombredenieve(pantalla, x, y):
2
# Dibuja un círculo para la cabeza
3 pygame.draw.ellipse(pantalla,BLANCO, [35 + x, y, 25, 25])

4 # Dibuja un círculo para la parte central del hombre

pygame.draw.ellipse( pantalla,BLANCO, [23 + x, 20 + y, 50, 50])


5
# Dibuja un círculo para la parte baja del hombre
6
pygame.draw.ellipse( pantalla,BLANCO, [x, 65 + y, 100, 100])
7

Luego, en el bucle principal del programa, podremos


dibujar múltiples hombres de nieve, tal como se puede
ver en la Figura 10.1.
Figure 10.1: Hombre de nieve dibujado por una
función

Llamando a la función dibujar_hombredenieve

1
2
# Hombre de nieve en la parte superior izquierda
3 dibujar_hombredenieve ( pantalla , 10, 10)
4 # Hombre de nieve en la parte superior derecha

5 dibujar_hombredenieve ( pantalla , 300, 10)

# Hombre de nieve en la parte inferior izquierda


6
dibujar_hombredenieve ( pantalla , 10, 300)
7
8
Vídeo: Convertir un código existente en una función
Un ejemplo completo se puede ver en:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=functions_and_graphics.py

Puede que de alguno de los talleres anteriores hayamos


guardado el código de algo que dibuje una figura
interesante. Pero ahora, ¿cómo lo metemos dentro de
una función? Veamos el siguiente ejemplo:
Código para dibujar un hombre de palitos

1
2
3
# Cabeza
4 pygame.draw.ellipse(pantalla, NEGRO, [96, 83, 10, 10], 0)

5 # Piernas

pygame.draw.line(pantalla, NEGRO, [100, 100], [105, 110], 2)


6
pygame.draw.line(pantalla, NEGRO, [100, 100], [95, 110], 2)
7
# Cuerpo
8 pygame.draw.line(pantalla, ROJO, [100, 100], [100, 90], 2)

9 # Brazos

pygame.draw.line(pantalla, ROJO, [100, 90], [104, 100], 2)


10
pygame.draw.line(pantalla, ROJO, [100, 90], [96, 100], 2)
11
12
13
Figure 10.2: Hombre de palitos

Este código puede introducirse fácilmente dentro de


una función, añadiendo el comando def e indentándolo.
Tenemos que traer todos los datos que la función
necesita para dibujar el hombre de palitos. Luego
añadirle la variable pantalla que le diga dónde debe
dibujarlo y unas coordenadas x, y, que le digan en qué
lugar de ella colocarlo.

¡Pero no podemos definir la función en el medio del


bucle del programa! El código debe eliminarse de la
parte principal del programa. Las declaraciones de
funciones deben ir al principio. Por ello, moveremos
este código a ese lugar. Para poder visualizarlo mejor,
observar la Figura 10.3.
Función que siempre dibuja en el mismo sitio un hombre de palitos

1 def dibuja_hombrepalitos(pantalla,x,y):

# Cabeza
2
pygame.draw.ellipse(pantalla, NEGRO, [96, 83, 10, 10], 0)
3 # Piernas

4 pygame.draw.line(pantalla, NEGRO, [100, 100], [105, 110], 2)


5 pygame.draw.line(pantalla, NEGRO, [100, 100], [95, 110], 2)

# Cuerpo
6
pygame.draw.line(pantalla, ROJO, [100, 100], [100, 90], 2)
7 # Brazos

8 pygame.draw.line(pantalla, ROJO, [100, 90], [104, 100], 2)

pygame.draw.line(pantalla, ROJO, [100, 90], [96, 100], 2)


9
10
11
12
13
14
Figure 10.3: Escribir una Función y Situarla en el Lugar Correcto

En este momento, el código toma unas coordenadas x e


y. Por desgracia, no sabe realmente qué hacer con ellas.
Ya puedes especificar cualquier coordenada que
quieras, que la figura del hombre de palitos siempre
aparecerá exactamente en el mismo lugar. Esto no resulta
muy útil que digamos. Las líneas siguiente añaden,
literalmente, las coordenadas x e y al código que ya
teníamos.
Segundo intento con la función hombre de palitos

1
2
3
def dibuja_hombrepalitos(pantalla, x, y):
4
# Cabeza
5 pygame.draw.ellipse(pantalla, NEGRO,[96 + x, 83 + y, 10, 10], 0)

6 # Piernas

pygame.draw.line(pantalla, NEGRO, [100 + x, 100 + y], [105 + x, 110 + y], 2)


7
pygame.draw.line(pantalla, NEGRO, [100 + x, 100 + y], [95 + x, 110 + y], 2)
8 # Cuerpo

9 pygame.draw.line(pantalla, ROJO, [100 + x, 100 + y], [100 + x, 90 + y], 2)

# Brazos
10
pygame.draw.line(pantalla, ROJO, [100 + x, 90 + y], [104 + x, 100 + y], 2)
11
pygame.draw.line(pantalla, ROJO, [100 + x, 90 + y], [96 + x, 100 + y], 2)
12
13
14
Ahora el problema es que la figura aparece dibujada a
cierta distancia del origen. Vamos a asumir el origen en
(0,0) y dibujar la figura alrededor de 100 píxeles, hacia
abajo y a la derecha. Observa la Figura 10.4 de cómo el
hombre de palitos no aparece en las coordenadas (0,0)
que le habíamos pasado.

Figure 10.4: Hombre de Palitos

Añadiendo x e y a la función, desplazamos el origen del


hombre de palitos en esa cantidad. Por ejemplo si
llamamos:
dibuja_hombrepalitos(pantalla, 50, 50)

El código no sitúa a la figura en (50, 50). Más bien,


desplaza el origen hacia abajo y a la derecha en 50
píxeles. Como nuestra figura ya había sido dibujada
previamente alrededor de las coordenadas (100,100),
ahora se encuentra en (150,150). ¿Pero cómo podemos
solucionar esto, de forma que la figura se dibuje
realmente donde la llamada de la función indica?

Figure 10.5: Hallando los Mínimos Valores para X e Y

Encuentra los valores más pequeños para x e y tal como


se muestra en la Figura 10.5, luego, resta este valor de
cada x e y de la función. No los confundas con los
valores para la altura y el ancho. Este es un ejemplo de
dónde hemos restado los mínimos de x e y:
Tercer intento con la función hombre de palitos
def dibuja_hombrepalitos(pantalla, x, y):
1
# Cabeza
2
pygame.draw.ellipse(pantalla, NEGRO, [96 - 95 + x, 83 - 83 + y, 10, 10], 0)
3 # Piernas

4 pygame.draw.line(pantalla, NEGRO, [100 - 95 + x, 100 - 83 + y], [105 - 95 + x, 110 - 83 + y]

pygame.draw.line(pantalla, NEGRO, [100 - 95 + x, 100 - 83 + y], [95 - 95 + x, 110 - 83 + y],


5
# Cuerpo
6
pygame.draw.line(pantalla, ROJO, [100 - 95 + x, 100 - 83 + y], [100 - 95 + x, 90 - 83 + y],
7 # Brazos
8 pygame.draw.line(pantalla, ROJO, [100 - 95 + x, 90 - 83 + y], [104 - 95 + x, 100 - 83 + y],

pygame.draw.line(pantalla, ROJO, [100 - 95 + x, 90 - 83 + y], [96 - 95 + x, 100 - 83 + y], 2


9
10
11
12
13
14

O, para hacerlo más sencillo, realiza tú mismo la resta:


Función hombre de palitos final

1
2
3
def dibuja_hombrepalitos(pantalla, x, y):
4
# Cabeza
5 pygame.draw.ellipse(pantalla, NEGRO, [1 + x, y, 10, 10], 0)

6 # Piernas

pygame.draw.line(pantalla, NEGRO ,[5 + x, 17 + y], [10 + x, 27 + y], 2)


7
pygame.draw.line(pantalla, NEGRO, [5+ x, 17 + y], [x, 27 + y], 2)
8 # Cuerpo

9 pygame.draw.line(pantalla, ROJO, [5 + x, 17 + y], [5 + x, 7 + y], 2)

# Brazos
10
pygame.draw.line(pantalla, ROJO, [5 + x, 7 + y], [9 + x, 17 + y], 2)
11
pygame.draw.line(pantalla, ROJO, [5 + x, 7 + y], [1 + x, 17 + y], 2)
12
13
14
10.2 Ratón
Vídeo: Mover con el ratón

Genial, ahora ya sabemos cómo escribir una función que


dibuje un objeto sobre unas coordenadas específicas.
¿Pero cómo averiguamos esas coordenadas? La forma
más fácil de conseguirlo es mediante el ratón. Solo
necesitamos una línea de código para lograrlo.
pos = pygame.mouse.get_pos()

El truco está en que esas coordenadas son devueltas


como una lista, o más concretamente, como una tupla
inmodificable. Tanto los valores de x, como los de y son
almacenados en la misma variable. Por ello, si hacemos
un print(pos), obtendremos lo que se muestra en la
Figura 10.6.

Figure 10.6: Coordenadas

La variable pos es una tupla de dos números. La


coordenada x se encuentra en la posición 0 del array y
la coordenada y en la 1. Así pueden extraerse fácilmente
para pasarlas a la función que dibuja el objeto:
Controlar un objeto vía ratón
# Lógica del Juego

pos = pygame.mouse.get_pos()

x = pos[0]

y = pos[1]

# Sección de dibujo

dibuja_hombrepalitos(pantalla, x, y)

Obtener el ratón debería ir en la sección “lógica del


juego” del bucle principal del programa. La llamada a la
función debería ir en la sección de “dibujo” del bucle
principal del programa.

El único problema con esto, es que el puntero del ratón


se sitúa justo encima de la figura, dificultando su visión,
tal como se ve en la Figura 10.7.

Figure 10.7: Figura de palitos con cursor de ratón encima

Podemos ocultar el ratón escribiendo el siguiente


código, justo antes del bucle principal del programa:
Ocultar el cursor del ratón
# Ocultar el cursor del ratón

pygame.mouse.set_visible(False)
Podemos encontrar un ejemplo completo de todo esto
en:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=move_mouse.py

10.3 Teclado
Vídeo: Mover con el teclado

El control con el teclado es un poco más complicado. No


podemos extraer la x e y como con el ratón. El teclado
no nos devuelve una x e y. Por esto necesitamos:

• Crear unas x e y iniciales para nuestra posición de


partida.
• Establecer en píxeles, una “velocidad” por
fotograma, para cuando una tecla de flecha es
pulsada. (pulsar tecla)
• Devolver a cero la velocidad para cuando soltemos
la tecla de flecha. (soltar tecla)
• Dependiendo de la velocidad, ajustar x e y en cada
fotograma.

Parece complicado, pero es como el rectángulo saltarín


que ya hicimos, con la excepción de que la velocidad es
controlada por el teclado

Para empezar, establecemos la ubicación y


velocidad antes de que comience el bucle principal:
# Velocidad en píxeles por fotograma

x_speed = 0

y_speed = 0

# Posición actual

x_coord = 10

y_coord = 10

Dentro del bucle principal while del programa


necesitamos incluir algunos objetos en nuestro bucle
procesador de eventos. Además de buscar por un
evento pygame.QUIT, el programa precisa buscar los eventos
del teclado. Cada vez que el usuario pulsa una tecla, se
genera un evento.

Un evento pygame.KEYDOWN es generado cuando una tecla es


pulsada. Un evento pygame.KEYUP es generado cuando
soltamos la tecla. Cuando el usuario pulsa una tecla, la
velocidad del vector se establece en 3 o -3 píxeles por
fotograma. Cuando levanta el dedo de la tecla, el vector
velocidad vuelve a cero. En último lugar, las
coordenadas del objeto se ajustan por el vector,
dibujándose en ese momento. Observa el siguiente
código :
Controlar un objeto vía teclado
for evento in pygame.event.get():

if evento.type == pygame.QUIT:

hecho = True

# El usuario pulsa una tecla


if evento.type == pygame.KEYDOWN:

# Resuelve que ha sido una tecla de flecha, por lo que

# ajusta la velocidad.

if evento.key == pygame.K_LEFT:

x_speed = -3

if evento.key == pygame.K_RIGHT:

x_speed = 3

if evento.key == pygame.K_UP:

y_speed = -3

if evento.key == pygame.K_DOWN:

y_speed = 3

# El usuario suelta la tecla

if evento.type == pygame.KEYUP:

# Si se trata de una tecla de flecha, devuelve el vector a cero

if evento.key == pygame.K_LEFT:

x_speed = 0

if evento.key == pygame.K_RIGHT:

x_speed = 0

if evento.key == pygame.K_UP:

y_speed = 0

if evento.key == pygame.K_DOWN:

y_speed = 0

# Mueve el objeto de acuerdo a la velocidad del vector.

x_coord += x_speed

y_coord += y_speed

# Dibuja al hombre de palitos

dibuja_hombrepalitos(pantalla, x_coord, y_coord)

Puedes ver el código completo aquí:


ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=move_keyboard.py
Tenemos que observar que en este ejemplo no se impide
que el objeto salga fuera de los límites de la pantalla.
Para hacerlo, en la sección de la lógica del juego,
necesitaremos crear un conjunto de declaraciones if que
comprueben los valores de x_coord y y_coord. Si se hallaran
fuera de los límites de la pantalla, las resetearían. El
código exacto para conseguir esto lo dejamos como un
ejercicio para el lector.

La siguiente tabla muestra una lista completa de códigos


de teclado que podemos usar en Pygame:
Código Pygame ASCII Nombre Común

K_BACKSPACE \b retroceso

K_RETURN \r volver

K_TAB \t tab

K_ESCAPE ^[ escape

K_SPACE espacio

K_COMMA , coma

K_MINUS - menos

K_PERIOD . punto

K_SLASH / barra

K_0 0 0

K_1 1 1

K_2 2 2
Código Pygame ASCII Nombre Común

K_3 3 3

K_4 4 4

K_5 5 5

K_6 6 6

K_7 7 7

K_8 8 8

K_9 9 9

K_SEMICOLON ; punto y coma

K_EQUALS = igual

K_LEFTBRACKET [ corchete izquierdo

K_RIGHTBRACKET] corchete derecho

K_BACKSLASH \ contrabarra

K_BACKQUOTE ` tilde grave

K_a a a

K_b b b

K_c c c

K_d d d

K_e e e

K_f f f

K_g g g

K_h h h

K_i i i
Código Pygame ASCII Nombre Común

K_j j j

K_k k k

K_l l l

K_m m m

K_n n n

K_o o o

K_p p p

K_q q q

K_r r r

K_s s s

K_t t t

K_u u u

K_v v v

K_w w w

K_x x x

K_y y y

K_z z z

K_DELETE delete

K_KP0 keypad 0 teclado numérico

K_KP1 keypad 1 teclado numérico

K_KP2 keypad 2 teclado numérico

K_KP3 keypad 3 teclado numérico


Código Pygame ASCII Nombre Común

K_KP4 keypad 4 teclado numérico

K_KP5 keypad 5 teclado numérico

K_KP6 keypad 6 teclado numérico

K_KP7 keypad 7 teclado numérico

K_KP8 keypad 8 teclado numérico

K_KP9 keypad 9 teclado numérico

K_KP_PERIOD . punto teclado numérico

K_KP_DIVIDE / dividir teclado numérico

K_KP_MULTIPLY * multiplicar teclado numérico

K_KP_MINUS - restar teclado numérico

K_KP_PLUS + sumar teclado numérico

K_KP_ENTER \r intro teclado numérico

K_KP_EQUALS = igual teclado numérico

K_UP up arriba

K_DOWN down abajo

K_RIGHT right derecha

K_LEFT left izquierda

K_INSERT insert insertar

K_HOME home inicio

K_END end fin

K_PAGEUP page página arriba

K_PAGEDOWN page página abajo


Código Pygame ASCII Nombre Común

K_F1 F1

K_F2 F2

K_F3 F3

K_F4 F4

K_F5 F5

K_F6 F6

K_F7 F7

K_F8 F8

K_F9 F9

K_F10 F10

K_F11 F11

K_F12 F12

K_NUMLOCK bloqnum

K_CAPSLOCK bloqmayus

K_RSHIFT shift derecha

K_LSHIFT shift izquierda

K_RCTRL ctrl derecha

K_LCTRL ctrl izquierda

K_RALT alt derecha

K_LALT alt izquierda


10.4 Mandos de Juegos
Los mandos de juegos requieren un conjunto de códigos
diferentes, pero la idea es sencilla.

Para empezar, verificamos si el ordenador tiene un


joystick. Luego, antes de usarlo, lo iniciamos. Solo
necesitamos hacer esto una sola vez. Hazlo después del
bucle principal del programa:
Inicializar el mando de juegos antes de su uso
# Posición actual

x_coord = 10

y_coord = 10

# Hacemos un recuento del número de joysticks conectados al ordenador

joystick_cuenta = pygame.joystick.get_count()

if joystick_cuenta == 0:

# No joysticks!

print ("Error, No he encontrado ningún joystick.")

else:

# Usa el joystick #0 y lo inicializa

mi_joystick = pygame.joystick.Joystick(0)

mi_joystick.init()

Un joystick devolverá dos valores reales. Si estuviera


perfectamente centrado, devolvería (0,0). Si estuviera
todo arriba y a la izquierda, devolvería (-1,-1). Si
estuviera todo abajo y a la derecha, devolvería (1,1). Si
se hallara en algún lugar intermedio, los valores se
escalarían proporcionalmente. Observa las imágenes
del mando de la Figura 10.8 para hacerte una idea de
cómo funciona esto.

Figure 10.8: Centro (0,0)

Figure 10.9: Arriba Izquierda (-1,-1)

Figure 10.10: Arriba (0,-1)

Figure 10.11: Arriba Derecha (1,-1)


Figure 10.12: Derecha (1,0)

Figure 10.13: Abajo Derecha (1,1)

Figure 10.14: Abajo (0,1)


Figure 10.15: Abajo Izquierda (-1,1)

Figure 10.16: Izquierda (-1,0)

Dentro del bucle principal del programa, los valores


devueltos por el joystick pueden ser multiplicados de
acuerdo a cuán lejos un objeto ha de moverse. En el caso
del código siguiente, mover por completo el joystick en
una dirección, lo hará a 10 píxeles por fotograma,
debido a que los valores del joystick son multiplicados
por 10.
Controlar un objeto vía un mando de juegos
# Esta parte va en el bucle principal del programa!

# Mientras exista un joystick

if joystick_cuenta != 0:

# Esto obtiene la posición del eje del mando

# Devuelve un número entre -1.0 y +1.0

horiz_axis_pos = mi_joystick.get_axis(0)

vert_axis_pos = mi_joystick.get_axis(1)
# Mueve x de acuerdo al eje. Multiplicamos por 10 para aumentar el movimiento.

# Convierte a entero debido a que no podemos dibujar un píxel en 3.5, solo o 3 o 4.

x_coord = x_coord + int(horiz_axis_pos * 10)

y_coord = y_coord + int(vert_axis_pos * 10)

# Limpia la pantalla

pantalla.fill(BLANCO)

# Dibuja el objeto en las coordenadas apropiadas

dibuja_hombrepalitos(pantalla, x_coord, y_coord)

El ejercicio completo lo tenemos en


ProgramArcadeGames.com/python_examples_es/f.php
?lang=es&file=move_game_controller.py.

Los mandos de los juegos poseen un montón de


joysticks y botones, incluso “gatillos”! A continuación se
puede ver una captura de pantalla y el código para
mostrar todo lo que hace un joystick en cada momento.
Ten en cuenta, que antes de ejecutar el programa,
tendrás que conectar el mando o no lo detectará.
Figure 10.17: Programa para Llamadas del Joystick

joystick_calls.py

1 """

# Sample Python/Pygame Programs


2
# Simpson College Computer Science
3 # http://programarcadegames.com/

4 # http://simpson.edu/computer-science/

"""
5
import pygame
6
# Definimos algunos colores
7 NEGRO = (0, 0, 0)

8 BLANCO = (255, 255, 255)

class TextPrint(object):
9
”'
10
Esta es una sencilla clase que nos ayudará a imprimir sobre la pantalla
11 No tiene nada que ver con los joysticks, tan solo imprime información

12 ”'

def __init__(self):
13
"""Constructor"""
14 self.reset()

15 self.x_pos = 10
16 self.y_pos = 10

self.font = pygame.font.Font(None, 20)


17
def print(self, mi_pantalla, text_string):
18 textBitmap = self.font.render(text_string, True, NEGRO)

19 mi_pantalla.blit(textBitmap, [self.x, self.y])

self.y += self.line_height
20
def reset(self):
21
self.x = 10
22 self.y = 10

23 self.line_height = 15

def indent(self):
24
self.x += 10
25 def unindent(self):
26 self.x -= 10

27 pygame.init()

# Establecemos el largo y alto de la pantalla [largo,alto]


28
dimensiones = [500, 700]
29 pantalla = pygame.display.set_mode(dimensiones)

30 pygame.display.set_caption("Mi Juego")

#Iteramos hasta que el usuario pulsa el botón de salir.


31
hecho = False
32
# Lo usamos para gestionar cuán rápido de refresca la pantalla.
33 reloj = pygame.time.Clock()

34 # Inicializa los joysticks

pygame.joystick.init()
35
# Se prepara para imprimir
36 text_print = TextPrint()

37 # -------- Bucle Principal del Programa -----------

while not hecho:


38
# PROCESAMIENTO DEL EVENTO
39
for evento in pygame.event.get():
40 if evento.type == pygame.QUIT:
41 hecho = True

# Acciones posibles del joystick: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUT


42
if evento.type == pygame.JOYBUTTONDOWN:
43 print("Botón presionado del joystick.")

44 if evento.type == pygame.JOYBUTTONUP:

print("Botón liberado del joystick.")


45
# DIBUJAMOS
46
# Primero, limpiamos la pantalla con color blanco. No pongas otros comandos de dib
47 # encima de esto, de lo contrario serán borrados por el comando siguiente.

48 pantalla.fill(BLANCO)

text_print.reset()
49
# Contamos el número de joysticks
50 joystick_count = pygame.joystick.get_count()
51 text_print.print(pantalla, "Número de joysticks: {}".format(joystick_count) )

52 text_print.indent()

# Para cada joystick:


53
for i in range(joystick_count):
54 joystick = pygame.joystick.Joystick(i)

55 joystick.init()

text_print.print(pantalla, "Joystick {}".format(i) )


56
text_print.indent()
57
# Obtiene el nombre del Sistema Operativo del controlador/joystick
58 nombre = joystick.get_name()

59 text_print.print(pantalla, "Nombre del joystick: {}".format(nombre))

# Habitualmente, los ejes van en pareja, arriba/abajo para uno, e izquierda/derech


60
# para el otro.
61 ejes = joystick.get_numaxes()

62 text_print.print(pantalla, "Número de ejes: {}".format(ejess))

text_print.indent()
63
for i in range(ejes):
64
eje = joystick.get_axis(i)
65 text_print.print(pantalla, "Eje {} valor: {:>6.3f}".format(i, eje))
66 text_print.unindent()

botones = joystick.get_numbuttons()
67
text_print.print(pantalla, "Número de botones: {}".format(botones))
68 text_print.indent()

69 for i in range(botones):

boton = joystick.get_button(i)
70
text_print.print(pantalla, "Botón {:>2} valor: {}".format(i,boton))
71
text_print.unindent()
72 # Hat switch. Todo o nada para la dirección, no como en los joysticks.

73 # El valor vuelve en un array.

hats = joystick.get_numhats()
74
text_print.print(pantalla, "Número de hats: {}".format(hats))
75 text_print.indent()
76 for i in range(hats):

77 hat = joystick.get_hat(i)

text_print.print(pantalla, "Hat {} valor: {}".format(i, str(hat)))


78
text_print.unindent()
79 text_print.unindent()

80 # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO

# Avanzamos y actualizamos la pantalla con lo que hemos dibujado.


81
pygame.display.flip()
82
# Limitamos a 60 fotogramas por segundo.
83 reloj.tick(60)

84 pygame.quit()

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

10.5 Repaso
10.5.1 Test

Haz click para ir al Test.

10.5.2 Ejercicios

Haz click para ir a los Ejercicios.

10.5.3 Taller

Haz click para ir al Taller.


Chapter 11: Gráficos de Mapas de
bits y Sonido
Si queremos ir más allá de las formas simples como
círculos o rectángulos, debemos dotar a nuestros
programas de la habilidad para trabajar con imágenes de
mapas de bits. Este tipo de imágenes pueden ser
fotografías o gráficos creados con algún programa de
dibujo.

Pero solo con las imágenes no es suficiente. Los juegos


necesitan también sonido! En este capítulo te
enseñaremos como incluir este tipo de archivos en tus
juegos.

11.1 Guardemos el Programa en


una Carpeta
Video: Organizando un proyecto en una carpeta

Hasta ahora, los programas que hemos hecho,


implicaban solo un archivo. Ahora que vamos a incluir
imágenes y sonidos, tenemos más archivos que serán
parte de nuestro programa. Es fácil mezclarlos con otros
programas que estemos desarrollando. La forma de
mantener todo organizado, es poner cada uno de esos
programas dentro de su respectiva carpeta. Antes de
comenzar cualquier nuevo proyecto, pulsa sobre
'Carpeta Nueva' y utilízala para poner en ella todos los
archivos, tal como se ve en la Figura 11.1.

Figure
11.1: Crear una carpeta nueva

11.2 Establecer Una Imagen de


Fondo
Vídeo: Organizar un proyecto dentro de una carpeta

¿Necesitas una imagen de fondo para tu juego?


Encuentra una como la de la Figura 11.2. Si estás
buscándola en Internet a través de tu navegador,
normalmente basta con hacer clic en el botón derecho
del ratón y seleccionar Guardar imagen como.... Guarda
la imagen en la carpeta que te acabas de crear para
nuestro juego.
Figure 11.2: Imagen de Fondo

Cualquier imagen usada en el juego debe previamente


haber sido dimensionada correctamente. No uses una
imagen de 5000x5000 píxeles, tomada con una cámara
de alta resolución, e intentes cargarla en una pantalla de
800x600. Utiliza algún programa de retoque de
imágenes (hasta el MSPaint serviría) para
redimensionar y/o cortar la imagen antes de usarla en tu
programa Python.

Cargar una imagen es un proceso sencillo que requiere


de una sola línea de código. Pero lo que contiene esa
línea de código necesita de una explicación que
desarrollaremos en tres partes. La primera versión de
nuestro comando load, cargará un archivo
llamado saturn_family1.jpg. Este archivo debe encontrarse en
la misma carpeta donde se aloja nuestro programa, de
otra forma el ordenador no lo encontrará.
pygame.image.load("saturn_family1.jpg")

Respeta las imágenes que posean derechos de autor


(copyright). Publicar imágenes sobre las cuales no tienes
derechos es una acción ilegal. Si lo que estás haciendo es
un trabajo que no vas a compartir, entonces no hay
problema. Si estás haciendo un trabajo de clase, consulta
con tu profesora o profesor acerca de su política sobre
este asunto.
Para trabajos en clase, yo sugiero añadir un comentario
en el programa, justamente antes de cargar la imagen.
Indica de dónde viene la imagen, tal como hicimos en el
ejemplo anterior (No pongas a Google como fuente, eso
es como usar “biblioteca” como tu fuente bibliográfica.
Encuentra la página web original de la imagen.)

Este código cargará la imagen, pero todavía no hay


forma de referenciarla y mostrarla! Necesitamos una
variable que reciba lo que el comando load() devuelva. En
la siguiente versión, creamos una nueva variable
llamada imagen_defondo.
imagen_defondo = pygame.image.load("saturn_family1.jpg")

Por último, necesitamos que la imagen sea convertida a


un formato con el que Pygame pueda trabajar
fácilmente. Para ello añadimos .convert() a la función que
carga la imagen. La función .convert() es un método de la
clase Image. Hablaremos más sobre clases, objetos y
métodos en el Capítulo 12.

Todas las imágenes deberían cargarse usando un código


similar al de la siguiente línea. Basta con ajustar el
nombre de la variable y el del archivo a nuestro caso.
imagen_defondo = pygame.image.load("saturn_family1.jpg").convert()
La carga de la imagen debería hacerse antes del bucle
principal del programa. Aunque es posible hacerlo
dentro, esto provocaría que el programa extrajera la
imagen del disco veinte o más veces por segundo. Algo
totalmente innecesario. Basta con hacerlo una vez al
iniciarse el programa.

Para mostrar la imagen en pantalla usamos el


comando blit. Éste “vuelca” la imagen en pantalla. Ya lo
hemos usado previamente en el Capítulo 5 cuando
quisimos mostrar texto en la pantalla de juego.

El comando blit es un método de la variable pantalla. Por


ello, debemos inicializar nuestro comando
por pantalla.blit. Después, tenemos que pasar la imagen a
volcar, y dónde hacerlo. Este comando debe ir dentro del
bucle principal, de forma que se dibuje en cada
fotograma:
pantalla.blit(imagen_defondo, [0, 0])

Este código vuelca la imagen residente


en imagen_defondo sobre la pantalla empezando en las
coordenadas (0, 0).

11.3 Mover una Imagen


Video: Mover una Imagen
Bien, ahora queremos cargar la imagen y moverla
alrededor de la pantalla. Empezaremos con un sencilla
nave espacial de color naranja. Esta imagen y otras igual
de buenas puedes obtenerlas de http://kenney.nl/. Ver la
Figura 11.3. La imagen de la nave puede descargarse
desde la página web del libro, o también puedes escoger
otras imágenes en formato .gif o .png, con fondo ya sea
blanco o negro. No vayas a usar imágenes con formato
.jpg.

Figure 11.3: Imagen del jugador

Para cargar esta imagen necesitamos el mismo tipo de


comandos que usamos con la imagen de fondo. En este
caso asumimos que el archivo se ha guardado
como jugador.png.
imagen_protagonista = pygame.image.load("jugador.png").convert()

En el interior del bucle principal, las coordenadas del


ratón son extraídas y luego pasadas a otra
función blit como las coordenadas donde se debe dibujar
la imagen:
#Obtiene la posición actual del ratón, devolviéndola como

#una lista con dos números.

posicion_jugador = pygame.mouse.get_pos()
x = posicion_jugador[0]

y = posicion_jugador[1]

# Vuelca la imagen en la pantalla:

pantalla.blit(imagen_protagonista, [x, y])

Vaya, tenemos un problema. La imagen es una nave


espacial con un fondo negro fijo. Así que cuando la
imagen se dibuja, el programa muestra la siguiente
Figura 11.4.

Lo que queremos es solo una nave espacial y no una


nave con un fondo rectangular! Pero todas las imágenes
que podemos cargar son rectangulares. Entonces ¿cómo
hacemos para visualizar solo la parte de la imagen que
nos interesa? La forma de conseguirlo es decirle al
programa que haga “transparente” uno de los colores,
de forma que desaparezca. Esto se puede hacer
inmediatamente después de la carga. La siguiente línea
convierte el color negro (asumiendo que NEGRO es una
variable ya definida) en transparente:
imagen_protagonista.set_colorkey(NEGRO)
Esto debería funcionar con la mayoría de archivos del
tipo .gif o .png. En el caso de archivos .jpg, lo más
probable es que no sea así. El formato .jpg es fantástico
para contener fotografías, pero la imagen es modificada
ligeramente por el algoritmo usado para reducir su
tamaño. Las imágenes del tipo .gif y .png también han
sido comprimidas, pero el algoritmo de compresión
usado en este caso no modifica la imagen original. El
formato .bmp no comprime en absoluto las imágenes,
con el resultado de acabar con archivos de un tamaño
enorme. Debido a que el formato .jpg modifica la
imagen, no todo el color de fondo será el mismo. En la
Figura 11.5 se puede ver una nave espacial guardada en
formato jpeg con fondo blanco. El blanco alrededor de
la nave no es exactamente (255, 255, 255), pero se le
aproxima bastante.

Figure 11.5: Defectos en la Compresión JPEG


Si vas a elegir una imagen que luego sea transparente,
escoge los formatos .gif o .png. Son los mejores formatos
para artes gráficas. Las fotos, por contra, deberían ser
.jpg. Ten en cuenta que no basta con cambiar la
extensión de un archivo .jpg para convertirlo, por
ejemplo en .png. Siempre será .jpg le pongas el nombre
que le pongas. Hace falta usar un programa gráfico para
convertir entre los distintos formatos. Pero esto no
solucionará las alteraciones que haya sufrido la imagen.

Aquí tienes tres buenos sitios donde buscar imágenes


gratuitas para tus programas:
Kenney.nl
OpenGameArt.Org
HasGraphics.com

11.4 Sonidos
Video: Sonidos

En esta sección reproduciremos el sonido de un láser


cuando pulsemos el ratón. El sonido lo obtuvimos
previamente de Kenney.nl. Puedes descargarlo también
desde:
ProgramArcadeGames.com/python_examples/laser5.o
gg
Al igual que sucede con las imágenes, debemos cargar
los sonidos antes de su uso. Esto puede hacerse en
cualquier punto antes del bucle principal. La siguiente
línea carga un archivo de sonido y crea una variable
llamada pulsar_sonido que lo referencia:
pulsar_sonido = pygame.mixer.Sound("laser5.ogg")

Podemos oír el sonido usando el comando siguiente:


pulsar_sonido.play()

¿Pero dónde colocamos esta línea? Si lo hacemos dentro


del bucle principal, se ejecutará 20 veces por segundo.
¡Desesperante! Necesitamos una especie de
“disparador”. Si ocurre cierta acción, entonces, se
ejecuta el sonido. Por ejemplo, usando el siguiente
código, el sonido se escuchará cuando el usuario
presione el ratón:
for evento in pygame.event.get():

if evento.type == pygame.QUIT:

hecho = True

elif evento.type == pygame.MOUSEBUTTONDOWN:

pulsar_sonido.play()

Los archivos de audio no comprimidos, habitualmente


terminan en la extensión .wav. Estos archivos son más
grandes que los de otros formatos, debido a que no se
les ha pasado ningún algoritmo que reduzca su tamaño.
También tenemos el popular .mp3, pero este formato
posee unas patentes que lo convierten en poco
recomendable para ciertas aplicaciones. Otro formato,
gratuito, es el OGG Vorbis que termina en la
extensión .ogg.

Pygame no reproduce todos los archivos .wav que puedas


encontrarte en Internet. Si ves que no se reproducen,
puedes intentar convertirlos mediante el
programa Audacity en un archivo ogg-vorbis, con
extensión .ogg. Este formato ocupa poco espacio y es
compatible con Pygame.

Si lo que quieres es una música de fondo para tu


programa, puedes mirar el siguiente ejemplo:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=background_music.py

Por favor, recuerda que no puedes redistribuir música


con copyright en tus programas. Si haces un vídeo con
este tipo de música de fondo, algunos sitios como
Youtube y otros similares, lo marcarán como una
violación del copyright.

Puedes encontrar sonidos gratuitos para tus programas


en las siguientes páginas:
OpenGameArt.Org
www.freesound.org

11.5 Listado Completo


bitmapped_graphics.py
1 """

2 Sample Python/Pygame Programs

3 Simpson College Computer Science

4 http://programarcadegames.com/

5 http://simpson.edu/computer-science/

6 Explanation video: http://youtu.be/4YqIKncMJNs


Explanation video: http://youtu.be/ONAK8VZIcI4
7
Explanation video: http://youtu.be/_6c4o41BIms
8
"""
9
import pygame
10
# Definimos algunos colores
11
BLANCO = (255, 255, 255)
12
NEGRO = (0, 0, 0)
13
# Inicializamos
14
pygame.init()
15
# Creamos una pantalla de 800x600.
16 pantalla = pygame.display.set_mode([800, 600])
17 # Establecemos el nombre de la ventana.
18 pygame.display.set_caption('CMSC 150 es divertido')
19 reloj = pygame.time.Clock()
20 # Antes del bucle cargamos el sonido:
21 sonido_click = pygame.mixer.Sound("laser5.ogg")

22 # Establecemos la posición de los gráficos


23 posicion_base = [0, 0]

24 # Carga y sitúa los gráficos.

25 imagen_de_fondo = pygame.image.load("saturn_family1.jpg").convert()

26 imagen_personaje = pygame.image.load("playerShip1_orange.png").convert
imagen_personaje.set_colorkey(NEGRO)
27
hecho = False
28
while not hecho:
29
reloj.tick(10)
30
for evento in pygame.event.get():
31
if evento.type == pygame.QUIT:
32
hecho = True
33
elif evento.type == pygame.MOUSEBUTTONDOWN:
34
sonido_click.play()
35
# Copia la imagen en pantalla:
36
pantalla.blit(imagen_de_fondo, posicion_base)
37 # Obtiene la posición actual del ratón. Devuelve ésta como
38 # una lista de dos números.
39 posicion_del_personaje = pygame.mouse.get_pos()
40 x = posicion_del_personaje[0]
41 y = posicion_del_personaje[1]
42 # Copia la imagen en pantalla:

43 pantalla.blit(imagen_personaje, [x, y])

44 pygame.display.flip()

45 reloj.tick(60)

46 pygame.quit()

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

11.6 Repaso
11.6.1 Test

Haz click para ir al Test.

11.6.2 Ejercicios

Haz click para ir a los Ejercicios.

11.6.3 Taller

Haz click para ir al Taller.


Chapter 12: Introducción a las
Clases
Las clases y los objetos son poderosas herramientas de
programación. Facilitan la tarea de programar. De
hecho, ya estás familiarizado con el concepto de clases y
objetos. Una clase es la “clasificación” de un objeto. Tal
como “persona” o “imagen.” Un objeto es una instancia
particular de una clase. Tal como “María” es una
instancia de “Persona.”

Los objetos poseen atributos, tales como el nombre,


altura y edad de una persona. Los objetos también
poseen métodos. Los métodos definen qué acciones
pueden realizar los objetos, tales como correr, saltar o
sentarse.

12.1 ¿Por Qué Estudiar Clases?


Vídeo: Por qué Clases?

Cada personaje en un juego de aventuras necesita datos:


un nombre, ubicación, fuerza, está levantando el brazo,
en qué dirección se dirige, etc. Además, esos
personajes hacen cosas. Corren, saltan, golpean y
hablan.
Si no empleamos clases, nuestro código Python para
guardar todo esto tendría este aspecto:

1 nombre = "Link"
2 sexo = "Varón"

3 golpe_puntos_max = 50

golpe_puntos_actuales = 50
4

Para poder hacer cualquier cosa con este personaje,


necesitaremos pasar esos datos a una función:

1 def mostrar_personaje(nombre, sexo, golpe_puntos_max, golpe_puntos_actuales):

2 print(nombre, sexo, golpe_puntos_max, golpe_puntos_actuales)

Ahora imagínate crear un programa que tenga un


conjunto de variables como ésta por cada personaje,
monstruo y/o objeto de tu juego. Entonces, necesitamos
crear funciones que trabajen con todos ellos. De repente,
parece que nos hemos metido en un berenjenal de datos,
y ya nada de esto parece divertido.

¡Pero espera, que esto empeora aún más! A medida que


nuestro juego se expanda, necesitaremos agregar
nuevos campos que describan a nuestro personaje. Por
ejemplo, en este caso hemos agregado velocidad_max:
nombre = "Link"
1
sexo = "Varón"
2 golpe_puntos_max = 50

golpe_puntos_actuales = 50
3
velocidad_max = 10
4 def mostrar_personaje(nombre, sexo, golpe_puntos_max, golpe_puntos_actuales,velocida

5 print(nombre, sexo, golpe_puntos_max, golpe_puntos_actuales)

6
7
8

En el ejemplo anterior solo hay una función. Pero en un


videojuego grande, podríamos tener cientos de
funciones que trataran con el personaje principal.
Añadir un nuevo campo que ayude a describir lo que
tiene y hace un personaje, requeriría ir una por una de
las funciones, y añadirlo a su lista de parámetros. Eso
significaría un montón de trabajo. Y, a lo mejor,
necesitamos añadir velocidad_max a otros tipos de personajes
tales como monstruos. Debe existir una forma más
eficaz de hacer esto. De alguna manera, nuestro
programa necesita empaquetar todos esos campos de
datos y así poder manejarlos más fácilmente.

12.2 Definir y Crear Clases


Simples
Vídeo: Definir Clases Simples
Una forma eficaz de manejar múltiples atributos es
definir una estructura que contenga toda esa
información. Podemos darle un nombre a ese
“agrupamiento”, algo así como Personaje o Dirección.
Esto se puede hacer fácilmente en Python, y en
cualquier otro lenguaje moderno, usando una clase.

Por ejemplo, podemos definir una clase que represente


un personaje del juego:

1
2 class Personaje():

""" Esta es una clase que representa al protagonista principal del juego. """
3
def __init__(self):
4 """ Este es un método que establece las variables del objeto. """
5 self.name = "Luis"

6 self.sexo = "Varón"

self.puntos_impacto_max = 50
7
self.puntos_impacto_actuales = 50
8 self.vel_max = 10

9 self.cantidad_escudos = 8

10

Este es otro ejemplo, definimos una clase que contenga


todos los campos para una dirección:
Define an address class

1 class Direccion():

2 """ Contiene todos los campos para una dirección postal. """
3 def __init__(self):

""" Establece los campos de la dirección. """


4
self.nombre = ""
5 self.via1 = ""

6 self.via2 = ""

self.ciudad = ""
7
self.provincia = ""
8
self.cod_postal = ""
9
10

En el código anterior, Direccion es el nombre de la clase. Las


variables en la clase, como nombre y ciudad, son conocidas
como atributos o campos. (Observa las similitudes y
diferencias entre declarar una clase y una función.)

Al contrario que con las funciones y variables, los


nombres de las clases deberían empezar con una letra
mayúscula. Aunque es totalmente posible hacerlo con
minúsculas, no se considera una buena práctica.

La expresión def __init__(self): es una función especial


llamada constructor la cual se ejecuta automáticamente
cuando la clase es creada. Hablaremos más del
constructor en un momento

El self. es algo así como el pronombre mi. Cuando se


encuentra dentro de la clase Direccion, estamos hablando
de mi nombre, de mi ciudad, etc. No debemos usar self.,
fuera de la definición de la clase Direccion, para referirnos
a un campo de Direccion ¿Por qué? Porque como pasa con
el pronombre “mi”, éste tiene un significado totalmente
distinto cuando es dicho por otra persona!

Para visualizar mejor las clases y cómo se relacionan, los


programadores usan habitualmente diagramas. En la
Figura 12.1 podemos ver un diagrama para la
clase Direccion. Observa cómo el nombre de la clase se sitúa
por encima del nombre de los atributos. A la derecha de
éstos se encuentra el tipo de dato que contienen, tales
como cadenas de textos (strings) o números enteros
(integer).

Figure 12.1: Diagrama de Clase

El código de la clase la define como tal, pero realmente


no crea una instancia de ella. El código le dice al
ordenador qué campos tiene una dirección y cuáles
serán sus valores iniciales. Pero aún no tenemos una
dirección real. Podemos definir una clase sin crearla, de
la misma forma que podemos definir una función sin
llegar a llamarla. Para crear una clase y establecer sus
campos, observa el siguiente ejemplo:
Crear una instancia de la clase dirección

1
2
# Crea una dirección
3 casa_direccion = Direccion()

4 # Establece los campos de la dirección

casa_direccion.nombre = "John Smith"


5
casa_direccion.via1 = "701 N. C Street"
6 casa_direccion.via2 = "Carver Science Building"

7 casa_direccion.ciudad = "Indianola"

casa_direccion.provincia = "IA"
8
casa_direccion.cod_postal = "50125"
9
10

En la línea 2 se crea una instancia para la clase dirección.


Observa cómo usamos el nombre de
clase Direccion seguido de paréntesis. El nombre de la
variable puede ser cualquiera que siga las reglas
habituales.

Para establecer los campos de la clase, el programa debe


usar el operador punto. Este operador es el punto que está
en medio de casa_direccion y el nombre del campo. Observa
cómo hacemos lo mismo para las líneas de la 5 a la 10.

Un problema muy común cuando trabajamos con clases,


es no especificar con qué instancia de la clase queremos
hacerlo. Si solo se ha creado una dirección, es
comprensible asumir que el ordenador sabrá cómo usar
la dirección de la que estás hablando. Sin embargo, esto
no siempre es así. Observa el siguiente ejemplo:

1
2
class Direccion():
3
def __init__(self):
4 self.nombre = ""

5 self.via1 = ""

self.via2 = ""
6
self.ciudad = ""
7 self.provincia = ""
8 self.cod_postal = ""

9 # Crea una dirección

mi_direccion = Direccion()
10
# Cuidado! Esto no establece el nombre para la dirección!
11 nombre = "Dr. Craven"

12 # Esto tampoco

Direccion.nombre = "Dr. Craven"


13
# Esto sí está bien:
14
mi_direccion.nombre = "Dr. Craven"
15
16
17
18
19
20

Podemos crear una segunda dirección y usar los campos


de ambas instancias. Mira el siguiente ejemplo:
Trabajar con dos instancias de dirección

1 class Direccion():

2 def __init__(self):

self.nombre = ""
3
self.via1 = ""
4 self.via2 = ""
5 self.ciudad = ""

self.provincia = ""
6
self.cod_postal = ""
7
# Crea una dirección
8 casa_direccion = Direccion()

9 # Establece los campos de la dirección

casa_direccion.nombre = "John Smith"


10
casa_direccion.via1 = "701 N. C Street"
11
casa_direccion.via2 = "Carver Science Building"
12 casa_direccion.ciudad = "Indianola"

13 casa_direccion.provincia = "IA"

casa_direccion.cod_postal = "50125"
14
# Crea otra dirección
15 casa_vacaciones_direccion = Direccion()

16 #Establece los campos de la nueva dirección

casa_vacaciones_direccion.nombre = "John Smith"


17
18 casa_vacaciones_direccion.via1 = "1122 Main Street"

casa_vacaciones_direccion.via2 = ""
19
casa_vacaciones_direccion.ciudad = "Panama City Beach"
20 casa_vacaciones_direccion.provincia = "FL"

21 casa_vacaciones_direccion.cod_postal = "32407"

print("La dirección principal del cliente está en " + casa_direccion.ciudad)


22
print("Su casa de vacaciones está en " + casa_vacaciones_direccion.ciudad)
23
24
25
26
27
28
29
30
31
32
33

La línea 11 crea la primera instancia para Direccion; la línea


22 crea la segunda. La variable casa_direccion apunta a la
primera instancia y casa_vacaciones_direccion apunta a la
segunda.

Las líneas de la 25-30 establecen los campos para esta


nueva instancia de clase. La línea 32 imprime la ciudad
donde está la vivienda habitual,
porque casa_direccion aparece antes del operador punto. La
línea 33 imprime su casa de vacaciones debido a
que casa_vacaciones_direccion aparece antes del operador
punto.

En este ejemplo, decimos que Direccion es la clase, ya que


define una nueva clasificación para un objeto de datos.
Las variables casa_direccion y casa_vacaciones_direccion aluden a
objetos, porque se refieren a instancias reales de la
clase Direccion. Básicamente, diríamos que un objeto es una
instancia de una determinada clase. De la misma forma
que “Bob” y “Nancy” son instancias de la clase
Humana.

Podemos visualizar la ejecución del siguiente código


utilizando www.pythontutor.com. Existen tres
variables en juego. Una apunta a la definición de la
clase Direccion. Las otras dos apuntan a diferentes objetos
de direcciones y a los datos contenidos en ellos.
Figure 12.2: Dos Direcciones
Colocar muchos campos de datos dentro de una clase,
facilita el tránsito de estos dentro y fuera de una función.
En el siguiente código, la función toma una dirección
como parámetro y lo imprime en pantalla. No es
necesario pasar un parámetro para cada campo de la
dirección.

31
32
# Imprime una dirección en pantalla
33
def imprimir_direccion(direccion):
34
print(direccion.nombre)
35 # Si existe una via1 en esa dirección, imprímela

36 if( len(direccion.via1) > 0 ):

print (direccion.via1)
37
# Si existe una via2 en esa dirección, imprímela
38 if( len(direccion.via2) > 0 ):

39 print( direccion.via2 )

print( direccion.ciudad+", "+direccion.provincia+" "+direccion.cod_postal )


40
imprimir_direccion( casa_direccion )
41
print()
42 imprimir_direccion( casa_vacaciones_direccion )

43
44

12.3 Añadir Métodos a las Clases


Vídeo: Métodos
Además de poseer atributos, las clases también pueden
tener métodos. Un método es una función que existe
dentro de una clase. Ampliando un ejemplo anterior,
definimos la clase Perroy le añadimos el código para que
ladre:

1
2 class Perro():

3 def __init__(self):

self.edad = 0
4
self.nombre = ""
5 self.peso = 0

6 def ladra(self):

print("Guau")
7
8

Entre las líneas 7-8 está contenida la definición del


método. Las definiciones de métodos en una clase, son
casi idénticas a las definiciones de las funciones. La
mayor diferencia es la inclusión del parámetro selfen la
línea 7. El primer parámetro, en cualquier método de
una clase, debe ser self. Este parámetro es
imprescindible, incluso aunque la función no haga uso
de él.

Estas son las ideas que tenemos que tener en mente


cuando creamos métodos para clases:
• Los atributos deben ir primero, los métodos
después.
• El primer parámetro, en cualquier método, debe
ser self.
• Las definiciones de métodos deben ir indentadas
exactamente una tabulación.

Podemos invocar a los métodos de manera similar a


cómo referenciamos atributos de un objeto. Observa el
siguiente código:

1
2
mi_perro = Perro()
3 mi_perro.nombre = "Spot"

4 mi_perro.peso = 20

mi_perro.edad = 3
5
mi_perro.ladra()
6
7

La línea 1 crea al perro. Las líneas 3-5 establecen los


atributos de los objetos. La línea 7 llama a la función ladra.
Observa que a pesar de que la función ladra tiene un
parámetro self, al llamarla no le pasamos nada. Esto se
debe a que se asume que el primer parámetro es una
referencia al objeto perro en sí mismo. Entre bastidores
Python realiza una llamada que tiene este aspecto:
# Ejemplo, no es legal realmente

Perro.ladra(mi_perro)

Si la función ladra necesita hacer referencia a cualquiera


de los atributos, lo hace utilizando la variable de
referencia self. Por ejemplo, podemos modificar la
clase Perro, de forma que cuando el perro ladre, también
imprima su nombre. En el siguiente código, se accede al
atributo nombre usando el operador punto y la
referencia self.

6 def ladra(self):

7 print( "dice Guau", self.nombre )

Los atributos son adjetivos y los métodos, verbos. El


esquema de una clase podría ser como el de la
Figura 12.3.

Figure 12.3: Clase Perro


12.3.1 Ejemplo: Clase Pelota

Podemos usar este ejemplo en Python/Pygame para


dibujar una pelota. El tener todos los parámetros
contenidos dentro de una clase, nos facilita el manejo de
los datos. El diagrama para la clase Pelota se puede ver en
la Figura 12.4.

Figure 12.4: Clase Pelota

1 class Pelota():

def __init__(self):
2
# --- Atributos de la Clase ---
3 # Posición de la pelota
4 self.x = 0

5 self.y = 0

# vector Pelota
6
self.cambio_x = 0
7 self.cambio_y = 0

8 # Dimensiones de la Pelota
9 self.talla = 10

# color de la Pelota
10
self.color = [255,255,255]
11 # --- Métodos para la Clase ---

12 def mover(self):

self.x += self.cambio_x
13
self.y += self.cambio_y
14
def dibujar(self, pantalla):
15 pygame.draw.circle(pantalla, self.color, [self.x, self.y], self.talla )

16
17
18
19
20
21
22
23
24

El siguiente código, que creará y establecerá los


atributos de la pelota, irá antes del bucle principal:
laPelota = Pelota()

laPelota.x = 100

laPelota.y = 100

laPelota.cambio_x = 2

laPelota.cambio_y = 1

laPelota.color = [255,0,0]
El siguiente código, que dibujará y moverá la pelota, irá
dentro del bucle principal:
laPelota.mover()

laPelota.dibujar(pantalla)

12.4 Referencias
Vídeo: Referencias

Llegamos al punto donde se separan los verdaderos


programadores de los aspirantes a serlo: la comprensión
de las referencias de clases. Observa el siguiente código:

1
2
3
class Persona:
4
def __init__(self):
5 self.nombre = ""

6 self.dinero = 0

bob = Persona()
7
bob.nombre = "Bob"
8 bob.dinero = 100

9 nancy = Persona()

nancy.nombre = "Nancy"
10
print(bob.nombre, "tiene", bob.dinero, "dólares.")
11
print(nancy.nombre, "tiene", nancy.dinero, "dólares.")
12
13
14
El código anterior crea dos instancias para la clase
Persona(). Podemos visualizar las dos clases
usando www.pythontutor.com Figura 12.5.

Figure 12.5: Dos


Personas

El anterior código no tiene nada nuevo, pero el siguiente


sí:
class Persona:
1
def __init__(self):
2
self.nombre = ""
3 self.dinero = 0

4 bob = Persona()

bob.nombre = "Bob"
5
bob.dinero = 100
6
nancy = bob
7 nancy.nombre = "Nancy"
8 print(bob.nombre, "tiene", bob.dinero, "dólares.")

print(nancy.nombre, "tiene", nancy.dinero, "dólares.")


9
10
11
12
13
14

Ves la diferencia en la línea 10?

Un error muy común al trabajar con objetos, es asumir


que la variable bob es el objeto Persona. Pero no es así. La
variable bob es una referencia al objeto Persona. Es decir,
guarda la dirección de memoria donde se encuentra el
objeto, no el objeto en sí.

Si realmente bob fuera el objeto, entonces, la línea 9


podría crear una copia del objeto, con lo que tendríamos
dos objetos en existencias. En principio, la salida del
programa debería mostrar que tanto Bob como Nancy
tienen 100 dólares. Pero cuando realmente ejecutamos el
programa, nos encontramos con esta otra salida en su
lugar:

Nancy tiene 100 dólares.


Nancy tiene 100 dólares.
Lo que almacena bob es una referencia al objeto. En lugar
de usar el término referencia, uno podría llamarlo
también; dirección, puntero, o handle (manija). Una
referencia es una dirección en la memoria del
ordenador. Un lugar donde es almacenado el objeto.
Esta dirección es un número hexadecimal, que si lo
imprimiéramos, tendría un aspecto similar a 0x1e504. Al
ejecutarse la línea 9, lo que realmente se copia es la
dirección y no el objeto al que apunta esta dirección.
Observa la Figura 12.6.

Figure 12.6: Referencias a Clases

También podemos ejecutar este código


en www.pythontutor.com para observar cómo ambas
variables apuntan al mismo objeto.
Figure 12.7: Una
Persona, Dos Punteros

12.4.1 Funciones y Referencias

Observa el siguiente código. En la línea 1 se crea una


función que toma un número como parámetro. La
variable dinero contiene una copia del número que le han
pasado a la función. Sumarle 100 a ese número, no
cambia el valor almacenado por bob.dinero en la línea 11.
Por ello, la sentencia print en la línea 14, imprime 100 en
lugar de 200.
def dameDinero1(dinero):
1
dinero += 100
2
class Persona():
3 def __init__(self):
4 self.nombre = ""

self.dinero = 0
5
bob = Persona()
6 bob.nombre = "Bob"

7 bob.dinero = 100

dameDinero1(bob.dinero)
8
print(bob.dinero)
9
10
11
12
13
14

Si ejecutamos esto en PythonTutor, podremos observar


que existen dos instancias para la variable dinero. Una de
ellas es una copia y, además, local a la función dameDinero1.
Figure 12.8:
Referencias a Funciones

Observa el siguiente código adicional. Este código


provoca que bob.dinero se incremente y que la sentencia
print escriba 200.

14
def dameDinero2(persona):
15
persona.dinero += 100
16
dameDinero2(bob)
17 print(bob.dinero)

18

¿Por qué ocurre esto? Pues esto se debe a


que persona contiene una copia de la dirección de memoria
del objeto, y no el objeto en sí mismo. Pensemos en ello
como si se tratase del número de una cuenta bancaria.
La función tiene una copia de ese número de cuenta, no
una copia de todos nuestros depósitos. Por ello, usando
una copia del número de cuenta para depositar 100
dólares, conseguimos que el balance bancario de Bob
aumente.

Figure 12.9:
Referencias a Funciones

Los arrays funcionan de la misma forma. Una función


que tome un array (lista) como parámetro y modifique
los valores del mismo, estará modificando el mismo
array que el propio código ha creado. Lo que se copia es
la dirección del array, no el array entero.
12.4.2 Preguntas de Repaso
1. Crea una clase llamada Gato. Otórgale atributos
tales como nombre, color, y peso. Dale un método
llamado miau.
2. Crea una instancia de la clase gato, completa sus
atributos y llama al método miau.
3. Crea una clase llamada Monstruo. Dale un atributo
para nombre y un atributo entero (int) para
resistencia. Crea un método
llamado reducirResistencia que tome un
parámetro cantidad y reduzca en esa cantidad la
resistencia de nuestro monstruo. Dentro de ese
método se debe imprimir que el animal ha muerto
si su resistencia está por debajo de cero.

12.5 Constructores
Vídeo: Constructores

Tenemos un terrible problema con la siguiente clase Perro.


Por defecto, cuando creamos el perro, éste no tiene
nombre. Todos sabemos que los perros deben tener un
nombre. No podemos permitir que nazcan perros a los
que nunca se les asigne un nombre. Pero el código de
abajo permite esto, y ese perro nunca tendrá un nombre.
class Perro()

def __init__(self):

self.nombre = ""
mi_perro = Perro()

Python no quiere que esto suceda. Por ello, en Python,


las clases tienen una función especial que es llamada en
el momento en que una instancia de esa clase es creada.
Añadiendo esa función, llamada constructor, el
programador puede añadir el código necesario, el cual,
automáticamente, será ejecutado cada vez que una
instancia de la clase sea creada. Observa el siguiente
ejemplo:
Ejemplo de una clase con un constructor

1
2 class Perro():

3 def __init__(self):

# Llamada al constructor cuando creamos un objeto de este tipo


4
self.nombre = ""
5 print("Ha nacido un perro nuevo!")

6 # Esto crea al perro

mi_perro = Perro()
7
8

El constructor empieza en la línea 2. Debe ser nombrado


como __init__. Hay dos guiones bajos antes y después de
la palabra init. Un error muy común es usar uno solo.

El constructor debe tomar self como primer parámetro,


tal como sucede con otros métodos en una clase.
Cuando el programa es ejecutado, imprimirá:
Ha nacido un perro nuevo!

Cuando el objeto Perro es creado en la línea 8, la


función __init__ es llamada automáticamente y el mensaje
aparece en pantalla.

12.5.1 Evitar este Error

Todo lo necesario para un método lo colocamos dentro


de una sola definición. No lo definimos dos veces. Por
ejemplo:

1
2 # Mal:

3 class Perro():

def __init__(self):
4
self.edad = 0
5
self.nombre = ""
6 self.peso = 0

7 def __init__(self):

print("¡Un perro nuevo!")


8
9

El ordenador sencillamente ignorará el primer __init__ y


se irá a la última definición. Haz esto en su lugar:

1 # Bien:

2 class Perro():
3 def __init__(self):

self.edad = 0
4
self.nombre = ""
5 self.peso = 0

6 print("¡Un perro nuevo!")

Podemos utilizar un constructor para inicializar y


establecer los datos de un objeto. El ejemplo anterior de
la clase Perro permitía que el atributo nombre permaneciera
en blanco aún después de la creación del objeto perro.
¿Cómo podemos impedir esto? Muchos objetos
necesitan tener valores en el momento preciso de ser
creados. Podemos usar la función constructor para
lograrlo. Observa el siguiente código:
Constructor que toma un dato para inicializar la clase

1
class Perro():
2
def __init__(self, nombre_nuevo):
3 """Constructor"""

4 self.nombre = nombre_nuevo

# Esto crea al perro


5
mi_perro = Perro("Spot")
6 # Imprime el nombre para verificar que así ha sido

7 print(mi_perro.nombre)

# Esta línea producirá un error porque


8
# el nombre no ha sido introducido.
9
su_perro = Perro()
10
11
12
13
14
15

Ahora, en la línea 3, la función constructor tiene un


nuevo parámetro llamado nombre_nuevo. El valor de éste es
usado para establecer el atributo nombre para la
clase Perro en la línea 8. Ya no será posible crear una clase
Perro que no tenga un nombre. En la línea 15 se intenta
esto mismo. Se produce un error en Python y el código
no se ejecuta. Un error común es nombrar al parámetro
de la función __init__de la misma forma que al atributo, y
asumir que esos dos valores se sincronizarán
automáticamente. Esto no sucederá.

12.5.2 Preguntas de Repaso


1. ¿Cómo debería comenzar el nombre de una clase;
por mayúsculas o minúsculas?
2. ¿Cómo debería comenzar el nombre de un
método; por mayúsculas o minúsculas?
3. ¿Cómo debería comenzar el nombre de un
atributo; por mayúsculas o minúsculas?
4. ¿Qué es lo que deberíamos listar primero en un
clase; atributos o métodos?
5. ¿De qué otra forma podemos llamar a una
referencia?
6. ¿De qué otra forma podemos llamar a una variable
de instancia?
7. ¿Cuál es el nombre para la instancia de una clase?
8. ¿Crea una clase llamada Estrella que imprima “Ha
nacido una estrella!” cada vez que es creada.
9. Crea una clase llamada Monstruo con atributos para
resistencia y nombre. Añádele un constructor que
establezca la resistencia y nombre del objeto, con
datos que se pasen como parámetros.

12.6 Herencia
Vídeo: Herencia

Otro rasgo poderoso de las clases y objetos es la


posibilidad de hacer uso de la herencia. Es posible crear
una clase que herede todos los atributos y métodos de
una clase padre.

Por ejemplo, un programa podría crear una clase


llamada Barco que tenga todos los atributos necesarios
para dibujar un barco en un juego:
Definición de clase para barco
class Barco():
1
def __init__(self):
2 self.tonelaje = 0
3 self.nombre = ""

self.esta_atracado = True
4
def atracar(self):
5 if self.esta_atracado:

6 print("Ya has atracado.")

else:
7
self.esta_atracado = True
8
print("Atracando")
9 def desatracar(self):

10 if not self.esta_atracado:

print("No estás atracado.")


11
else:
12 self.esta_atracado = False
13 print("Desatracando")

14
15
16
17
18
19

Para comprobar nuestro código:


Estado de atraque de nuestro barco
b = Barco()
1 b.atracar()
2 b.desatracar()

3 b.desatracar()

b.atracar()
4
b.atracar()
5
6
7

Las salidas:

Ya has atracado.
Desatracando.
No estás atracado.
Atracando
Ya has atracado.

(Si miras el vídeo de esta sección, notarás que la clase


"Barco" no se ejecuta. El código anterior ya ha sido
corregido, cosa que no he hecho con el vídeo.
Recuérdalo, no importa lo simple que parezca el código,
ni cuán experimentado te consideres, comprueba tu
código antes de entregarlo!)

Nuestro programa necesita también de un submarino.


Nuestro submarino puede hacer todo lo que el barco,
pero además necesitamos un comando sumergirse. Si no
usamos la herencia, tenemos dos opciones.

• Una, añadir el comando sumergirse() a nuestro


barco. No es una gran idea. No deberíamos dar la
impresión de que nuestro barco se sumerge
normalmente.
• Dos, podríamos crear una copia de la clase Barco y
llamarla Submarino. En esta nueva clase añadiríamos
el comando sumergirse(). Al principio sería fácil,
pero las cosas podrían complicarse si cambiáramos
la clase Barco. El programador debería recordar que
no solo debemos cambiar la clase Barco, sino
también hacer los mismos cambios en la
clase Submarino. Mantener sincronizado este código
es laborioso y está sujeto a errores.

Afortunadamente existe un camino mejor. Nuestro


programa puede crear una clase hija que herede todos
los atributos y métodos de su clase padre. Entonces, la
clase hija podría añadir campos y métodos que se
correspondan con sus necesidades. Por ejemplo:

1 class Submarino(Barco):

2 def sumergirse(self):

print("Sumergirse!")
3

La línea 1 es la clave. Con solo haber añadido Barco entre


paréntesis, durante la declaración de la clase, hemos
llamado automáticamente, a todos los atributos y
métodos de la clase Barco. Si actualizamos Barco, la clase
hija Submarino, se actualizará automáticamente. ¡Así de fácil
es la herencia!
El siguiente código está esquematizado en la
Figura 12.10.

Figure 12.10: Diagrama de Clase

Ejemplo de las Clases Persona, Empleado y Cliente


class Persona():
1
def __init__(self):
2 self.nombre = ""
3 class Empleado(Persona):

4 def __init__(self):

# Llamamos primero a la clase consstructor padre


5
super().__init__()
6 # Ahora establecemos las variables
7 self.nombre_del_puesto= ""

class Cliente(Persona):
8
def __init__(self):
9 super().__init__()

10 self.email = ""

john_smith = Persona()
11
john_smith.nombre = "John Smith"
12
jane_empleado = Empleado()
13 jane_empleado.nombre = "Empleado Jane"

14 jane_empleado.nombre_del_puesto = "Desarrollador Web"

bob_cliente = Cliente()
15
bob_cliente.nombre = "Bob Cliente"
16 bob_cliente.email = "enviame@spam.com"
17
18
19
20
21
22
23
24
25
26
27

Colocando Persona entre los paréntesis de las líneas 5 y 13,


el programador le ha dicho al ordenador que Persona es
una clase padre para Empleado y Cliente. Esto permite al
programa establecer el atributo nombre en las líneas 19 y 22.

Los métodos también se heredan. Cualquier método


que posea la clase padre será heredado por la clase hija.
¿Pero qué sucederá si tenemos un método en cada clase,
hija y padre?

Existen dos opciones. En la primera podemos ejecutar


ambas usando la palabra super(). Usamos super() seguido
por el operador punto, así el nombre del método te
permite llamar a la versión padre del método.

El código anterior muestra la primera opción, no sólo


usamos super para el constructor hijo, sino también para
el padre.

Si estás escribiendo un método para el hijo y quieres


llamar al método padre, éste será la primera línea en el
método hijo. Observa cómo lo hemos hecho en el
ejemplo anterior.

Todos los constructores deberían llamar al constructor


padre. Es muy triste un hijo sin su padre. De hecho, hay
lenguajes que fuerzan a esta regla. Python no.

¿La segunda opción? Los métodos pueden


ser sobreescritos por una clase hija para proporcionar
funcionalidades diferentes. El siguiente ejemplo
muestra ambas opciones. El Empleado.informesobreescribe
a Persona.informe debido a que nunca llama ni ejecuta el
método padre. El informe Cliente llama al método padre,
y el método informe en Cliente se añade a la funcionalidad
de Persona.
Sobreescribir constructores
class Persona():
1
def __init__(self):
2
self.nombre = ""
3 def informe(self):

4 # Informe básico

print("Informe para", self.nombre)


5
class Empleado(Persona):
6 def __init__(self):

7 # Llamamos primero a la clase constructor padre/super

super().__init__()
8
# Establecemos las variables ahora
9
self.nombre_del_puesto = ""
10 def informe(self):

11 # Aquí solo sobreescribimos informe:

print("Informe para", self.nombre)


12
class Cliente(Persona):
13 def __init__(self):
14 super().__init__()

15 self.email = ""

def informe(self):
16
# Ejecutamos el informe padre:
17 super().informe()
18 # Añadimos ahora nuestro propio código al final, de forma que hacemos los dos

print("e-mail del Cliente:", self.email)


19
john_smith = Persona()
20 john_smith.nombre = "John Smith"

21 jane_empleado = Empleado()

jane_empleado.nombre = "Empleado Jane"


22
jane_empleado.nombre_del_puesto = "Desarrollador Web"
23
bob_cliente = Cliente()
24 bob_cliente.nombre = "Cliente Bob"

25 bob_cliente.email = "envia_me@spam.com"

john_smith.informe()
26
jane_empleado.informe()
27 bob_cliente.informe()
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

12.6.1 Es-Una y Tiene-Una Relación

Las clases tienen dos tipos principales de relaciones. Son


“es una” y “tiene una” relación.

La clase padre debería siempre ser una versión más


general, abstracta de la clase hija. Este tipo de relación
hija-padre se denomina relación es una. Por ejemplo,
una clase padre Animal puede tener una clase hija
Perro. La clase Perro podría tener una clase hija Poodle.
Otro ejemplo, un delfín es unmamífero. No funciona en
sentido inverso, un mamífero no tiene por qué ser un
delfín. Por ello, la clase Delfín nunca sería la clase padre
de la clase Mamífero. De la misma forma, la clase Mesa
no podría ser padre de la clase Silla, debido a que una
silla no es una mesa.

El otro tipo es tiene una. Este tipo relación se implementa


mediante atributos de clase. Un perro tiene un nombre,
por ello, la clase Perro tiene un atributo para nombre. De
la misma forma, una persona podría tener un perro, lo
que se implementaría de forma que la clase Persona
tuviera un atributo para Perro. La clase Persona no
derivaría de Perro. Lógicamente esto puede ser
ofensivo.

Observando el ejemplo anterior podemos ver que:

• Empleado es una persona.


• Cliente es una persona.
• Persona tiene un nombre.
• Empleado tiene un puesto.
• Cliente tiene un e-mail.

12.7 Variables Estáticas vs.


Variables de Instancia
La diferencia entre una variable estática y otra de
instancia es confusa. Afortunadamente no necesitamos
comprender completamente la diferencia en estos
momentos. Pero si continúas programando lo
necesitarás. Por lo tanto, de momento, haremos una
breve introducción aquí.

También existen algunas particularidades del Python


que me tuvieron confuso durante los primeros años en
los que estuvo disponible este libro. Por ello, es probable
que te encuentres con vídeos y ejemplos antiguos donde
yo estaba equivocado.
Una variable de instancia es la clase de variable que
hemos estado empleando hasta ahora. Cada instancia de
la clase tiene su propio valor. Por ejemplo, en una
habitación llena de personas, cada una tiene su propia
edad. Algunas de las edades serán las mismas, pero
nosotros necesitamos llevar la cuenta individual de cada
edad.

Con variables de instancia, no podemos decir


simplemente “edad” en una habitación llena de gente.
Necesitamos especificar la edad de quién estamos
hablando. Además, si no hay gente en la habitación,
hablar de edad allí donde no hay personas que la
tengan, no tiene mucho sentido.

Con las variables estáticas, el valor es el mismo para cada


instancia particular de la clase. Aún en el caso de que no
hayan instancias, todavía hay un valor para la variable
estática. Por ejemplo, podríamos tener una variable
estática contar para el número de clases Humano que hayan en
existencia. No tenemos seres humanos? El valor es cero,
y aún así, existe la variable.

En el siguiente ejemplo, la ClaseA crea una variable de


instancia. La ClaseB crea una variable estática .
# Ejemplo de una variable de instancia
1
class ClaseA():
2 def __init__(self):

self.y = 3
3
# Ejemplo de una variable estática
4 class ClaseB():

5 x =7

# Creamos las instancias de clase


6
a = ClaseA()
7
b = ClaseB()
8 # Dos formas de imprimir la variable estática.

9 # La segunda es la forma correcta de hacerlo.

print(b.x)
10
print(ClaseB.x)
11 # Una forma de imprimir una variable de instancia
12 # La segunda genera un error, ya que no sabemos a que instancia referirnos.

13 print(a.y)

print(ClaseA.y)
14
15
16
17
18
19
20
21
22

En el anterior ejemplo, las líneas 16 y 17 imprimen la


variable estática. La línea 17 es la forma “correcta” de
hacerlo. Al contrario de lo que pasaba antes, podemos
referirnos al nombre de la clase, cuando usamos
variables estáticas, en lugar de usar una variable que
apunte a una instancia particular. Debido a que estamos
trabajando con el nombre de la clase, viendo la línea 17,
podemos inmediatamente decir que estamos ante una
variable estática. La línea 16 podría ser a la vez una
variable de instancia o estática, por lo que emplear la
línea 17 resulta ser una mejor opción.

La línea 22 imprime la variable de instancia, tal como ya


hemos hecho en anteriores ejemplos. La línea 23
generará un error, debido a que cada instancia de y es
diferente (después de todo, es una variable de instancia)
y nosotros no le estamos diciendo al ordenador de que
instancia de la ClaseA estamos hablando.

12.7.1 Variables de Instancia. Ocultando


Variables Estáticas

Este es un rasgo de Python que no me gusta. Es posible


tener una variable estática, y una variable de
instancia con el mismo nombre. Observa el siguiente
ejemplo:
# Clase con una variable estática
1
class ClaseB():
2
x =7
3 # Creamos una instancia de clase
4 b = ClaseB()

# Esto imprime 7
5
print(b.x)
6 # Esto también imprime 7

7 print(ClaseB.x)

# Asignamos un nuevo valor a x usando el nombre de clase


8
ClaseB.x = 8
9
# Esto imprime 8
10 print(b.x)

11 # Esto también imprime 8

print(ClaseB.x)
12
# Asignamos un nuevo valor a x usando la instancia.
13 # ¡Pero espera, realmente no está asignando un nuevo valor a x!
14 # Esto crea una variable x completamente nueva. Esta x es una

15 # variable de instancia. La variable estática también se llama

# x. Pero son dos variables diferentes. Esto es supercomplicado y


16
# y es una muy mala costumbre.
17 b.x = 9

18 # Esto imprime 9

print(b.x)
19
# Esto imprime 8. NO 9!!!
20
print(ClaseB.x)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

Esto de permitir que las variables de instancia oculten a


las variables estáticas, me ha confundido durante
muchos años!

12.8 Repaso
12.8.1 Test

Haz click para ir al Test.

12.8.2 Ejercicios

Haz click para ir a los Ejercicios.

12.8.3 Taller

Haz click para ir al Taller.


Chapter 13: Introducción a los
Sprites
Vídeo: Introducción a los Sprites

Todos nuestros juegos necesitan de algo que pueda


gestionar objetos que se tocan. Pelotas que salen
rebotadas de una raqueta, extraterrestres alcanzados
por un rayo láser, o nuestro personaje favorito
recogiendo una moneda. Todos ellos requieren lo que se
denomina detección de colisiones.

La biblioteca Pygame posee soporte para sprites. Un


sprite es una imagen bidimensional que forma parte de
una escena gráfica aun mayor. Habitualmente, un sprite
es cierto tipo de objeto que interactúa. Por ejemplo, un
coche, una rana o nuestro pequeño fontanero.
Originalmente, las videoconsolas venían equipadas con
hardware para recrear los sprites. En la actualidad este
hardware especializado ya no es necesario, pero
seguimos usando el término “sprite.”

13.1 Sprites Básicos y Colisiones


Veremos paso a paso un programa que utiliza sprites.
En este ejemplo se muestra cómo crear una pantalla con
bloques negros, que recogeremos mediante un bloque
rojo controlado por el ratón, tal como se puede ver en la
Figura 13.1. El programa mantiene un “marcador” con
los bloques que llevemos recogidos. El código para este
ejemplo lo podemos encontrar en:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=sprite_collect_blocks.py

Figure 13.1: Ejemplo de un Juego con Sprites


La primera línea de nuestro programa empieza como
otros tantos juegos que ya hemos hecho:

1
2 import pygame

import random
3
# Definimos algunos colores
4
NEGRO = ( 0, 0, 0)
5 BLANCO = (255, 255, 255)

6 ROJO = (255, 0, 0)

Para tener soporte de sprites, importamos la biblioteca


Pygame en la línea 1. Para colocar aleatoriamente los
bloques, importamos la biblioteca random en la línea 2.
La definición estándar de los colores la encontramos
entre las líneas 5-7; hasta aquí no hay nada que no
hayamos visto ya.

9
class Bloque(pygame.sprite.Sprite):
10 """

11 Esta clase representa la pelota.

Deriva de la clase "Sprite" en Pygame.


12
"""
13

En la línea 9 comienza la definición de la clase Bloque.


Observa que esta es un clase hija de la clase Sprite. El
texto pygame.sprite. especifica la biblioteca y el paquete, algo
que trataremos en el capítulo 14. Todas las
funcionalidades por defecto de la clase Sprite serán ahora
parte de la clase Bloque.

15
16 def __init__(self, color, largo, alto):

""" Constructor. Pasa el color del bloque,


17
así como su posición x e y. """
18 # Llama al constructor de la clase padre (Sprite)

19 super().__init()__

20

En la línea 15, el constructor de la clase Bloque recibe un


parámetro para self, tal como sucede para cualquier otro
constructor. También toma parámetros que definen el
color, el alto y el largo del objeto.

Es importante llamar al constructor de la clase padre


en Sprite para permitir que los sprites se inicialicen. Esto
se consigue en la línea 20.

22 # Crea una imagen del bloque y lo rellena de color.


23 # También podría tratarse de una imagen cargada desde el disco.

24 self.image = pygame.Surface([largo, alto])

self.image.fill(color)
25
Las líneas 24 y 25 crean la imagen que finalmente
aparecerá en la pantalla. La línea 24 crea una imagen en
blanco. La línea 25 la rellena de negro. Si el programa
necesita dibujar una figura distinta al bloque negro,
deberemos modificar las siguientes líneas de código:

Por ejemplo:

15
16
17 def __init__(self, color, largo, alto):

18 """

Constructor Elipse. Pasa el color de la elipse,


19
y sus dimensiones.
20
"""
21 # Llama al constructor de la clase padre (Sprite)

22 super().__init__()

# Establece el color de fondo y que éste será transparente.


23
self.image = pygame.Surface([largo, alto])
24
self.image.fill(BLANCO)
25 self.image.set_colorkey(BLANCO)

26 # Dibuja la elipse

pygame.draw.ellipse(self.image, color, [0, 0, largo, alto])


27
28
29

Si emplearamos el código anterior, todo tendría forma


de elipse. La línea 29 dibuja la elipse y la 26 hace que el
blanco sea transparente de forma que resalte el color de
fondo. Es el mismo concepto que ya vimos en el capítulo
11, donde convertíamos en transparente el color blanco
del fondo de la imagen.

15
16
17 def __init__(self):

18 """ Constructor Gráfico del Sprite . """

# Llama al constructor de la clase padre (Sprite)


19
super().__init__()
20
# Carga la imagen
21 self.image = pygame.image.load("jugador.png").convert()

22 # Establece el color transparente

self.image.set_colorkey(BLANCO)
23
24
25

En el código anterior cambiamos el origen del sprite. Si


lo que deseamos es un mapa de bits, cargamos una
imagen (línea 22) y determinamos que el fondo
transparente sea de color blanco (línea 25). En este caso,
las dimensiones del sprite se ajustarán automáticamente
a las del gráfico, con lo que no necesitamos pasárselas a
la función. Observa como en la línea 15 ya no aparecen
esos parámetros.
Independientemente del tipo de sprite que tengamos,
aún existe otra línea que necesitamos para nuestro
constructor:

27 # Captura el objeto rectángulo que posee las dimensiones de la imagen.


28 # Actualiza la posición de ese objeto estableciendo los valores de

29 # rect.x y rect.y

self.rect = self.image.get_rect()
30

El atributo rect, es una variable que, a su vez, es instancia


de la clase Rect que proporciona Pygame. Este rectángulo
representa las dimensiones del sprite. La clase
rectángulo tiene unos atributos para x e y que hay que
establecer. Pygame dibujará el sprite donde se
encuentren esos atributos x e y. Por eso, para mover el
sprite, el programador necesita
establecer mySpriteRef.rect.x y mySpriteRef.rect.y,
donde mySpriteRef es la variable que apunta al sprite.

Bueno, ya hemos terminado con la clase Bloque. Es tiempo


de empezar con el código de iniciación.

32 # Inicializamos Pygame

pygame.init()
33
# Establecemos el largo y alto de la pantalla
34
largo_pantalla = 700
35 alto_pantalla = 400

36 pantalla = pygame.display.set_mode([largo_pantalla, alto_pantalla])


37
38

El código anterior inicializa Pygame y crea una ventana


para el juego. Hasta aquí nada nuevo respecto a otros
programas Pygame.

40
41 # Esta es una lista de 'sprites.' Cada bloque del programa es

42 # añadido a esta lista.

# La lista es gestionada por una clase llamada 'Group.'


43
bloque_lista = pygame.sprite.Group()
44 # Esta es una lista de cada sprite.

45 # Incluido el del protagonista también.

listade_todoslos_sprites = pygame.sprite.Group()
46
47

Una ventaja importante de trabajar con sprites, es la


posibilidad de agruparlos. Con un solo comando
podemos dibujar, y mover a la vez, todos los sprites que
se encuentren agrupados. Podemos también registrar
colisiones de sprites contra un grupo entero.

El código anterior crea dos listas. La


variable listade_todoslos_sprites contendrá cada sprite del
juego. Usaremos esta lista para dibujar todos los sprites.
La variable bloque_lista contiene cada objeto con los que
puede colisionar el protagonista. En este ejemplo se
incluirá cada objeto del juego, excepto al protagonista.
No queremos que el protagonista se encuentre en esta
lista. Si estuviera en ella, Pygame siempre nos
devolvería que el protagonista está chocando, y no
podríamos comprobar cuándo es que realmente
colisiona con los objetos del bloque_lista.

49
50
for i in range(50):
51
# Esto representa un bloque
52
bloque = Bloque(NEGRO, 20, 15)
53 # Establece una ubicación aleatoria para el bloque

54 bloque.rect.x = random.randrange(largo_pantalla)

bloque.rect.y = random.randrange(alto_pantalla)
55
# Añade el bloque a la lista de objetos
56
bloque_lista.add(bloque)
57 listade_todoslos_sprites.add(bloque)

58
59

El bucle que comienza en la línea 49 añade 50 bloques


sprite negros a la pantalla. La línea 51 crea un bloque
nuevo, establece su color, su largo y su altura. Las líneas
54 y 55 establecen las coordenadas donde aparecerá el
objeto. La línea 58 añade el bloque a la lista de bloques
con los que el protagonista puede colisionar. La línea 59
lo añade a la lista de todos los bloques. Esto debería
parecerse bastante al código que escribiste para la
práctica del Taller 13.

61 # Creamos un bloque protagonista ROJO

62 protagonista = Bloque(ROJO, 20, 15)

listade_todoslos_sprites.add(protagonista)
63

Las líneas 61-63 determinan al protagonista de nuestro


juego. La línea 62 crea un bloque rojo que funcionará
finalmente como el protagonista. Para que este bloque
se pueda dibujar, en la línea 63 lo añadimos
a listade_todoslos_sprites, y no en bloque_lista.

65
66
# Iteramos hasta que el usuario pulse el botón de cerrar.
67
hecho = False
68 # Determinamos cuán rápido se refresca la pantalla

69 reloj = pygame.time.Clock()

marcador = 0
70
# -------- Bucle Principal del Programa -----------
71
while not hecho:
72 for evento in pygame.event.get():

73 if evento.type == pygame.QUIT:

hecho = True
74
# Limpiamos la pantalla
75 pantalla.fill(BLANCO)
76
77
78
79
80

El anterior código es el estándar que ya introdujimos en


el Capítulo 5. La línea 71 inicializa nuestra
variable marcador a 0.

82
83 # Obtenemos la posición actual del ratón como

84 # una lista de dos números.

pos = pygame.mouse.get_pos()
85
# Extraemos los valores x e y de la lista,
86
# de la misma forma que extraemos letras de una cadena de caraceteres (string).
87 # Colocamos al objeto protagonista en la ubicación del ratón

88 protagonista.rect.x = pos[0]

protagonista.rect.y = pos[1]
89
90

Tal como ya vimos en otros programas Pygame, la


posición del ratón es recogida por la línea 84. Lo nuevo
e importante está contenido entre las líneas 89-90, donde
el rectángulo que contiene al sprite es trasladado a una
nueva ubicación. Recordemos que este rectángulo fue
creado previamente en la línea 31, y que sin esta parte,
el código no funcionaría.
92 # Observamos si el bloque protagonista ha colisionado con algo.

93 lista_impactos_bloques = pygame.sprite.spritecollide(protagonista, bloque_lista, Tr

Esta línea de código toma al sprite referenciado


como protagonista y lo chequea frente al resto de sprites
en bloque_lista. El código devuelve una lista con los sprites
que coincidan con él. Si no hubieran sprites
coincidentes, devolvería una lista vacía. El operador
booleano True, retirará de la lista a todos los sprites que
coincidan. Si este operador estuviera establecido
como False, los sprites no serían retirados.

95 # Comprobamos la lista de colisiones.


96 for bloque in lista_impactos_bloques:

97 marcador += 1

print(marcador)
98

Este bucle itera sobre cada uno de los sprites de la lista


de colisiones creada anteriormente en la línea 93. Si
hubieran sprites en esa lista, incrementaría el marcador
por cada una de las colisiones. Luego mostraría el
marcador en pantalla. Observemos que el
comando print de la línea 98, no imprimirá el marcador
en la ventana principal junto a los sprites, sino en la
consola. ¿Cómo conseguir que el marcador aparezca
sobre la ventana principal? Eso es parte del Taller 14.
100 # Dibujamos todos los sprites

101 listade_todoslos_sprites.draw(pantalla)

La variable listade_todoslos_sprites, que pertenece a la


clase Group, posee un método llamado draw. Este método
itera a través de la lista llamando al método draw en cada
uno de los sprites. Esto significa, que con solo una línea
de código, el programa consigue que cada uno de los
sprites en listade_todoslos_sprites sea dibujado.

103
104
# Limitación de 60 fotogramas por segundo
105 reloj.tick(60)

106 # Avanzamos y actualizamos la pantalla con todo aquello que ha sido dibujado.

pygame.display.flip()
107
pygame.quit()
108
109

Las líneas 103-109 refrescan la pantalla, y llaman al


método quit cuándo el bucle principal termina.

13.2 Sprites En Movimiento


Vídeo: Sprites En Movimiento

Hasta ahora, en el ejemplo que hemos tratado, solo el


sprite protagonista se mueve. ¿Cómo podemos
conseguir que el programa mueva todos los sprites?
Pues muy fácil, sólo necesitamos dos pasos adicionales:

Lo primero es añadir un método nuevo a la clase Bloque.


Llamaremos update a este nuevo método. La función
update será llamada automáticamente cuando update sea
llamado para la lista entera.

Colocamos esto en el sprite:


def update(self):

""" Llamado en cada fotograma. """

# Desplazamos el bloque hacia abajo un píxel

self.rect.y += 1

Colocamos esto dentro del bucle principal:


# Llamamos al método update() para todos los bloques en bloque_lista

bloque_lista.update()

El código dista todavía de ser perfecto. De momento,


todos los bloques escapan de la pantalla sin volver a
aparecer. Las siguientes líneas mejorarán la
función update de forma que los bloques vuelvan a
aparecer por la parte superior de la pantalla.
def update(self):

# Desplaza el bloque hacia abajo un píxel

self.rect.y += 1

if self.rect.y > alto_pantalla:


self.rect.y = random.randrange(-100, -10)

self.rect.x = random.randrange(0, largo_pantalla)

Si lo que queremos es que el programa resetee las


posiciones de los bloques en la parte superior de la
pantalla, escribiremos las siguientes líneas:
def reset_pos(self):

""" Reseteamos la posición en la parte superior de la pantalla en ubicaciones aleatorias

Llamada por update() o por el bucle principal si ocurre una colisión.

"""

self.rect.y = random.randrange(-300, -20)

self.rect.x = random.randrange(0, largo_pantalla)

def update(self):

""" Llamado en cada fotograma. """

# Desplazamos un píxel hacia abajo el bloque

self.rect.y += 1

# Si el bloque está muy abajo, lo devuelve a la parte superior de la pantalla.

if self.rect.y > 410:

self.reset_pos()

En lugar de que los bloques se destruyan al colisionar,


podríamos hacer que el programa llamara a la
función reset_pos y que el bloque se desplazara a la parte
superior de la pantalla, listo para ser recogido.
# Observa si el bloque protagonista ha colisionado con algo.

lista_impactos_bloques = pygame.sprite.spritecollide(protagonista, bloque_lista, False)

# Comprueba la lista de colisiones.

for bloque in lista_impactos_bloques:

marcador += 1
print(marcador)

# Resetea el bloque hacia la parte superior para que vuelva a caer.

bloque.reset_pos()

El código completo está aquí:


ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=moving_sprites.py

Si en lugar de eso quieres el código para sprites que


rebotan, mira aquí:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=moving_sprites_bounce.py

Si lo que quieres es que se muevan en círculos:


ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=sprite_circle_movement.py

13.3 La Clase Juego


Anteriormente, en el Capítulo 9, introdujimos las
funciones. Al final de ese capítulo hablamos de una
opción que era la función main. A medida que los
programas se hacen extensos, esta técnica nos ayudará
a evitar futuros problemas que puedan provenir de
tener que recorrer un montón de código. De momento,
nuestros programas no son muy largos. Sin embargo,
conozco algunas personas a las cuales les gusta tener
todo organizado desde el principio.
Para esas personas que ya se enfrentan a códigos muy
largos, esta es una técnica opcional para mantener
organizado el código. (Si no perteneces a este grupo
todavía, puedes saltarte esta sección y marcarla para
cuando tus programas se vuelvan extensos.) Mira el
vídeo para que tengas una idea de como trabaja el
programa.
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=game_class_example.py

13.4 Otros Ejemplos


Aquí debajo encontrarás más ejemplos de lo que puedes
hacer con sprites. Algunos, incluso, incluyen un vídeo
que explica como funcionan.

13.4.1 ¿Disparamos?

Figure 13.2: Disparamos?

¿Estás interesado en un juego donde haya que pegar


tiros? Algo así como el clásico Space Invaders? En el
siguiente ejemplo se explica cómo crear sprites que
representen dianas:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=bullets.py

13.4.2 Paredes

¿A lo mejor lo que buscas más bien, es un juego del tipo


aventuras? ¿No quieres que tu protagonista vague
libremente por todo el espacio? El ejemplo siguiente te
muestra cómo agregar paredes que entorpezcan los
movimientos del protagonista:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=move_with_walls_example.py

Figure 13.3: Podemos tropezarnos con muros

¡Espera! ¿No tienes suficiente para tu aventura con una


sola habitación? ¿Quieres que tu personaje se desplace
entre distintas pantallas? ¡Pues lo podemos hacer! Mira
el siguiente ejemplo donde el personaje se tiene que
desplazar entre un laberinto de estancias:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=maze_runner.py
Figure 13.4: Laberinto

13.4.3 Plataformas

¿Te interesa un juego de plataformas como Donkey


Kong? Podemos usar el mismo concepto que hemos
empleado para las paredes, pero añadiéndole un toque
de fuerza de gravedad:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=platform_jumper.py

Figure 13.5: Saltando entre plataformas

Las buenas plataformas se mueven de un lado a otro.


Este sería un ejemplo:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=platform_scroller.py
Figure 13.6: Juego de plataformas de desplazamiento lateral

Los juegos de plataformas más divertidos tienen


superficies que se mueven. Observa cómo lo hemos
hecho en el ejemplo siguiente:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=platform_moving.py

Figure 13.7: Plataformas móviles

13.4.4 Serpiente/Ciempiés

De vez en cuando me he cruzado con alumnos que


quieren construir un juego del tipo “serpiente” o
“ciempiés”. Aquí te encuentras con una serpiente que
posee varios segmentos que debes controlar. Esto
requiere que cada segmento se guarde en una lista.
Aunque aprender a hacer esto requiere de dos nuevos
comandos, el concepto que hay detrás de ello no es muy
difícil de comprender.

Controlar una serpiente o ciempiés que se mueven


alrededor de la pantalla:
ProgramArcadeGames.com/python_examples/f.php?la
ng=es&file=snake.py

Figure 13.8: Serpiente

13.4.5 Usando Hojas de Sprites

Este es un ejemplo bastante extenso sobre el uso de


“hojas de sprites”, las cuales proporcionarán los gráficos
de fondo necesarios, para un juego de plataformas.
También permite múltiples niveles y plataformas
móviles. El juego se ha dividido en varios
archivos.ProgramArcadeGames.com/python_examples
/sprite_sheets
Figure 13.9: Plataformas con hojas de sprites

13.4.6 Test

Haz click para ir al Test.

13.4.7 Ejercicios

No hay ejercicios para este capítulo.

13.4.8 Taller

Haz click para ir al Taller.


Chapter 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:

1. Trocear el código en unidades más pequeñas y


fáciles de usar.
2. Permite que varias personas trabajen al mismo
tiempo en un programa.
3. 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:
Vídeo: Bibliotecas

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:
archiv test.py con todo su contenido

1
2 # Función Foo

def foo():
3
print("foo!")
4 # Llamada a la función Foo

5 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.
mis_funciones.py

1 # Función Foo

2 def foo():

print("foo!")
3
archivo test.py que no funciona

1 # Llamada a la función Foo que no se ejecuta

2 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:
archivo test.py que importa pero que aún no funciona

1
# Importa el archivo <kbd>mis_funciones.py</kbd>
2
import mis_funciones
3
# Llamada a la función Foo que aún no se ejecuta
4 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:
archivo test.py que finalmente funciona

1
# Importa el archivo mis_funciones.py
2
import mis_funciones
3
# Llamada a la función Foo que ya se ejecuta
4 mis_funciones.foo()

Esto funciona debido a que hemos


antepuesto mis_funciones a la llamada de la función.

14.3 Namespace
Vídeo: 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
funciones_alumnos.py

1 def imprimir_informe():

2 print ("Informe de Notas del Estudiante:" )

funciones_financieras.py

1 def imprimir_informe():

2 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:
archivo test.py que llama a distintas funciones imprimir_informe

1
import funciones_alumnos
2
import funciones_financieras
3
funciones_alumnos.imprimir_informe()
4 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:
test.py

1
# import foo
2
from mis_funciones import *
3 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.
Ejemplo OpenPyXL

1 """

Uso de OpenPyXL para crear una hoja de cálculo Excel


2
"""
3 from openpyxl import Workbook

4 import random

# Creamos una hoja Excel


5
wb = Workbook()
6
# Tomamos la hoja activa
7 ws = wb.active

8 # Podemos introducir datos directamente en las celdas

ws['A1'] = "Esto es una prueba"


9
# Podemos añadir también filas
10 for i in range(200):
11 ws.append([random.randrange(1000)])
12 # Guardamos el archivo

wb.save("ejemplo.xlsx")
13
14
15
16
17
18
19
20
21
22

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.
Ejemplo con Beautiful Soup (bs4)

1 """

Mostramos cómo leer desde una página web


2
"""
3 from bs4 import BeautifulSoup

4 import urllib.request
5 # Leemos el archivo

url= "http://www.espnfc.com/spi/rankings/_/view/fifa/teamId/203/mexico?cc=5901"
6
pagina = urllib.request.urlopen(url)
7 sopa = BeautifulSoup(page.read(), "html.parser")

8 # Encontramos la tabla con los datos

rango = sopa.find("table").tbody
9
# Obtenemos una lista con las filas de la tabla
10
filas = rank.findAll("tr")
11 # Iteramos a través de cada fila

12 for fila in filas:

# Obtenmos una lista con las celdas de la fila


13
celdas = fila.findAll("td")
14 # Iteramos a través de cada celda
15 for celda in celdas:

16 print(celda.text, end=", ")

# Listo, hemos terminado con esa fila. Imprimos una línea en blanco y vamos a la
17
# siguiente.
18 print()

19
20
21
22
23
24
25
26
27
28
29
30
31

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:
Figure 14.1: Gráfica Simple De Líneas

Ejemplo 1
"""
1
Recta con cuatro valores.
2 Por defecto, el eje x empieza en cero.
3 """

4 import matplotlib.pyplot as plt

y = [1, 3, 8, 4]
5
plt.plot(y)
6 plt.ylabel('Valor del Elemento')
7 plt.xlabel('Número del Elemento')

plt.show()
8
9
10
11
12
13

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.
Figure 14.2: Especificar valores para x

Ejemplo 2

1 """

Recta con cuatro valores.


2
Ahora especificamos unos valores para el eje x también.
3
"""
4 import matplotlib.pyplot as plt

5 x = [1, 2, 3, 4]

y = [1, 3, 8, 4]
6
plt.plot(x, y)
7
plt.ylabel('Valor del Elemento')
8 plt.xlabel('Elemento')

9 plt.show()
10
11
12
13
14
15

Es bastante fácil añadir otra serie de datos a la gráfica.

Figure 14.3: Representar dos series de datos

Ejemplo 3
1
2
3
4 """
5 En este ejemplo se muestra como dibujar dos series diferentes de datos

sobre la misma gráfica.


6
"""
7
import matplotlib.pyplot as plt
8 x = [1, 2, 3, 4]

9 y1 = [1, 3, 8, 4]

y2 = [2, 2, 3, 3]
10
plt.plot(x, y1)
11
plt.plot(x, y2)
12 plt.ylabel('Valor del Elemento')

13 plt.xlabel('Elemento')

plt.show()
14
15
16
17

También puedes añadirle una leyenda a la gráfica:


Figure 14.4: Añadir leyenda

Example 4

1 import matplotlib.pyplot as plt

x = [1, 2, 3, 4]
2
y1 = [1, 3, 8, 4]
3
y2 = [2, 2, 3, 3]
4 plt.plot(x, y1, label = "Serie 1")

5 plt.plot(x, y2, label = "Serie 2")

leyenda = plt.legend(loc = 'upper center', shadow = True, fontsize = 'x-large')


6
leyenda.get_frame().set_facecolor('#00FFCC')
7
plt.ylabel('Valor del Elemento')
8 plt.xlabel('Elemento')

9 plt.show()
10
11
12
13
14
15
16

Puedes añadir también anotaciones:

Figure 14.5: Añadir anotaciones


Ejemplo 5

1
2
3
4
5
"""
6 Anotando sobre la gráfica

7 """

import matplotlib.pyplot as plt


8
x = [1, 2, 3, 4]
9 y = [1, 3, 8, 4]

10 plt.annotate('Aquí',

xy = (2, 3),
11
xycoords = 'data',
12
xytext = (-40, 20),
13 textcoords = 'offset points',

14 arrowprops = dict(arrowstyle = "->",

connectionstyle = "arc, angleA = 0,armA = 30,rad = 10"),


15
)
16 plt.plot(x, y)
17 plt.ylabel('Valor del Elemento')

18 plt.xlabel('Elemento')

plt.show()
19
20
21
22
23
¿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.

Figure 14.6: Especificar un estilo de línea

Ejemplo 6
"""
1
Aquí mostramos cómo establecer el estilo de línea y los símbolos.
2 """

3 import matplotlib.pyplot as plt

x = [1, 2, 3, 4]
4
y1 = [1, 3, 8, 4]
5
y2 = [2, 2, 3, 3]
6 # Primer carácter: Estilo de línea

# Elegir entre '-', '--', '-.', ':', 'None', ' ', ”


7
# Segundo carácter: color
8 # http://matplotlib.org/1.4.2/api/colors_api.html

9 # Tercer carácter: forma del símbolo

# http://matplotlib.org/1.4.2/api/markers_api.html
10
plt.plot(x, y1, '-ro')
11
plt.plot(x, y2, '--g^')
12 plt.ylabel('Valor del Elemento')

13 plt.xlabel('Elemento')

plt.show()
14
15
16
17
18
19
20
21
22
23
24
25

Pasar a una gráfica de barras es tan fácil como


cambiar plot por bar.
Figure 14.7: Gráfica de Barras

Ejemplo 7

1
"""
2 Cómo construir una gráfica de barras.

3 """

import matplotlib.pyplot as plt


4
x = [1, 2, 3, 4]
5
y = [1, 3, 8, 4]
6 plt.bar(x, y)

7 plt.ylabel('Valor del Elemento')

plt.xlabel('Elemento')
8
plt.show()
9
10
11
12
13
14

Puedes añadirle etiquetas a los valores de los ejes.

Figure 14.8: X-axis labels

Ejemplo 8
"""
1
Cómo añadir etiquetas a los valores del eje x.
2 """

import matplotlib.pyplot as plt


3
x = [1, 2, 3, 4]
4 y = [1, 3, 8, 4]

5 plt.plot(x, y)

labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs']


6
plt.xticks(x, labels)
7
plt.ylabel('Valor del Elemento')
8 plt.xlabel('Elemento')

9 plt.show()

10
11
12
13
14
15
16
17

También puedes dibujar funciones. Para dibujar la


función seno, necesitamos una biblioteca distinta
llamada numpy.
Figure 14.9: Dibujar la función seno

Ejemplo 9

1 """

Uso de la biblioteca numpy para dibujar una función para un determinado


2
rango de valores.
3
"""
4 import numpy

5 import matplotlib.pyplot as plt

x = numpy.arange(0.0, 2.0, 0.001)


6
y = numpy.sin(2 * numpy.pi * x)
7
plt.plot(x, y)
8 plt.ylabel('Valor del Elemento')

9 plt.xlabel('Elemento')
10 plt.show()

11
12
13
14
15
16

Si quieres, puedes también rellenar la gráfica.

Figure 14.10: Rellenar una gráfica


Ejemplo 10

1
2
3
4
"""
5
Uso de 'fill' para rellenar una gráfica
6
"""
7 import numpy

8 import matplotlib.pyplot as plt

x = numpy.arange(0.0, 2.0, 0.001)


9
y = numpy.sin(2 * numpy.pi * x)
10 plt.plot(x, y)

11 # 'b' significa azul. 'alpha' es la transparencia.

plt.fill(x, y, 'b', alpha = 0.3)


12
plt.ylabel('Valor del Elemento')
13
plt.xlabel('Elemento')
14 plt.show()

15
16
17
18

Crear una gráfica de tarta.


Figure 14.11: Gráfica de tarta

Ejemplo 11

1 """

Crear una gráfica de tarta


2
"""
3
import matplotlib.pyplot as plt
4 # Etiquetas para la gráfica de tarta

5 etiquetas = ['C', 'Java', 'Objective-C', 'C++', 'C#', 'PHP', 'Python']

# Tamaños para cada etiqueta. Lo usamos para crear un porcentaje


6
dimensiones = [17, 14, 9, 6, 5, 3, 2.5]
7
# Para una lista de colores, ver:
8 # https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/colors.py

9 colores = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral', 'darkcyan', 'darksa


10 # ¿Cuán separada ha de estar una porción? Normalmente cero.

separar = (0, 0.0, 0, 0, 0, 0, 0.2)


11
# Definimos la ratio de aspecto al valor 'equal' para que la tarta dibujada sea cir
12 plt.axis('equal')

13 # Finalmente, dibujamos la tarta

plt.pie(dimensiones, explode = separar, labels = etiquetas, colors = colores,


14
autopct = '%1.1f%%', shadow = True, startangle = 90)
15
plt.show()
16
17
18
19
20
21
22
23
24
25
26

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:
Figure 14.12: Gráfica de Caja y Bigotes

Ejemplo 12

1 """

Crear una gráfica de caja y bigotes para la cotización de un valor en bolsa


2
"""
3
import matplotlib.pyplot as plt
4 from matplotlib.dates import DateFormatter, WeekdayLocator,\

5 DayLocator, MONDAY

from matplotlib.finance import quotes_historical_yahoo_ohlc, candlestick_ohlc


6
# Extraemos las cotizaciones para las fechas siguientes
7
fecha1 = (2014, 10, 13)
8 fecha2 = (2014, 11, 13)

9 # Vamos a la web y nos traemos la información de la cotización


10 valores = quotes_historical_yahoo_ohlc('AAPL', fecha1, fecha2)

if len(valores) == 0:
11
raise SystemExit
12 # Definimos el aspecto de la gráfica

13 fig, ax = plt.subplots()

fig.subplots_adjust(bottom=0.2)
14
# Ponemos marcas mayores para los lunes
15
lunes = WeekdayLocator(MONDAY)
16 ax.xaxis.set_major_locator(lunes)

17 # Ponemos marcas menores para el resto de días

resto_dias = DayLocator()
18
ax.xaxis.set_minor_locator(resto_dias)
19 # Damos formato a los días
20 formato_semana = DateFormatter('%b %d') # e.g., Ene 12

21 ax.xaxis.set_major_formatter(formato_semana)

ax.xaxis_date()
22
candlestick_ohlc(ax, valores, width=0.6)
23 ax.autoscale_view()

24 plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')

plt.show()
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

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

Haz click para ir al Test.

14.5.2 Ejercicios

Haz click para ir a los Ejercicios.

14.5.3 Taller

Haz click para ir al Taller.


Chapter 15: Búsquedas

Las búsquedas son operaciones muy habituales en


computación. Cada vez que pulsamos Ctrl+F para
“encontrar”, o usamos el teclado para seleccionar un
objeto, o cuando un servidor web extrae información de
un cliente para presentarle una página personalizada,
estamos haciendo una búsqueda.

Existen muchas formas de buscar datos. Google ha


basado su multimillonaria compañía en este hecho. En
este capítulo introduciremos los dos métodos más
simples para realizar búsquedas; búsqueda
lineal y búsqueda binaria.

15.1 Leer Desde Un Archivo


Vídeo: Leer un Archivo

Antes de empezar a discutir el cómo buscar,


necesitamos aprender primero, cómo leer los datos
guardados en un archivo. Leer un conjunto de datos
desde un archivo, es una forma más divertida que tener
que hacerlo a mano repetidamente.

Pongamos que tenemos que crear un programa que nos


permita encontrar rápidamente el nombre de un súper
villano. Para comenzar, nuestro programa necesita una
base datos de super villanos. Descarga y guarda el
siguiente archivo:
http://ProgramArcadeGames.com/chapters/16_searchi
ng/super_villains.txt
Todos son nombres aleatorios generados por el website
nine.frenchboys.net. De todas formas, en la última visita
que hice al website, éste ya no generaba súper villanos.

Guarda el archivo y recuerda dónde lo hiciste.

En el mismo directorio donde se


encuentra super_villains.txt, crea, guarda y ejecuta el
siguiente programa Python:

1
file = open("super_villains.txt")
2
for line in file:
3 print(line)

4
En este código, aparece tan solo un nuevo comando; open.
Como es una función incorporada en Python, tal
como print, no tenemos necesidad de usar un import. Los
detalles completos de esta función los puedes encontrar
en la documentación de Python. Pero en estos
momentos, la documentación de ese comando es tan
técnica, que no merece la pena perder el tiempo en ella.

A pesar de que el programa anterior proporciona una


manera simple de leer un archivo, tiene dos problemas.
En la línea 1 abrimos un archivo y lo dejamos preparado
para leerlo. El nombre del archivo va entrecomillado. La
nueva variable file es un objeto que representa al archivo
que está siendo leído. La línea 3 muestra cómo un
sencillo bucle for puede usarse para leer un archivo línea
a línea. Piensa en file como una lista de líneas, donde la
nueva variable line, irá tomando los valores de cada una
de esas líneas, a medida que el programa itera a través
del bucle.

Intenta ejecutar el programa. Verás que uno de los


problemas que tiene, es que el texto se imprime a doble
espacio. La razón de esto, es que cada línea extraída del
archivo y guardada en la variable line, incluye un retorno
de carro como parte de la cadena de texto. ¿Recuerdas
del Capítulo 1, el retorno de carro y el avance de línea?
Y como bien sabes, la sentencia print añade otro retorno
de carro, con el resultado de que tenemos una salida a
doble espacio.

El segundo problema que tenemos es que el archivo ha


sido abierto, pero no cerrado. Este problema no es tan
obvio como el del doble espacio, pero es importante. El
sistema operativo Windows solo puede abrir unos
cuantos archivos a la vez. Un archivo, normalmente,
solo puede ser abierto por un programa a la vez. Si
dejamos el archivo abierto, evitaremos que otros
programas puedan usarlo, además de ocupar recursos
del sistema. Tenemos que cerrar el archivo de forma que
Windows sepa que nuestro programa ya no lo está
usando. En nuestro caso no es tan crítico, ya que cuando
cualquier programa termina de ejecutarse, Windows
cierra automáticamente cualquier archivo que se haya
dejado abierto. Pero como eso es un mal hábito, vamos
a actualizar nuestro código:

1
2
file = open("super_villains.txt")
3 for line in file:

4 line = line.strip()

print(line)
5
file.close()
6
7
El código anterior funciona mejor. Tiene dos elementos
nuevos. En la línea 4 se hace una llamada al método strip,
incluido en cada clase String. Esta función devuelve una
cadena nueva, sin los espacios finales ni los retornos de
carro de la cadena original. El método no altera la
cadena original, en todo caso, crea una nueva. La
siguiente línea de código no funcionaría por si sola :
line.strip()

Si la programadora quiere que la variable original haga


referencia a la nueva cadena, deberá asignarla tal como
se ve en la línea 4.

La segunda novedad está en la línea 7. En ella se cierra


el archivo evitando que sea el sistema operativo el
encargado de hacerlo más adelante.

15.2 Volcado A Un Array


Es bastante útil volcar los contenidos de un archivo en
un array, de manera que el programa los pueda procesar
posteriormente. En Python lo podemos hacer fácilmente
de la siguiente manera:
Leer un archivo desde el disco y pasarlo a un array
# Leemos un archivo desde el disco y lo pasamos a un array
1
file = open("super_villains.txt")
2 lista_nombres = []

for line in file:


3
line = line.strip()
4 lista_nombres.append(line)

5 file.close()

6
7
8
9

Aquí combinamos una característica nueva, leer desde


un archivo, con otra vista previamente, cómo crear un
array vacío y rellenarlo con datos que le vamos pasando,
tal como vimos en el Capítulo 7. Para comprobar que el
archivo ha sido volcado correctamente en el array, el
programador podría imprimir la longitud del array:
print("Había",len(lista_nombres),"nombres en el archivo.")

O el programador podría mostrar el contenido completo


del array:
for nombre in lista_nombres:

print(nombre)

Antes de continuar con otras búsquedas diferentes,


avanza y comprueba que puedes leer el archivo.
15.3 Búsqueda Lineal
Si un programa tiene un conjunto de datos en un array,
¿cómo podemos hacer para encontrar un elemento
específico? Esto se puede hacer de dos formas. La
primera es utilizar una búsqueda lineal. Empieza por el
primero, y continúa comparando elementos hasta que
encuentra el deseado (o se queda sin elementos.)

15.3.1 Algoritmo de Búsqueda Lineal


Búsqueda Lineal

1
2
# Búsqueda Lineal
3
clave = "Morgana la Arpía"
4
i =0
5 while i < len(lista_nombres) and lista_nombres[i] != clave:

6 i += 1

if i < len(lista_nombres):
7
print( "El nombre se encuentra en la posición", i)
8
else:
9 print( "El nombre no se encuentra en la lista." )

10
11
Vídeo: Búsqueda Lineal

La búsqueda lineal es bastante sencilla. En la línea 2 se


establece una variable incremental que lleva la cuenta
de cuál es el siguiente elemento de la lista que el
programa necesita comprobar. El primer elemento que
debe ser comprobado es el cero, por lo que
establecemos i a cero.

La línea siguiente es un poco más compleja. El programa


debe iterar hasta que suceda alguno de los siguientes
hechos: que encuentre el elemento, o que se quede sin
elementos que buscar. El primer comparador mira si el
elemento actual es menor que la longitud de la lista. Si
es así, seguimos iterando. El segundo comparador mira
si el elemento actual en la lista de nombres es igual al
nombre que estamos buscando.

Comprobar que no nos hemos quedado sin elementos


de búsqueda es lo primero que debe hacerse. De otra forma,
el programa buscaría un elemento inexistente, lo que
produciría un error.

En la línea 4 se avanza hacia el siguiente elemento si las


condiciones de la línea 3 se cumplen.

Al final del bucle, el programa comprueba si el final de


la lista se alcanzó en la línea 6. Recuerda, una lista
de n elementos se enumera de 0 a n-1. Por lo tanto, si i es
igual a la longitud de la lista, hemos acabado. Si es
menor, hemos encontrado el elemento.
15.4 Variantes De La Búsqueda
Lineal
Podemos usar variantes de la búsqueda lineal para
crearnos distintos algoritmos. Digamos que tenemos,
por ejemplo, una lista de aliens. Quizás nos interese
comprobar si en el grupo hay un alien verde, o si son
todos verdes, o cuáles son verdes.

Para empezar, necesitamos definir nuestro alien:


Clase Alien

1
class Alien:
2 """ Clase que define un alien"""
3 def __init__(self, color, peso):

4 """ Constructor. Establece peso y color"""

self.color = color
5
self.peso = peso
6

Luego debemos crearnos una función que compruebe si


posee el rasgo que estamos buscando. En nuestro caso;
¿es verde? Asumiremos que el color es una cadena de
texto que convertiremos a mayúsculas para eliminar el
riesgo de algún conflicto con las minúsculas.
método tiene_propiedad de la clase Alien
1
def tiene_propiedad(mi_alien):
2
""" Comprobamos si un item posee esa propiedad.
3 En este caso, ¿es verde el alien? """

4 if mi_alien.color.upper() == "VERDE":

return True
5
else:
6
return False
7

15.4.1 Al Menos Uno De Los Elementos Tiene


Esa Propiedad?

¿Al menos uno de los aliens es verde? Lo podemos


comprobar. El algoritmo básico tras ésta comprobación
es:
Bucle while que comprueba si en la lista hay un elemento que tenga la propiedad

1 def comprueba_si_un_elemento_tiene_propiedad_v1(mi_lista):

2 """ Devuelve true si al menos un item tiene esa

propiedad. """
3
i =0
4 while i < len(mi_lista) and not tiene_propiedad(lista[i]):
5 i += 1

6 if i < len(mi_lista):

# Se encontró un elemento con esa propiedad


7
return True
8 else:

9 # No existe un elemento con esa propiedad

return False
10
11
12
13
14

Esto también se podría hacer con un bucle for. En este


caso, el bucle se detendría con un return una vez que el
elemento ha sido encontrado. El código es más corto,
pero no todos los programadores escogerían esta vía.
Algunos programadores piensan que los bucles no
deberían terminar prematuramente con declaraciones
del tipo return o break. Esto depende claro, de las
preferencias personales de cada uno, o de las que tenga
la persona que paga la factura.
Bucle for que comprueba si en la lista hay un elemento que tenga la propiedad

1
def comprueba_si_un_elemento_tiene_propiedad_v2(mi_lista):
2
""" Devuelve true si al menos un item tiene esa
3 propiedad. Funciona como v1, pero con menos código. """

4 for item in mi_lista:

if tiene_propiedad(item):
5
return True
6
return False
7
15.4.2 ¿Todos Los Elementos Tienen Una
Propiedad?

¿Son verdes todos los aliens? Este código es muy similar


al anterior. Encuentra la diferencia e intenta entender las
razones del cambio.
Comprueba si todos los objetos tienen una propiedad

1
def comprueba_si_todos_elementos_tienen_propiedad(mi_lista):
2 """ Devuelve true si TODOS los items tienen una propiedad. """
3 for item in mi_lista:

4 if not tiene_propiedad(item):

return False
5
return True
6

15.4.3 Creamos Una Lista Con Todos Los


Elementos Que Tengan Una Propiedad

¿Qué sucede si necesitas una lista en la que todos los


aliens que sean verdes? Esta es una combinación del
código anterior, junto con el código para añadir
elementos a una lista que vimos en el Capítulo 7.
Creamos otra lista con todos los elementos que cumplan una propiedad
def obtener_elementos_coincidentes(lista):
1
""" Construye una lista completamente nueva con todos los items
2
que coincidan con nuestra propiedad. """
3 lista_coincidentes = []
4 for item in lista:

if tiene_propiedad(item):
5
lista_coincidentes.append(item)
6 return lista_coincidentes

7
8

¿Cómo ejecutarías todo esto en un test? El código


anterior lo podemos combinar con éste para ello:
Ejecutar Funciones de Muestra

1
2
3
lista_aliens = []
4
lista_aliens.append(Alien("Verde", 42))
5
lista_aliens.append(Alien("Rojo", 40))
6 lista_aliens.append(Alien("Azul", 41))

7 lista_aliens.append(Alien("Púrpura", 40))

resultado = comprueba_si_un_elemento_tiene_propiedad_v1(lista_aliens)
8
print("Resultado del test comprueba_si_un_elemento_tiene_propiedad_v1:", resultado)
9 resultado = comprueba_si_un_elemento_tiene_propiedad_v2(lista_aliens)

10 print("Resultado del test comprueba_si_un_elemento_tiene_propiedad_v1:", resultado)

resultado = comprueba_si_todos_elementos_tienen_propiedad(lista_aliens)
11
print("Resultado del test comprueba_si_todos_elementos_tienen_propiedad:", resultad
12
resultado = obtener_elementos_coincidentes(lista_aliens)
13 print("Número de items devueltos por el test obtener_elementos_coincidentes:", len(

14
15
16
17

Puedes ver un ejemplo completo y funcional aquí:


programarcadegames.com/python_examples/show_fil
e.php?file=property_check_examples.py

Los algoritmos anteriores pueden usarse como parte de


la solución a un problema mayor, tal como encontrar
todas las direcciones no válidas en una lista de clientes.

15.5 Búsqueda Binaria


Vídeo: Leer un Archivo

Posiblemente, una forma más rápida de buscar en una


lista sea a través de una búsqueda binaria. El proceso de
una búsqueda binaria se puede describir usando como
ejemplo el clásico juego de “adivina un número entre 1
y 100”. Para facilitar la comprensión del proceso, vamos
a modificar el juego a “adivina un número entre 1 y
128”. El rango de los números es inclusivo, es decir,
tanto el 1 como el 128 son posibles también.

Si una persona tuviera que usar la búsqueda lineal como


método para adivinar el número secreto, el juego se
convertiría en algo muy largo y aburrido.

Adivina un número entre 1 y 128: 1


Muy bajo.
Adivina un número entre 1 y 128: 2
Muy bajo.
Adivina un número entre 1 y 128: 3
Muy bajo.
....
Adivina un número entre 1 y 128: 93
Muy bajo.
Adivina un número entre 1 y 128: 94
Correcto!

La mayoría usaría una búsqueda binaria para encontrar


el número. Éste sería un buen ejemplo de ello:

Adivina un número entre 1 y 128: 64


Muy bajo.
Adivina un número entre 1 y 128: 96
Muy alto.
Adivina un número entre 1 y 128: 80
Muy bajo.
Adivina un número entre 1 y 128: 88
Muy bajo.
Adivina un número entre 1 y 128: 92
Muy bajo.
Adivina un número entre 1 y 128: 94
Correcto!

En cada una de las pasadas del juego, al obtener un


“alto” o “bajo”, la persona que adivina es capaz de
restringir, a la mitad, el problema.
En una búsqueda binaria, es necesario hacer un
seguimiento de los límites, superior e inferior, de la lista
que contiene la respuesta. El ordenador o la persona que
adivina, escoge el punto medio de esos elementos.
Volviendo a nuestro ejemplo:

Para un límite inferior de 1, y un límite superior de 128,


el punto medio es .
Adivina un número entre 1 y 128: 64
Muy bajo.

Para un límite inferior de 65, y un límite superior de 128,


el punto medio es .
Adivina un número entre 1 y 128: 96
Muy alto.

Para un límite inferior de 65, y un límite superior de 95,


el punto medio es .
Adivina un número entre 1 y 128: 80
Muy bajo.

Para un límite inferior de 81, y un límite superior de 95,


el punto medio es .
Adivina un número entre 1 y 128: 88
Muy bajo.

Para un límite inferior de 89, y un límite superior de 95,


el punto medio es .
Adivina un número entre 1 y 128: 92
Muy bajo.

Para un límite inferior de 93, y un límite superior de 95,


el punto medio es .
Adivina un número entre 1 y 128: 94
Correcto!

Una búsqueda binaria reduce significativamente el


número de intentos. En el peor de los casos se
necesitarían 7 intentos para encontrar un número entre
1 y 128. Un intento más elevaría el límite a 256. Para 9
intentos, el número podría estar entre 1 y 512. En sólo 32
intentos, una persona podría encontrar un número entre
1 y 4.2e9.

Para determinar cuán larga podría ser la lista, dado un


cierto número de intentos, podríamos usar la fórmula ,
donde es el tamaño de la lista y el número de intentos.
Por ejemplo:
(con 7 intentos se pueden manejar 128 números
diferentes)

Si nuestro problema consiste en determinar el número


de intentos, conociendo el tamaño de la lista, podemos
recurrir a la función log. Específicamente log base 2. Si no
especificamos la base, la mayoría podría asumir que
hablamos del logaritmo natural en base que no es
precisamente lo que queremos. Por ejemplo, usando log
base 2 para encontrar el número de intentos,
tendríamos:

¡Bueno, vale! ya hemos tenido bastantes matemáticas.


Lo que nos interesa; ¿dónde está el código? Pues el
código para realizar una búsqueda binaria es un poco
más complicado que el de una búsqueda lineal:
Búsqueda Binaria
# Búsqueda Binaria
1
clave = "Morgana la Arpía"
2 limite_inferior = 0

3 limite_superior = len(lista_nombres)-1

encontrado = False
4
# Iteramos hasta encontrar el objeto, o donde se encuentran nuestros límites superi
5
while limite_inferior <= limite_superior and not encontrado:
6 # Hallamos la posición intermedia

7 posicion_intermedia = (limite_inferior+limite_superior) // 2

# Determinamos si:
8
# desplazamos hacia arriba el límite superior, o
9
# desplazamos hacia abajo el límite inferior, o si
10 # ya hemos encontrado lo que buscábamos

11 if lista_nombres[posicion_intermedia] < clave:

limite_inferior = posicion_intermedia + 1
12
elif lista_nombres[posicion_intermedia] > clave:
13 limite_superior = posicion_intermedia - 1
14 else:

encontrado = True
15
if encontrado:
16 print("El nombre se encuentra en la posición", posicion_intermedia)

17 if not found:

print("El nombre no se encontraba en la lista.")


18
19
20
21
22
23
24
25
26
27
28

Como las listas comienzan por el elemento cero, la línea


3 establecería el límite inferior a cero. La línea 4
establecería el límite superior como la longitud de la
lista menos uno. Por lo tanto, para una lista de 100
elementos, el límite inferior sería 0 y el superior, 99.

La variable booleana de la línea 5 se usaría para permitir


que el bucle while supiera que el elemento en cuestión
ha sido encontrado.
La línea 6 comprueba si el elemento fue encontrado o
nos hemos quedado sin elementos que buscar. Si
sucediera esto último, terminaríamos con que el límite
inferior se igualaría al superior.

La línea 7 encuentra la posición intermedia. Es posible


llegar a una posición intermedia igual a 64.5. Pero
sabemos que no es posible que exista tal posición 64.5.
(Aunque J.K. Rowling fue lo suficientemente lista para
crear un andén , eso no funcionaría en nuestro caso.) La
mejor manera de manejar esta situación, es utilizar el
operador // que introdujimos en el Capítulo 5. Es
parecido al operador /, con la diferencia de que solo
devuelve resultados enteros. Por ejemplo, 11 // 2 daría 5,
en lugar de 5.5.

Empezando en la línea 8, el programa comprueba si


nuestra predicción ha sido alta, baja o correcta. Si ha
sido baja, el límite inferior asciende justo por encima de
ella. Si nuestra predicción ha sido alta, el límite superior
desciende justo por debajo de ella. Pero si hemos dado
en la diana, encontrado se establece a True finalizando la
búsqueda.

Dada una lista con 100 elementos, una persona podría


intuir razonablemente, que de media con una búsqueda
lineal, el programa tendría que comprobar 50 elementos
antes de encontrar el correcto. Con una búsqueda
binaria, debería aún, realizar al menos siete intentos. En
cursos avanzados sobre algoritmos, podrás encontrar la
fórmula exacta. De momento en este curso, basta con
que asumamos que los casos promedio y peor son lo
mismo.

15.6 Repaso
15.6.1 Test

Haz click para ir al Test.

15.6.2 Ejercicios

Haz click para ir a los Ejercicios.

15.6.3 Taller

Haz click para ir al Taller.


Chapter 16: Retículas y Matrices
16.1 Introducción
Vídeo: Retículas y Matrices'

Juegos como el buscaminas, el tres en raya, y muchos de


aventuras, almacenan los datos del juego en retículas de
números. Por ejemplo, un tablero de tres en raya como
éste:

OO

...puede utilizar una retícula numérica para representar


los espacios vacíos, los 0's y lad X's de la manera
siguiente:

022

010

100

También podemos llamar a esta retícula numérica


como array bidimensional o matriz. (Finalmente
aprenderemos algo de The Matrix.) Los valores
numéricos de la retícula representan lo que debería ser
mostrado en cada una de las posiciones del tablero. Para
nuestro ejemplo, el 0 representa un lugar donde nadie
ha jugado, el 1 representa a la X y el 2 al 0.

Figure 16.1:
Buscaminas y su Retícula Numérica

La Figura 16.1 es un ejemplo del clásico buscaminas. Lo


hemos modificado para mostrar, tanto el juego clásico a
la izquierda, como la retícula numérica empleada, a la
derecha.

El número 10 representa una mina, el 0 un espacio que no


ha sido tocado, y el 9 una casilla que ya lo fue. Los
números del 1 al 8 representan la cantidad de minas que
hay en las 8 casillas de alrededor, y solo se muestran
cuando el jugador pulsa la casilla.

En realidad, el buscaminas puede tener dos retículas.


Una para mostrar el juego en curso y otra, separada por
completo, que llevará la cuenta de los lugares
“marcados” por la jugadora en el tablero, donde cree
que pueden encontrarse las minas.

Los mapas de los clásicos juegos de aventura son


creados con lo que se denomina un editor de mapas.
Éstos, son retículas enormes donde cada localización es
un simple número que representa el tipo de terreno que
va allí. El terreno puede ser polvoriento, un camino, una
senda, césped verde, césped amarillento, etc. Los
programas como Tiled Qt, mostrados en la Figura 16.2,
permiten que el desarrollador cree mapas fácilmente y
que pueda guardar la retícula en el disco.
Figure 16.2: Uso de Tiled Qt para crear una mapa de aventura
Los juegos de aventuras también emplean múltiples
retículas numéricas, algo similar al buscaminas, donde
hay una retícula para las minas y otra para las marcas.
En los juegos de aventuras, una retícula, o “capa”,
representa el tipo de terreno por el que puedes caminar;
otra representa objetos por donde no se puede andar,
tales como paredes y árboles; otra representaría cosas
que te pueden matar al instante, como la lava o pozos
sin fondo; otra podría representar objetos que se pueden
recoger y desplazar; y aún otra, que podría contener la
posición inicial de los monstruos.

Los mapas como estos se pueden cargar en Python, pero


desadortunadamente, una descripción completa de
cómo hacerlo, está más allá del propósito de este libro.
Ciertos proyectos, como PyTMX, proporcionan algo del
código necesario para cargar este tipo de mapas.

16.2 Aplicación
Basta de charla y vamos a escribir algo de código. En
este ejemplo crearemos una retícula que se activará si
mostramos un bloque de color blanco o verde. Podemos
cambiar el valor de la retícula y hacer que sea verde
haciendo clic sobre ella. Es un primer paso hacia un
juego del tipo el buscaminas, hundir la flota, etc. (Hubo
un año en que una estudiante me llamó para enseñarme
una versión modificada de un programa como éste,
donde aparecía mi nombre en luces parpadeantes. Fue
como mínimo....inquietante. Por favor, usen su
conocimiento para hacer el bien solamente!)

Ir a la página de códigos de ejemplo y descargarse la


siguiente plantilla:
ProgramArcadeGames.com/python_examples/f.php?fil
e=pygame_base_template_es.py

Intenta recrear este programa usando las instrucciones


contenidas en la plantilla. El programa final está al final
del capítulo. No te vayas hasta allí y lo copies! No habrás
aprendido nada si haces eso. Cualquiera puede copiar y
pegar código, pero si eres capaz de hacer este programa,
estás adquiriendo las habilidades por las que la gente
está dispuesta a pagar. Si solo puedes copiar y pegar,
has estado desperdiciando tu tiempo aquí.

16.2.1 Dibujar la Retícula


1. Ajusta las dimensiones de la ventana del
programa en 255x255 píxeles.
2. Crea unas variables llamadas largo, alto, y margen.
Establece el valor de largo y alto en 20. Esto
representará cuán grande es cada una de las
cuadrículas. El margen lo estableces en 5, lo que
representa el espacio entre cada cuadrícula y los
bordes de la pantalla. Crea estas variables antes
del bucle principal del programa.
3. Dibuja un cuadrado blanco en la esquina superior
izquierda de la pantalla. Dibújalo usando las
variables largo y alto que creaste anteriormente.
(Eres libre de elegir el color que quieras.) Cuando
hallas acabado, la ventana de tu programa debería
ser como la de la Figura 16.3.

Figure 16.3: Paso 3


4. Utiliza un bucle for para dibujar 10 cajas en la fila.
Usa columna como nombre de variable en el
bucle for. La salida se verá como una caja larga
hasta que no añadamos los márgenes entre las
celdas. Observa la Figura 16.4.

Figure 16.4: Paso 4


5. Ajusta el dibujo de la caja para añadirle la
variable margen. En este momento deberían aparecer
los espacios entre las celdas. Observa la

Figura 16.5. Figure 16.5:


Paso 5
6. Además de hacerlo entre las celdas, añade el
margen antes de dibujarlas. Esto impedirá que la
celda se vea pegada al borde de la ventana.
Observa la Figura 16.6.
Figure 16.6: Paso 6
7. Añade otro bucle for que itere para cada fila.
Llama fila a la variable dentro de este nuevo
bucle for. En este momento tendremos completa
nuestra retícula. Observa la Figura 16.7.

Figure 16.7: Paso 7


16.2.2 Rellenamos la Retícula
8. En estos momentos tenemos que crearnos una
array bidimensional. Hacerlo en Python,
desafortunadamente, no es tan fácil como en otros
lenguajes. Existen algunas bibliotecas que
podemos descargar para Python que facilitan esto,
pero en nuestro ejemplo no las vamos a utilizar.
Para crear un array bidimensional usaremos el
siguiente código:
Creamos un array numérico de 10x10

1
2 # --- Creamos una retícula numérica

3 # Creamos una lista vacía

grid = []
4
# Iteramos para cada fila
5 for fila in range(10):
6 # Para cada fila, creamos una lista que

7 # representará una fila completa

grid.append([])
8
# Iteramos para cada columna
9 for columna in range(10):

10 # Añade el número cero a la fila actual

grid[fila].append(0)
11
12
9. Podemos ver un ejemplo más reducido debajo en el
que se utilizan ciertas particularidades de Python,
pero que no voy a explicar en este libro:
Creamos un array numérico de 10x10

1 grid = [[0 for x in range(10)] for y in range(10)]

10. Utiliza cualquiera de los dos ejemplos


anteriores, y coloca el código que creará nuestro
array antes del bucle principal.

11. Como ejemplo, introducimos un 1 en una de


las celdas del array.

Los arrays dimensionales se suelen representar


indicando primero la fila y luego la columna. Esto
se llama ordenamiento por filas. La mayoría de
lenguajes utilizan esta característica, con la
excepción de Fortran y MATLAB, que emplean
ordenamiento por columnas.
# Fila 1, columna 5 = 1

grid[1][5] = 1

Coloca el anterior código en algún lugar antes del


bucle principal.
12. Selecciona el color de la celda basándote en el
valor de la variable llamada color. Para hacerlo
tienes primero que encontrar la línea de código
donde se dibuja la celda. Antes de ella, crea una
variable llamada color y establécela en blanco.
Luego, sustituye el color blanco en la declaración
de la celda con la variable color.
13. Selecciona el color basándote en el valor de la
celda. Luego de haber establecido color como
blanco, coloca una declaración if que busque en el
valor de grid[fila][columna], y cambie el color a verde
si el valor de la celda es igual a 1. Ahora
deberíamos tener un cuadrado verde. Observa la

Figura 16.8. Figure 16.8:


Paso 11
14. Imprime “click” por pantalla si el jugador
pulsa el ratón.
Revisa bitmapped_graphics.py para que veas cómo
detectar un click del ratón.
15. Imprime las coordenadas del ratón cuando el
jugador haga click.
Revisa move_mouse.py para que veas cómo obtener la
posición del ratón. Observa la Figura 16.9.

Figure 16.9: Paso 13


16. Convierte las coordenadas del ratón en
coordenadas de retícula. Imprime éstas últimas.
Recuerda que debes usar el largo y alto de cada
celda en combinación con el margen. Será
necesario que conviertas en número entero los
valores finales. Lo puedes hacer usando int o la
división parte entera //, en lugar de la división
normal /. Observa la Figura 16.10.

Figure 16.10: Paso 14


17. Convierte en 1 la celda. Observa la

Figura 16.11. Figure


16.11: Paso 15

16.2.3 Programa Final


array_backed_grid.py

1 """

2 Programa de ejemplo de cómo usar un array para respaldar una retícula en pantalla.
3 Sample Python/Pygame Programs

Simpson College Computer Science


4
http://programarcadegames.com/
5 http://simpson.edu/computer-science/

6 Vídeo explicativo: http://youtu.be/mdTeqiWyFnc

"""
7
import pygame
8
# Definimos algunos colores
9 NEGRO = (0, 0, 0)

10 BLANCO = (255, 255, 255)

VERDE = ( 0, 255, 0)
11
ROJO = (255, 0, 0)
12 # Establecemos el LARGO y ALTO de cada celda de la retícula.
13 LARGO = 20

14 ALTO = 20

# Establecemos el margen entre las celdas.


15
MARGEN = 5
16 # Creamos un array bidimensional. Un array bidimensional

17 # no es más que una lista de listas.

grid = []
18
for fila in range(10):
19
# Añadimos un array vacío que contendrá cada celda
20 # en esta fila

21 grid.append([])

for columna in range(10):


22
grid[fila].append(0) # Añade una celda
23 # Establecemos la fila 1, celda 5 a uno. (Recuerda, los números de las filas y

24 # columnas empiezan en cero.)

grid[1][5] = 1
25
# Inicializamos pygame
26
pygame.init()
27 # Establecemos el LARGO y ALTO de la pantalla
28 DIMENSION_VENTANA = [255, 255]

pantalla = pygame.display.set_mode(DIMENSION_VENTANA)
29
# Establecemos el título de la pantalla.
30 pygame.display.set_caption("Retículas y Matrices")

31 # Iteramos hasta que el usuario pulse el botón de salir.

hecho = False
32
# Lo usamos para establecer cuán rápido de refresca la pantalla.
33
reloj = pygame.time.Clock()
34 # -------- Bucle Principal del Programa-----------

35 while not hecho:

for evento in pygame.event.get():


36
if evento.type == pygame.QUIT:
37 hecho = True
38 elif evento.type == pygame.MOUSEBUTTONDOWN:

39 # El usuario presiona el ratón. Obtiene su posición.

pos = pygame.mouse.get_pos()
40
# Cambia las coordenadas x/y de la pantalla por coordenadas reticulares
41 columna = pos[0] // (LARGO + MARGEN)

42 fila = pos[1] // (ALTO + MARGEN)

# Establece esa ubicación a cero


43
grid[fila][columna] = 1
44
print("Click ", pos, "Coordenadas de la retícula: ", fila, columna)
45 # Establecemos el fondo de pantalla.

46 pantalla.fill(NEGRO)

# Dibujamos la retícula
47
for fila in range(10):
48 for columna in range(10):

49 color = BLANCO

if grid[fila][columna] == 1:
50
color = VERDE
51
pygame.draw.rect(pantalla,
52 color,
53 [(MARGEN+LARGO) * columna + MARGEN,

(MARGEN+ALTO) * fila + MARGEN,


54
LARGO,
55 ALTO])

56 # Limitamos a 60 fotogramas por segundo.

reloj.tick(60)
57
# Avanzamos y actualizamos la pantalla con lo que hemos dibujado.
58
pygame.display.flip()
59 # Pórtate bien con el IDLE.

60 pygame.quit()

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
y empieza a trabajar en tu propio videojuego!

16.2.4 Test

Haz click para ir al Test.

16.2.5 Ejercicios

Haz click para ir a los Ejercicios.


Chapter 17: Poniendo Orden
Las búsquedas binarias solo funcionan en listas que se
encuentren ordenadas. Entonces, cómo hacemos que
nuestros programas reciban una lista ordenada? ¿Cómo
es que un programa ordena una lista de objetos al hacer
click sobre el encabezado de una columna?

Existen distintos algoritmos para hacerlo. Los dos


algoritmos más simples para conseguirlo son los
llamados de ordenamiento por selección y de ordenamiento
por inserción. Existen algunos otros como los shell, por
mezcla, montículo y quicksorts.

La mejor forma de tener una idea de cómo funcionan es


verlos trabajar. Para ver los algoritmos de ordenamiento
más comunes en acción, visita este excelente website:
http://www.sorting-algorithms.com

Cada clase de ordenamiento tiene sus pros y sus contras.


Algunos ordenan rápidamente una lista si ésta se
encuentra casi ordenada al empezar. Otros lo hacen con
listas donde el orden es totalmente aleatorio. Otros lo
hacen muy rápido, pero a un alto coste de memoria.
Comprender cómo funciona un ordenamiento es clave
para seleccionar el algoritmo adecuado para un
programa.
17.1 Intercambiando Valores
Vídeo: Intercambiando valores en un array

Antes de aprender cómo ordenar, debemos aprender


cómo intercambiar valores entre dos variables. Esta es
una operación muy común en muchos de estos
algoritmos. Imaginemos que tenemos una lista con el
aspecto siguiente:
lista = [15, 57, 14, 33, 72, 79, 26, 56, 42, 40]

Digamos que el desarrollador quiere intercambiar las


posiciones 0 y 2, que contienen los números 15 y 14
respectivamente. Observa la Figura 17.1.

Figure 17.1: Intercambiando valores en un array

Un primer intento para desarrollar el código podría ser


éste:
lista[0] = lista[2]

lista[2] = lista[0]

Figure
17.2: Intento fallido de intercambiar valores en un array

Observa la Figura 17.2 para que tengas una idea de lo


que sucedería. Claramente no funciona. La primera
asignación lista[0] = lista[2] provoca que el valor 15 de la
posición 0 sea sobrescrito por el valor 14 de la posición
2, e irremediablemente perdido. La línea
siguiente, lista[2] = lista[0], tan solo vuelve a copiar el valor
14 a la celda 2, que ya posee ese mismo valor.

Para arreglar este problema, el intercambio de valores


en un array debería hacerse en tres pasos. Para ello es
necesario crear una variable temporal que guarde un
valor durante la operación de intercambio. Observa la
Figura 17.3. El código para hacerlo sería el siguiente:
Swapping two values in an array
1 temp = lista[0]

2 lista[0] = lista[2]

lista[2] = temp
3

La primera línea copia el valor de la posición 0 en la


variable temp. Esto permite que en la línea 2, el código
sobrescriba la posición 0 con el valor de la posición 2 sin
perder los datos. En la línea 3 se toma el antiguo valor
de la posición 0, en estos momentos dentro de la
variable temp, y se introduce en la posición 2.

Figure 17.3:
Método correcto para intercambiar valores en un array

17.2 Ordenamiento Por Selección


Vídeo: Conceptos de Ordenamiento Por Selección

El ordenamiento por selección empieza por el principio


de la lista. Entonces, el código escanea el resto de la lista
en búsqueda del valor más pequeño. Éste es llevado al
principio. Luego, el código se mueve hacia el siguiente
número más pequeño. Y así sucesivamente. El proceso
lo podemos observar gráficamente en la Figura 17.4.
Por Selección

El código para realizar un ordenamiento por selección


implica dos bucles anidados. El bucle exterior rastrea la
posición donde el código quiere colocar el valor más
pequeño. El bucle interior empieza por la ubicación
actual y escanea hacia la derecha, buscando el valor más
pequeño. Cuando lo encuentra tiene lugar el
intercambio.
Ordenamiento por selección

1
# Ordenamiento por selección
2
def ordenamiento_porseleccion(lista):
3 # Itera por todo el array

4 for pos_actual in range(len(mi_lista)):

# Encuentra la posición del valor más pequeño


5
# Empieza por la posición actual
6
pos_min = pos_actual
7 # Escanea de izquierda a derecha (hasta el final de la lista)

8 for escan_pos in range(pos_actual+1, len(mi_lista)):

# Es ésta la posición más pequeña?


9
if mi_lista[escan_pos] < mi_lista[pos_min]:
10
# Si lo es, la marcamos como la más pequeña
11 pos_min = escan_pos

12 # intercambia los dos valores

temp = mi_lista[pos_min]
13
mi_lista[pos_min] = mi_lista[pos_actual]
14 mi_lista[pos_actual] = temp

15
16
17
18
19
20
21
22

El bucle exterior siempre iterará veces. El bucle interior


iterará veces. Esto ocurrirá siempre sin importar si la
lista está en orden o no. La eficiencia del bucle puede
mejorarse si comprobamos si los valores
para pos_min y pos_actual son iguales antes de la línea 16. Si
ambos son iguales no hay necesidad de realizar las tres
líneas del código de intercambio.

Usaremos el siguiente código para comprobar el


funcionamiento del algoritmo anterior. La primera
función imprimirá la lista. Las siguientes líneas crearán,
una lista de números aleatorios, los imprimirán,
ordenarán y por último, volverá a imprimirlos. En la
línea 5, la sentencia print ordenará los números hacia la
derecha para facilitar su lectura. La sintaxis para los
formatos de impresión la veremos en el Capítulo 20.
Código para crear e imprimir una lista a ordenar
# Pega antes del siguiente código el algoritmo de ordenamiento por selección e intr
1
def print_lista(mi_lista):
2 for item in mi_lista:

print("{:3}".format(item), end="")
3
print()
4 # Creamos una lista con números aleatorios

5 mi_lista = []

for i in range(10):
6
lista.append(random.randrange(100))
7
# Intentamos el ordenamiento
8 print_lista(mi_lista)

9 ordenamiento_porseleccion(mi_lista)

print_lista(mi_lista)
10
11
12
13
14
15
16

Observa una una animación del ordenamiento por


selección en:
http://www.sorting-algorithms.com/selection-sort

Para una demostración muy particular de este


algoritmo, busca en Youtube el término “selection sort
dance” o utiliza este enlace:
http://youtu.be/Ns4TPTC8whw
Puedes iterar a través del código
usando http://pythontutor.com.

17.3 Ordenamiento Por Inserción


Vídeo: Conceptos sobre el Ordenamiento Por Inserción

El ordenamiento por inserción se parece al de selección


por la forma en la que funciona el bucle exterior. El
ordenamiento por selección empieza por la izquierda
del array y va trabajando hacia su derecha. La diferencia
estriba en que no selecciona el elemento más pequeño
para hacer el intercambio, lo que hace es trasladar hacia
la izquierda cada elemento más pequeño que va
encontrando en su camino. Lo podemos ver
gráficamente en la Figura 17.5.
Figure 17.5: Ordenamiento por
Inserción

Este ordenamiento divide la lista en dos secciones; la


mitad “ordenada” y la mitad “desordenada”. En cada
vuelta del bucle exterior, el algoritmo recogerá el
siguiente elemento desordenado y lo insertará en la
lista.

En el siguiente código, la variable pos_clave señala la


frontera entre las porciones ordenadas y desordenadas
de la lista. El algoritmo escanea hacia la izquierda
de pos_clave usando la variable escan_pos. Debemos observar
que en el ordenamiento por inserción, escan_pos desciende
hacia la izquierda en lugar de hacerlo hacia la derecha.
Cada celda que es mayor a valor_clave es desplazada una
posición hacia arriba (a la derecha).

Cuando el bucle encuentra una celda más pequeña


que valor_clave, se detiene y coloca valor_clave a su izquierda.

El bucle exterior, en un ordenamiento por inserción, se


ejecutará veces. El bucle interior lo hará, de
media, veces si fue creado aleatoriamente. Por el
contrario, si el bucle ha sido ordenado en parte
previamente, el interior no iterará muchas veces, siendo
el tiempo de ordenamiento cercano a .
Ordenamiento por Inserción
1 # Ordenamiento por inserción
2 def ordenamiento_porinsercion(mi_lista):

3 # Empieza en el segundo elemento (posición 1)

4 # Usa este elemento para insertarlo en la


5 # lista.
6 for pos_clave in range(1, len(mi_lista)):
7 # Obtiene el valor del elemento a insertar
8 valor_clave = mi_lista[pos_clave]
9 # Escanea desde la derecha hacia la izquierda (principio de la lista)
10 escan_pos = pos_clave - 1
11
# Itera cada elemento , trasladándolo hacia arriba hasta que
12
# alcanza su ubicación
13
while (escan_pos >= 0) and (mi_lista[escan_pos] > valor_clave):
14
mi_lista[escan_pos + 1] = mi_lista[escan_pos]
15
escan_pos = escan_pos - 1
16
# Introducimos la clave en la ubicación correcta
17
mi_lista[escan_pos + 1] = valor_clave
18

Observa una una animación del ordenamiento por inserción en:


http://www.sorting-algorithms.com/insertion-sort

Para una demostración muy particular de este algoritmo, busca en Youtube el


término “insertion sort dance” o utiliza este enlace:
http://youtu.be/ROalU379l3U

Puedes iterar a través del código usando http://pythontutor.com.

17.3.1 Test

Haz click para ir al Test.

17.3.2 Ejercicios

Haz click para ir a los Ejercicios.


Chapter 18: Excepciones
Vídeo: Gestión de Excepciones

¿Que te parecería si, por ejemplo, tu programa no


funcionara bien, evitar que el usuario viese los típicos
mensajes rojos de error de Python, o que el programa no
se colgara? Si es así, lo que necesitas son excepciones.

Las excepciones las usamos para gestionar condiciones


anormales que puedan aparecer durante la ejecución del
código. Habitualmente se usan para operaciones de red
o sobre archivos. Esto permite que el código salga airoso
si ocurre, por ejemplo, un desbordamiento del disco, un
error de red o problemas con determinados permisos.

18.1 Vocabulario
Los términos y frases más habituales cuando trabajamos
con excepciones son:

• Excepción: Este término puede significar dos cosas.


Primero, la condición que resulta de un flujo
anormal del programa. O se puede usar para
referirse al objeto que representa esa condición.
Cada excepción posee un objeto que guarda la
información sobre ella.
• Gestión de la excepción: El proceso de manejo de una
excepción para conseguir un flujo normal del
programa.
• Bloque de captura o bloque de excepción: Es el código
que maneja una condición anormal, y se dice que
“captura” la excepción.
• Lanzar (Throw) o levantar (raise): Cuando se ha
detectado alguna condición anormal en el flujo del
programa, se crea una instancia para el objeto
excepcional, entonces es “levantado” hacia un
código que lo capturará.
• Excepciones no gestionadas o Excepciones no
capturadas: Una excepción que ha sido lanzada
pero nunca capturada. Habitualmente termina en
error y el programa se cuelga o cierra
inesperadamente.
• Bloque try: Contiene el código que al ejecutarse
podría generar una excepción.

La mayoría de lenguajes usan términos como “lanzar” y


“capturar.” Desafortunadamente no es el caso de
Python. Python emplea “levantar” y “excepción.”
Nosotros hemos introducido los términos
lanzar/capturar porque son los más utilizados en la
industria.
18.2 Gestión de Excepciones
El código para manejar las excepciones es sencillo.
Observa el siguiente ejemplo:
Manejo de la división entre cero

1
# Dividir entre cero
2 try:

3 x =5/0

except:
4
print("Error división entre cero")
5

Sobre la línea 2 tenemos la sentencia try. Todas las líneas


indentadas siguientes pertenecen al “bloque try.”
Debajo del bloque try no debería existir ningún
código no-indentado que no empezara con la
sentencia except. La sentencia try define una sección que el
código intentará ejecutar.

Si apareciera cualquier excepción durante el


procesamiento del código, la ejecución saltaría
inmediatamente hacia el “bloque de captura” (“catch
block.”) Esta parte del bloque se halla indentada bajo la
sentencia except de la línea 4. Éste es el código responsable
de manejar el error.
Podemos tener un programa que use excepciones para
capturar errores que puedan aparecer durante la
conversión de un texto a número, como por ejemplo:
Manejo de errores durante la conversión a número

1
# Conversión fallida a número
2 try:

3 x = int("fred")

except:
4
print ("Error al intentar convertir fred a número")
5

En la línea 3 se lanzará una excepción debido a que


“fred” no puede ser convertido a número entero. El
código de la línea 5 imprimirá el mensaje de error.

Debajo podemos ver una versión ampliada del ejemplo


anterior. En este caso se comprueba si existe algún error
en la entrada de usuario, asegurándose de que se ha
introducido un número entero. Si el usuario no lo
introduce, el programa continuará demandándole que
lo haga. El código utiliza una excepción para capturar
un posible error de conversión que pudiera ocurrir en la
línea 5. Si se introdujera cualquier otra cosa que no fuera
un entero, se lanzaría una excepción al intentarse la
conversión en la línea 5. Si así fuera, el código de la línea
6, que establece numero_introducido a True, no se ejecutaría.
Una versión mejorada de la gestión de errores durante la conversión a número

1
numero_introducido = False
2
while not numero_introducido:
3 numero_cadena = input("Introduce un número entero: ")
4 try:

5 n = int(numero_cadena)

numero_introducido = True
6
except:
7 print ("Error, número entero incorrecto")

Los archivos son particularmente proclives a errores


durante las operaciones con ellos; el disco podría
llenarse por completo, un usuario podría borrar un
archivo mientras se escribe sobre él, o la unidad USB ser
extraída en mitad de una operación de lectura/escritura.
Este tipo de errores pueden ser fácilmente capturados
usando excepciones.
Comprobación de error al abrir un archivo

1
# Error al abrir un archivo
2 try:

3 mi_archivo = open("miarchivo.txt")

except:
4
print("Error al abrir el archivo")
5
Podemos capturar y procesar, de maneras distintas,
muchos tipos de errores. Por ejemplo, sería muy útil
mostrarle al usuario un mensaje de error más preciso
que un simple; “ocurrió un error.”

En el siguiente ejemplo, entra las líneas 5-8, podemos


encontrarnos con diferentes tipos de errores. Si
colocamos un IOError luego de un except en la línea 9, solo
los errores referidos a Entradas y Salidas (Input/Output)
serán manejados por esta parte del código. Igualmente,
la línea 11 solo maneja errores referidos a conversión de
valores, y la línea 13, errores producidos por la división
entre cero. La última excepción ocurre en la línea 15.
Como en esta línea no se incluye ningún tipo particular
de error, en ella se gestionarán aquellos errores no
cubiertos por los anteriores bloques except. La
sentencia except“captura-todo” debe ser siempre la
última.

En la línea 1 se importa la biblioteca sys, que será


utilizada en la línea 16 para imprimir el tipo de error
ocurrido.

1 import sys

# Errores múltiples
2
try:
3 mi_archivo = open("miarchivo.txt")

4 mi_linea = mi_archivo.readline()
5 mi_entero = int(s.strip())

mi_valor_calculado = 101 / mi_entero


6
except IOError:
7 print("I/O error")

8 except ValueError:

print("No he podido convertir el dato a número entero.")


9
except ZeroDivisionError:
10
print("División entre cero, error")
11 except:

12 print("Error Inesperado:", sys.exc_info()[0])

13
14
15
16

Una lista de las excepciones implementadas en Python


se encuentra disponible en la siguiente dirección web:
http://docs.python.org/library/exceptions.html

18.3 Ejemplo: Almacenar La


Máxima Puntuación
Aquí mostraremos cómo guardar la máxima
puntuación (high score) entre juegos. Esta puntuación
será almacenada en un archivo llamado high_score.txt.
high_score.py

1 """
2 # Sample Python/Pygame Programs

# Simpson College Computer Science


3
# http://programarcadegames.com/
4 # http://simpson.edu/computer-science/

5 """

def obtener_puntuacion_mas_alta():
6
# Puntuación más alta por defecto
7
puntuacion_mas_alta = 0
8 # Intentemos leer la puntuación más alta desde un archivo

9 try:

archivo_puntuacion_mas_alta = open("high_score.txt", "r")


10
puntuacion_mas_alta = int(archivo_puntuacion_mas_alta.read())
11 archivo_puntuacion_mas_alta.close()
12 print("La puntuación más alta es", puntuacion_mas_alta)

13 except IOError:

# Error al leer el archivo, no existe una puntuación más alta


14
print("Aún no existe una puntuación más alta.")
15 except ValueError:

16 # Hay un archivo allí, pero no entiendo los números.

print("Estoy confundido. Empezamos sin una puntuación alta.")


17
return puntuacion_mas_alta
18
def guardar_puntuacion_mas_alta(nueva_puntuacion_mas_alta):
19 try:

20 # Escribimos el archivo en disco

archivo_puntuacion_mas_alta = open("high_score.txt", "w")


21
archivo_puntuacion_mas_alta.write(str(nueva_puntuacion_mas_alta))
22 archivo_puntuacion_mas_alta.close()

23 except IOError:

# Um, no puedo escribirlo.


24
print("No soy capaz de guardar la puntuación alta.")
25
def main():
26 """ Aquí va el programa principal. """
27 # Obtenemos la puntuación más alta.

puntuacion_mas_alta = obtener_puntuacion_mas_alta()
28
# Obtenemos la puntuación del juego en curso
29 puntuacion_actual = 0

30 try:

# Pregúntale al usuario/a por su puntuación


31
puntuacion_actual = int(input ("¿Cuál es tu puntuación? "))
32
except ValueError:
33 # Error, no puedo convertir lo que ha escrito en un número

34 print("No comprendo qué has escrito.")

# Observa por si tenemos una nueva puntuación más alta


35
if puntuacion_actual > puntuacion_mas_alta:
36 # Conseguido! Guardamos en disco
37 print("Bravo! Nueva puntuación más alta!")

38 guardar_puntuacion_mas_alta(puntuacion_actual)

else:
39
print("Mejor suerte la próxima vez.")
40 # Llamamos a la función principal, empezamos el juego

41 if __name__ == "__main__":

main()
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

18.4 Objetos del tipo Excepción


A través de un objeto excepción, podemos extraer más
información acerca de un error. Este objeto puede ser
recogido mientras capturamos un error, usando la
palabra clave as. Por ejemplo:
Creamos una excepción

1 try:
2 x =5/0

3 except ZeroDivisionError as e:

print(e)
4
La variable e apunta hacia una información imprimible
más amplia sobre la excepción en cuestión. Podemos
hacer más cosas con los objetos excepción, pero
lamentablemente, eso está más allá del ámbito de este
capítulo.

18.5 Generamos Una Excepción


Las excepciones pueden generarse con el comando raise.
Por ejemplo:
Creamos una excepción

1
2 # Generamos excepciones

def get_input():
3
entrada_usuario = input("Escribe algo: ")
4
if len(entrada_usuario) == 0:
5 raise IOError("No has escrito nada")

6 get_input()

Usando el código anterior, intenta añadir una excepción que maneje


el IOError levantado.

También es posible crearnos excepciones personalizadas, pero esto


también está fuera del ámbito de este capítulo. Los que tengan más
curiosidad, pueden acudir a:
http://docs.python.org/tutorial/errors.html#raising-exceptions
18.6 Uso Apropiado de las Excepciones
Las excepciones no deberían ser utilizadas cuando una simple
declaración if podría manejar la situación. El código normal no debería
lanzar excepciones durante lo que se conoce como “happy path”
(”prueba feliz”?). Un código try/catch bien implementado es fácil de
leer, pero un código que incluye muchas excepciones y saltos puede ser
una pesadilla a la hora de depurar. (Una vez tuve la tarea de depurar un
código que leía un documento XML. El código generaba docenas de
excepciones para cada línea que leía del archivo. Todo era enormemente
lento y sujeto a errores. Ese código no debería haber generado una sola
excepción en el curso normal de leer un archivo.)

18.7 Repaso
18.7.1 Test

Haz click para ir al Test.

18.7.2 Ejercicios

Haz click para ir a los Ejercicios.


Chapter 18: Excepciones
Vídeo: Gestión de Excepciones

¿Que te parecería si, por ejemplo, tu programa no


funcionara bien, evitar que el usuario viese los típicos
mensajes rojos de error de Python, o que el programa no
se colgara? Si es así, lo que necesitas son excepciones.

Las excepciones las usamos para gestionar condiciones


anormales que puedan aparecer durante la ejecución del
código. Habitualmente se usan para operaciones de red
o sobre archivos. Esto permite que el código salga airoso
si ocurre, por ejemplo, un desbordamiento del disco, un
error de red o problemas con determinados permisos.

18.1 Vocabulario
Los términos y frases más habituales cuando trabajamos
con excepciones son:

• Excepción: Este término puede significar dos cosas.


Primero, la condición que resulta de un flujo
anormal del programa. O se puede usar para
referirse al objeto que representa esa condición.
Cada excepción posee un objeto que guarda la
información sobre ella.
• Gestión de la excepción: El proceso de manejo de una
excepción para conseguir un flujo normal del
programa.
• Bloque de captura o bloque de excepción: Es el código
que maneja una condición anormal, y se dice que
“captura” la excepción.
• Lanzar (Throw) o levantar (raise): Cuando se ha
detectado alguna condición anormal en el flujo del
programa, se crea una instancia para el objeto
excepcional, entonces es “levantado” hacia un
código que lo capturará.
• Excepciones no gestionadas o Excepciones no
capturadas: Una excepción que ha sido lanzada
pero nunca capturada. Habitualmente termina en
error y el programa se cuelga o cierra
inesperadamente.
• Bloque try: Contiene el código que al ejecutarse
podría generar una excepción.

La mayoría de lenguajes usan términos como “lanzar” y


“capturar.” Desafortunadamente no es el caso de
Python. Python emplea “levantar” y “excepción.”
Nosotros hemos introducido los términos
lanzar/capturar porque son los más utilizados en la
industria.
18.2 Gestión de Excepciones
El código para manejar las excepciones es sencillo.
Observa el siguiente ejemplo:
Manejo de la división entre cero

1
# Dividir entre cero
2 try:

3 x =5/0

except:
4
print("Error división entre cero")
5

Sobre la línea 2 tenemos la sentencia try. Todas las líneas


indentadas siguientes pertenecen al “bloque try.”
Debajo del bloque try no debería existir ningún
código no-indentado que no empezara con la
sentencia except. La sentencia try define una sección que el
código intentará ejecutar.

Si apareciera cualquier excepción durante el


procesamiento del código, la ejecución saltaría
inmediatamente hacia el “bloque de captura” (“catch
block.”) Esta parte del bloque se halla indentada bajo la
sentencia except de la línea 4. Éste es el código responsable
de manejar el error.
Podemos tener un programa que use excepciones para
capturar errores que puedan aparecer durante la
conversión de un texto a número, como por ejemplo:
Manejo de errores durante la conversión a número

1
# Conversión fallida a número
2 try:

3 x = int("fred")

except:
4
print ("Error al intentar convertir fred a número")
5

En la línea 3 se lanzará una excepción debido a que


“fred” no puede ser convertido a número entero. El
código de la línea 5 imprimirá el mensaje de error.

Debajo podemos ver una versión ampliada del ejemplo


anterior. En este caso se comprueba si existe algún error
en la entrada de usuario, asegurándose de que se ha
introducido un número entero. Si el usuario no lo
introduce, el programa continuará demandándole que
lo haga. El código utiliza una excepción para capturar
un posible error de conversión que pudiera ocurrir en la
línea 5. Si se introdujera cualquier otra cosa que no fuera
un entero, se lanzaría una excepción al intentarse la
conversión en la línea 5. Si así fuera, el código de la línea
6, que establece numero_introducido a True, no se ejecutaría.
Una versión mejorada de la gestión de errores durante la conversión a número

1
numero_introducido = False
2
while not numero_introducido:
3 numero_cadena = input("Introduce un número entero: ")
4 try:

5 n = int(numero_cadena)

numero_introducido = True
6
except:
7 print ("Error, número entero incorrecto")

Los archivos son particularmente proclives a errores


durante las operaciones con ellos; el disco podría
llenarse por completo, un usuario podría borrar un
archivo mientras se escribe sobre él, o la unidad USB ser
extraída en mitad de una operación de lectura/escritura.
Este tipo de errores pueden ser fácilmente capturados
usando excepciones.
Comprobación de error al abrir un archivo

1
# Error al abrir un archivo
2 try:

3 mi_archivo = open("miarchivo.txt")

except:
4
print("Error al abrir el archivo")
5
Podemos capturar y procesar, de maneras distintas,
muchos tipos de errores. Por ejemplo, sería muy útil
mostrarle al usuario un mensaje de error más preciso
que un simple; “ocurrió un error.”

En el siguiente ejemplo, entra las líneas 5-8, podemos


encontrarnos con diferentes tipos de errores. Si
colocamos un IOError luego de un except en la línea 9, solo
los errores referidos a Entradas y Salidas (Input/Output)
serán manejados por esta parte del código. Igualmente,
la línea 11 solo maneja errores referidos a conversión de
valores, y la línea 13, errores producidos por la división
entre cero. La última excepción ocurre en la línea 15.
Como en esta línea no se incluye ningún tipo particular
de error, en ella se gestionarán aquellos errores no
cubiertos por los anteriores bloques except. La
sentencia except“captura-todo” debe ser siempre la
última.

En la línea 1 se importa la biblioteca sys, que será


utilizada en la línea 16 para imprimir el tipo de error
ocurrido.

1 import sys

# Errores múltiples
2
try:
3 mi_archivo = open("miarchivo.txt")

4 mi_linea = mi_archivo.readline()
5 mi_entero = int(s.strip())

mi_valor_calculado = 101 / mi_entero


6
except IOError:
7 print("I/O error")

8 except ValueError:

print("No he podido convertir el dato a número entero.")


9
except ZeroDivisionError:
10
print("División entre cero, error")
11 except:

12 print("Error Inesperado:", sys.exc_info()[0])

13
14
15
16

Una lista de las excepciones implementadas en Python


se encuentra disponible en la siguiente dirección web:
http://docs.python.org/library/exceptions.html

18.3 Ejemplo: Almacenar La


Máxima Puntuación
Aquí mostraremos cómo guardar la máxima
puntuación (high score) entre juegos. Esta puntuación
será almacenada en un archivo llamado high_score.txt.
high_score.py

1 """
2 # Sample Python/Pygame Programs

# Simpson College Computer Science


3
# http://programarcadegames.com/
4 # http://simpson.edu/computer-science/

5 """

def obtener_puntuacion_mas_alta():
6
# Puntuación más alta por defecto
7
puntuacion_mas_alta = 0
8 # Intentemos leer la puntuación más alta desde un archivo

9 try:

archivo_puntuacion_mas_alta = open("high_score.txt", "r")


10
puntuacion_mas_alta = int(archivo_puntuacion_mas_alta.read())
11 archivo_puntuacion_mas_alta.close()
12 print("La puntuación más alta es", puntuacion_mas_alta)

13 except IOError:

# Error al leer el archivo, no existe una puntuación más alta


14
print("Aún no existe una puntuación más alta.")
15 except ValueError:

16 # Hay un archivo allí, pero no entiendo los números.

print("Estoy confundido. Empezamos sin una puntuación alta.")


17
return puntuacion_mas_alta
18
def guardar_puntuacion_mas_alta(nueva_puntuacion_mas_alta):
19 try:

20 # Escribimos el archivo en disco

archivo_puntuacion_mas_alta = open("high_score.txt", "w")


21
archivo_puntuacion_mas_alta.write(str(nueva_puntuacion_mas_alta))
22 archivo_puntuacion_mas_alta.close()

23 except IOError:

# Um, no puedo escribirlo.


24
print("No soy capaz de guardar la puntuación alta.")
25
def main():
26 """ Aquí va el programa principal. """
27 # Obtenemos la puntuación más alta.

puntuacion_mas_alta = obtener_puntuacion_mas_alta()
28
# Obtenemos la puntuación del juego en curso
29 puntuacion_actual = 0

30 try:

# Pregúntale al usuario/a por su puntuación


31
puntuacion_actual = int(input ("¿Cuál es tu puntuación? "))
32
except ValueError:
33 # Error, no puedo convertir lo que ha escrito en un número

34 print("No comprendo qué has escrito.")

# Observa por si tenemos una nueva puntuación más alta


35
if puntuacion_actual > puntuacion_mas_alta:
36 # Conseguido! Guardamos en disco
37 print("Bravo! Nueva puntuación más alta!")

38 guardar_puntuacion_mas_alta(puntuacion_actual)

else:
39
print("Mejor suerte la próxima vez.")
40 # Llamamos a la función principal, empezamos el juego

41 if __name__ == "__main__":

main()
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

18.4 Objetos del tipo Excepción


A través de un objeto excepción, podemos extraer más
información acerca de un error. Este objeto puede ser
recogido mientras capturamos un error, usando la
palabra clave as. Por ejemplo:
Creamos una excepción

1 try:
2 x =5/0

3 except ZeroDivisionError as e:

print(e)
4
La variable e apunta hacia una información imprimible
más amplia sobre la excepción en cuestión. Podemos
hacer más cosas con los objetos excepción, pero
lamentablemente, eso está más allá del ámbito de este
capítulo.

18.5 Generamos Una Excepción


Las excepciones pueden generarse con el comando raise.
Por ejemplo:
Creamos una excepción

1
2 # Generamos excepciones

def get_input():
3
entrada_usuario = input("Escribe algo: ")
4
if len(entrada_usuario) == 0:
5 raise IOError("No has escrito nada")

6 get_input()

Usando el código anterior, intenta añadir una excepción


que maneje el IOError levantado.

También es posible crearnos excepciones


personalizadas, pero esto también está fuera del ámbito
de este capítulo. Los que tengan más curiosidad, pueden
acudir a:
http://docs.python.org/tutorial/errors.html#raising-
exceptions

18.6 Uso Apropiado de las Excepciones


Las excepciones no deberían ser utilizadas cuando una simple
declaración if podría manejar la situación. El código normal no debería
lanzar excepciones durante lo que se conoce como “happy path”
(”prueba feliz”?). Un código try/catch bien implementado es fácil de
leer, pero un código que incluye muchas excepciones y saltos puede ser
una pesadilla a la hora de depurar. (Una vez tuve la tarea de depurar un
código que leía un documento XML. El código generaba docenas de
excepciones para cada línea que leía del archivo. Todo era enormemente
lento y sujeto a errores. Ese código no debería haber generado una sola
excepción en el curso normal de leer un archivo.)

18.7 Repaso
18.7.1 Test

Haz click para ir al Test.

18.7.2 Ejercicios

Haz click para ir a los Ejercicios.


Chapter 19: Recursividad
La pequeña niña no podía dormir, entonces, su mamá le
contó la historia de una ranita,
que no podía dormir, entonces, la mamá de la ranita le
contó la historia de un osito,
que no podía dormir, entonces, la mamá del osito le
contó la historia de una pequeña comadreja...
que se durmió.
...y el pequeño osito se durmió;
...y la ranita se durmió;
...y la niña se durmió.
(Fuente: http://everything2.com/title/recursion)

La recursividad es un objeto o proceso que se define en


términos de sí mismo. Ciertos patrones matemáticos
como los factoriales y la sucesión de Fibonacci son
recursivos. Documentos que contienen documentos que
a su vez contienen otros documentos, son recursivos.
Las imágenes fractales y ciertos procesos biológicos son
recursivos en su forma de producirse.
19.1 ¿Dónde Utilizamos la
Recursividad?
Ciertos documentos, como las páginas webs, son en
esencia recursivos. Por ejemplo, la Figura 19.1 nos
muestra un documento web.

Figure 19.1: Página Web

Ese documento puede estar dentro de una “caja,” la cual


ayuda a estructurar el contenido de la página, tal como
se ve en la Figura 19.2.

Figure 19.2:
Página Web con tablas
Esto funciona recursivamente. Cada caja contiene una
página web, que puede tener una caja, la cual podría
contener otra página web, tal como vemos en la
Figura 19.3.

Figure 19.3:
Página Web con recursividad

A menudo empleamos funciones recursivas para


búsquedas avanzadas y algoritmos de ordenamiento.
Veremos algunas de ellas aquí, y si en algún momento
sigues un curso sobre “estructura de datos”, verás
seguramente muchas más.

Aún en el caso de que una persona no se convierta en


programadora, comprender el concepto de los sistemas
recursivos es importante. Si en un determinado
proyecto surgiera la necesidad de trabajar con tablas,
documentos, o cosas por el estilo, recursivos, es
importante saber cómo explicar esto a la persona que
vaya a encargarse de la programación.

Supongamos, por ejemplo, que alguien quisiera que su


web de recetas tuviese la capacidad de mantener una
lista con ingredientes e instrucciones. Ahora,
imaginemos una segunda persona familiarizada con la
recursividad, la cual podría plantear que, en lugar de
hacer listas, tengamos la capacidad de combinar
ingredientes, con lo que obtendríamos recetas. Este
segundo sistema es evidentemente más efectivo.

19.2 ¿De Qué Forma Codificamos


la Recursividad?
En anteriores capítulos hemos usado funciones que
llamaban a otras funciones. Por ejemplo:
Funciones que llaman a otras funciones

1 def f():

g()
2
print("f")
3
def g():
4 print("g")

5 f()
6
7
8

También es posible que una función se llame a sí misma.


Es decir, haciendo uso de un concepto
llamadorecursividad. Por ejemplo:
Recursion

1
def f():
2
print("Hola")
3
f()
4 f()

El ejemplo anterior imprimirá Hola y luego llamará otra


vez a la función f(). Esto provocará que se imprima
otro Hola y que vuelva a llamarse a f(). Esto seguirá
haciéndose hasta que el ordenador se quede sin algo
llamado stack space (espacio de pila). Cuando sucede esto,
Python mostrará un largo error que finaliza con:
RuntimeError: maximum recursion depth exceeded

El ordenador te está diciendo, a ti programador, que has


ido demasiado lejos en la madriguera.
19.3 Controlar la Profundidad de
la Recursividad
Para usar eficientemente la recursividad, tiene que
existir una forma que evite que la función se llame a sí
misma una y otra vez. El siguiente ejemplo cuenta las
veces se ha llamado, y usa una declaración ifpara salir
cuando la función se llama a sí misma diez veces.

Output:

Llamada
de recur
Controlando los niveles de recursividad sividad,
nivel 1
1
Llamada
2 de recur
def f(nivel): sividad,
3 # Imprime el nivel en el que nos encontramos nivel 2

4 print("Llamada de recursividad, nivel",nivel) Llamada


de recur
# Si aún no hemos llegado al nivel ten... sividad,
5
nivel 3
if nivel < 10:
6 Llamada
# Llama otra vez a la función
de recur
7 # y suma uno al nivel sividad,
nivel 4
8 f(nivel+1)
Llamada
# Comienza las llamadas recursivas en el nivel 1
9 de recur
f(1) sividad,
10 nivel 5
Llamada
11 de recur
sividad,
nivel 6
Llamada
de recur
sividad,
nivel 7
Llamada
de recur
sividad,
nivel 8
Llamada
de recur
sividad,
nivel 9
Llamada
de recur
sividad,
nivel 10

19.4 Cálculo Factorial Usando


Recursividad
Cualquier código que pueda hacerse con recursividad,
también lo puede hacer sin ella. Algunos
programadores piensan que el código recursivo es más
fácil de entender.

Calcular el factorial de un número es un ejemplo clásico


del uso de la recursividad. Los factoriales son muy útiles
en probabilidad y estadística. Por ejemplo:

Recursivamente puede describirse así:

Aquí debajo hay dos ejemplos de funciones que


calculan . La primera no emplea la recursividad, la
segunda sí.
Factorial No Recursivo

1
# Este programa calcula el factorial de un número
2
# SIN emplear recursividad
3 def factorial_norecursivo(n):

4 respuesta = 1

for i in range(2, n+1):


5
respuesta = respuesta * i
6
return respuesta
7
Factorial Recursivo

1
# Este programa calcula el factorial de un número
2
# USANDO recursividad
3 def factorial_recursivo(n):

4 if(n == 1):

return n
5
else:
6
return n * factorial_recursivo(n-1)
7

Por sí solas estas funciones no hacen nada. Debajo hay


un ejemplo donde unimos todo. En él hemos añadido
algunas sentencias print a la función, lo que nos permitirá
ver que está sucediendo.

Experimentando con funciones recursivas Output


# Este programa calcula el factorial de un número :
1
# SIN emplear recursividad
Puedo ca
2
def factorial_norecursivo(n): lcular u
n factor
3 respuesta = 1 ial!
4 for i in range(2, n+1): Introduc
e un núm
print(i,"*",respuesta,"=", i*respuesta)
5 ero:7
respuesta = respuesta * i
2 * 1 =
6 return respuesta 2
7 print("Puedo calcular un factorial!") 3 * 2 =
6
entrada_usuario = input ("Introduce un número: ")
8 4 * 6 =
n = int(entrada_usuario)
24
9
respuesta = factorial_norecursivo(n)
5 * 24 =
10 print(respuesta) 120

11 # Este programa calcula el factorial de un número 6 * 120


= 720
# USANDO recursividad
12 7 * 720
def factorial_recursivo(n): = 5040
13 if( n == 1 ): 5040
14 return n Puedo ca
else: lcular u
15 n factor
x = factorial_recursivo(n-1) ial!
16
print( n, "*", x, "=", n * x ) Introduc
17 return n * x e un núm
ero:7
18 print("Puedo calcular un factorial!")
2 * 1 =
entrada_usuario = input ("Introduce un número:") 2
19
n = int(entrada_usuario) 3 * 2 =
20 6
respuesta = factorial_recursivo(n)
21 print(respuesta)
4 * 6 =
24
22 5 * 24 =
120
23
6 * 120
24 = 720
7 * 720
25 = 5040
26 5040

27
28
29
30
31
32

19.5 Rectángulos Recursivos


La recursividad es fantástica para trabajar con
documentos estructurados que a su vez son recursivos.
Por ejemplo, un documento web que tiene una tabla
dividida en filas y columnas para ayudar en el diseño.
Una fila podría ser el encabezado, otra el cuerpo
principal, y finalmente, otra para el pie. Dentro de una
de las celdas podría ir otra tabla. Y, dentro de ésta, una
tabla más.

Otro ejemplo son los e-mail. Es posible adjuntar el e-


mail de otra persona al tuyo. Pero este e-mail podría
tener otro e-mail adjunto, y así sucesivamente.

¿Podemos observar en acción la recursividad en alguno


de nuestros programas Pygame? Sí! La
Figura 19.4muestra un ejemplo que dibuja un
rectángulo, y recursivamente, continúa dibujando
rectángulos dentro de éste. Cada nuevo rectángulo es un
20% más pequeño que el anterior. Observa el código.
Presta especial atención a llamada de recursividad en la
función recursive_draw.

Figure 19.4: Rectángulos Recursivos

recursive_rectangles.py

1 """

Dibujaremos rectángulos de forma recursiva.


2
Sample Python/Pygame Programs
3
Simpson College Computer Science
4 http://programarcadegames.com/

5 http://simpson.edu/computer-science/
6 """

import pygame
7
# Definimos algunos colores
8 NEGRO = (0, 0, 0)

9 BLANCO = (255, 255, 255)

def dibujo_recursivo(x, y, largo, alto):


10
""" Función para dibujar recursivamente rectángulos. """
11
pygame.draw.rect(pantalla, NEGRO,
12 [x,y,largo,alto],

13 1)

# Es el rectángulo lo bastante largo como para volver a dibujarlo?


14
if(largo > 14):
15 # Reducimos
16 x += largo * .1

17 y += alto * .1

largo *= .8
18
alto *= .8
19 # Lo dibujamos otra vez recursivamente

20 dibujo_recursivo(x, y, largo, alto)

pygame.init()
21
# Establecemos el alto y largo de la pantalla
22
dimensiones = [700, 500]
23 pantalla = pygame.display.set_mode(dimensiones)

24 pygame.display.set_caption("Mi Juego")

# Iteramos hasta que el usuario haga click sobre el botón de cerrar


25
hecho = False
26 # Usado para gestionar cuán rápido se actualiza la pantalla

27 reloj = pygame.time.Clock()

# -------- Bucle Principal del Programa -----------


28
while not hecho:
29
for evento in pygame.event.get():
30 if evento.type == pygame.QUIT:
31 hecho = True

# Limpia la pantalla y establece su color de fondo


32
pantalla.fill(BLANCO)
33 # TODO EL CÓDIGO DE DIBUJO DEBERÍA IR DEBAJO DE ESTE COMENTARIO

34 dibujo_recursivo(0, 0, 700, 500)

# TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO


35
# # Avancemos y actualicemos la pantalla con lo que hemos dibujado.
36
pygame.display.flip()
37 # Limitamos a 60 fotogramas por segundo

38 reloj.tick(60)

# Pórtate bien con el IDLE. Si nos olvidamos de esta línea, el programa se 'colgará
39
# en la salida.
40 pygame.quit()
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

19.6 Fractales
Los fractales son definidos recursivamente. Aquí
puedes observar un fractal muy básico, que nos muestra
cómo cambia, en función de cuan “profunda” sea la
recursividad.
Figure 19.5: Fractal Recursivo nivel 0
Figure 19.6: Fractal Recursivo nivel 1
Figure 19.7: Fractal Recursivo nivel 2
Figure 19.8: Fractal Recursivo nivel 3

fractal.py

1 """

2 Ejemplo de Fractales usando recursividad


3 Sample Python/Pygame Programs

Simpson College Computer Science


4
http://programarcadegames.com/
5 http://simpson.edu/computer-science/

6 """

import pygame
7
# Definimos algunos colores
8
NEGRO = (0, 0, 0)
9 BLANCO = (255, 255, 255)

10 def dibujo_recursivo(x, y, largo, alto, cuenta):

# Dibujamos el rectángulo
11
# pygame.draw.rect(pantalla,NEGRO,[x,y,largo,alto],1)
12 pygame.draw.line(pantalla,
13 NEGRO,

14 [x + largo*.25,alto//2+y],

[x + largo*.75,alto//2+y],
15
3)
16 pygame.draw.line(pantalla,

17 NEGRO,

[x+largo*.25,(alto*.5)//2+y],
18
[x+largo*.25,(alto*1.5)//2+y],
19
3)
20 pygame.draw.line(pantalla,

21 NEGRO,

[x + largo*.75,(alto*.5)//2+y],
22
[x + largo*.75,(alto*1.5)//2+y],
23 3)

24 if cuenta > 0:

cuenta -= 1
25
# Arriba izquierda
26
dibujo_recursivo(x, y, largo // 2, alto // 2, cuenta)
27 # Arriba derecha
28 dibujo_recursivo(x + largo // 2, y, largo // 2, alto // 2, cuenta)

# Abajo izquierda
29
dibujo_recursivo(x, y + largo // 2, largo // 2, alto // 2, cuenta)
30 # Abajo derecha

31 dibujo_recursivo(x + largo // 2, y + largo // 2, largo // 2, alto // 2, cuenta)

pygame.init()
32
# Establecemos el alto y largo de la pantalla
33
dimensiones = [700, 500]
34 pantalla = pygame.display.set_mode(dimensiones)

35 pygame.display.set_caption("Mi Juego")

#Iteramos hasta que el usuario haga click sobre el botón de cerrar


36
hecho = False
37 # Usado para gestionar cuán rápido se actualiza la pantalla
38 reloj = pygame.time.Clock()

39 # -------- Bucle Principal del Programa -----------

while not hecho:


40
for evento in pygame.event.get(): # El usuario hizo algo
41 if evento.type == pygame.QUIT: # Si el usuario hace click sobre cerrar

42 hecho = True # Marca que ya lo hemos hecho, de forma que abandonamos el bucle

# Limpia la pantalla y establece su color de fondo


43
pantalla.fill(BLANCO)
44
# TODO EL CÓDIGO DE DIBUJO DEBERÍA IR DEBAJO DE ESTE COMENTARIO
45 nivel_fractal = 3

46 dibujo_recursivo(0, 0, 700, 700, nivel_fractal)

# TODO EL CÓDIGO DE DIBUJO DEBERÍA IR ENCIMA DE ESTE COMENTARIO


47
# # Avancemos y actualicemos la pantalla con lo que hemos dibujado.
48 pygame.display.flip()

49 # Limitamos a 20 fotogramas por segundo

reloj.tick(20)
50
# Pórtate bien con el IDLE. Si nos olvidamos de esta línea, el programa se 'colgará
51
# en la salida.
52 pygame.quit()
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

19.7 Búsqueda Binaria Recursiva


También podemos usar la recursividad para hacer
búsquedas binarias. Aquí debajo hay una búsqueda
binaria no recursiva del Capítulo 17:
Búsqueda binaria no recursiva
def busqueda_binaria_norecursiva(lista_busqueda, clave):
1
limite_inferior = 0
2
limite_superior = len(lista_busqueda) - 1
3 encontrado = False

4 while limite_inferior < limite_superior and encontrado == False:

pos_intermedia = (limite_inferior + limite_superior) // 2


5
if lista_busqueda[pos_intermedia] < clave:
6 limite_inferior = pos_intermedia + 1

7 elif list[pos_intermedia] > clave:

limite_superior = pos_intermedia
8
else:
9
encontrado = True
10 if encontrado:
11 print("El nombre se encuentra en la posición",pos_intermedia)

else:
12
print("El nombre no estaba en la lista.")
13 busqueda_binaria_norecursiva(nombre_en_lista,"Morgiana la Arpía")

14
15
16
17
18
19

La misma búsqueda binaria pero ahora usando


recursividad:
Búsqueda binaria recursiva

1 def busqueda_binaria_recursiva(lista_busqueda,clave, limite_inferior, limite_superi

pos_intermedia = (limite_inferior + limite_superior) // 2


2
if lista_busqueda[pos_intermedia] < clave:
3
busqueda_binaria_recursiva(lista_busqueda,
4 clave,

5 pos_intermedia + 1,

limite_superior)
6
elif lista_busqueda[pos_intermedia] > clave:
7 busqueda_binaria_recursiva(lista_busqueda,

8 clave,

limite_inferior,
9
pos_intermedia )
10
else:
11 print("Encontrado en la posición", pos_intermedia)

12 limite_inferior = 0
13 limite_superior = len(nombre_en_lista) - 1

busqueda_binaria_recursiva(nombre_en_lista,
14
"Morgiana la Arpía",
15 limite_inferior,

16 limite_superior)

17
18
19
20
21

19.8 Repaso
19.8.1 Test

Haz click para ir a los Ejercicios.


Chapter 20: Formatos
Aquí debajo puedes encontrar una tabla de referencias
rápidas para cuando formatees un texto. Para una
explicación detallada de cómo funciona el formateo de
un texto, deberás seguir leyendo.

Número Formato Salida Descripción

3.1415926 {:.2f} 3.14 2 lugares decimales

3.1415926 {:+.2f} +3.14 2 lugares decimales con signo

-1 {:+.2f} -1.00 2 lugares decimales con signo

3.1415926 {:.0f} 3 Sin decimales (se redondeará)

5 {:0>2d} 05 Añade ceros por la izquierda

1000000 {:,} 1,000,000 Formato numérico con separador com

0.25 {:.2%} 25.00% Formato porcentual

1000000000 {:.2e} 1.00e+09 Notación exponencial

11 {:>10d} 11 Alineación a la derecha

11 {:<10d} 11 Alineación a la izquierda


Número Formato Salida Descripción

11 {:^10d} 11 Centrado

Showing 1 to 11 of 11 entries

20.1 Números Decimales


Intenta ejecutar el siguiente programa, imprime unos
cuantos números aleatorios.

1
import random
2
for i in range(10):
3
x = random.randrange(20)
4 print(x)

La salida está alineada por la izquierda y el aspecto de


los números es horrible:
16

13

10

18

14

5
Podemos utilizar el formateo para que, alineándolos
hacia la derecha, la lista de números presente un mejor
aspecto. El primer paso es usar el comando format sobre la
cadena. Observa:

1
import random
2
for i in range(10):
3
x = random.randrange(20)
4 print("{}".format(x) )

Esto consigue que nuestro programa esté a un paso de


alinear a la derecha el número, pero aún no hemos
acabado. Observa que la cadena termina con .format(x).
Realmente, todas las cadenas son instancias de la clase
llamada String. Esta clase posee unos métodos que
podemos invocar. Uno de ellos es el método format.

La función format no imprimirá las llaves {}, en su lugar las


reemplazará con el valor contenido en x. El resultado
(debajo) se parece a lo previamente ya habíamos
obtenido.
7

15

12

3
8

15

12

Para alinear a la derecha, añadimos información extra


sobre cómo se debe formatear el número contenido
entre las llaves {}:
Justificación a la derecha de una lista de números

1
2import random
for i in range(10):
3
x = random.randrange(20)
4print("{:2}".format(x) )

5
7

15

12

15

12

8
Esto está mejor; hemos alineado a la derecha los
números! Pero cómo funciona? La expresión :2 que
hemos añadido es todo menos intuitiva.

Vamos a desglosarlo: los símbolos { } le dicen al


ordenador que vamos a formatear un número. A
continuación del símbolo :, dentro de las llaves, se
encuentra la información para el formato. En nuestro
caso es un 2 que especifica un ancho de campo de dos
caracteres. Este valor le indica al ordenador que tiene
que ajustar el número dentro de un campo de dos
caracteres de ancho. Por defecto, el intentaría justificar a
la derecha los números y a la izquierda el texto.

Mejor aún, el programa ya no necesita llamar a str( ) para


convertir el número a cadena de texto. Podemos dejar
fuera las conversiones a cadena.

¿Pero qué sucede si tienes números grandes? Bien,


primero creamos unos números aleatorios grandes:

1
import random
2
for i in range(10):
3
x = random.randrange(100000)
4 print( "{:6}".format(x) )

5
Esto produce un resultado justificado a la derecha, pero
aún no luce todo lo bien que debiera:

18394
72242
97508
21583
11508
76064
88756
77413
7930
81095

Pero, ¿dónde están las comas? Esta lista tendría un


mejor aspecto poniendo separadores cada tres dígitos.
Observa a continuación cómo lo hacemos :

1
import random
2
for i in range(10):
3
x = random.randrange(100000)
4 print( "{:6,}".format(x) )

65,732
30,248
13,802
17,177
3,584
7,598
21,672
82,900
72,838
48,557

Hemos añadido una coma después del especificador de


ancho de campo, con lo que ahora nuestros números
tienen comas. La coma debe ir después del especificador
de ancho de campo, nunca antes. Las comas son
incluidas en el cálculo del ancho de campo. Por
ejemplo, 1,024 tiene un ancho de campo de 5, y no de 4.

Podemos imprimir varios valores y combinarlos con


texto. Ejecuta el siguiente código:

1 x =5
2 y = 66

3 z = 777

print ("A - '{}' B - '{}' C - '{}'".format(x,y,z) )


4

El programa sustituirá las llaves por números


imprimiendo, además, el texto que hay en la cadena
entrecomillada:

A - '5' B - '66' C - '777'


Si existe un conjunto de tres llaves, el ordenador espera
encontrar tres valores que ordenar con el comando format.
El primer valor que aparezca en la lista sustituirá a la
primera llave.

Algunas veces queremos que un determinado valor


aparezca por duplicado, o a lo mejor, queremos que
aparezca en un orden distinto al que se introduce con la
función format.

1 x =5
2 y = 66

3 z = 777

print ("C - '{2}' A - '{0}' B - '{1}' C y otra vez - '{2}'".format(x,y,z) )


4

Observa que al colocar números entre las llaves,


estamos especificando el orden en que queremos que los
parámetros sean imprimidos por función format. Los
parámetros los enumeramos empezando por 0, de
forma que x es considerado el parámetro 0.

También podemos especificar la información de


formato después de los dos puntos. Por ejemplo:
x =5
1
y = 66
2
z = 777
3 print ("C - '{2:4}' A - '{0:4}' B - '{1:4}' C y otra vez - '{2:4}'".format(x,y,z) )
4

Podemos ver que el código anterior mostrará los valores


justificados hacia la derecha y con un ancho de campo
de tamaño cuatro:

C - ' 777' A - ' 5' B - ' 66' C y otra vez - ' 777'

20.2 Cadenas
Veamos ahora cómo formateamos cadenas de texto.

La siguiente lista tiene un aspecto horrible.

1
mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
2
mis_calorias = [4, 300, 70, 30]
3
for i in range(4):
4 print(mi_fruta[i], "son", mis_calorias[i], "calorías.")

Manzanas son 4 calorías.


Naranjas son 300 calorías.
Uvas son 70 calorías.
Peras son 30 calorías.

Ahora intentémoslo usando el comando format. Observa


cómo podemos introducir texto adicional y más de un
valor sobre la misma línea.
Formateando una lista de frutas

1
mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
2
mis_calorias = [4, 300, 70, 30]
3
for i in range(4):
4 print("{:7} son {:3} calorías.".format(mi_fruta[i], mis_calorias[i]) )

Manzanas son 4 calorías.


Naranjas son 300 calorías.
Uvas son 70 calorías.
Peras son 30 calorías.

Esto luce mejor y se parece más a lo que estamos


buscando. ¿Pero qué sucedería si no quisiéramos los
números justificados a la derecha y el texto a la
izquierda? Pues podemos usar los caracteres < y > tal
como se ve a continuación:

1
mi_fruta = ["Manzanas", "Naranjas", "Uvas", "Peras"]
2
mis_calorias = [4, 300, 70, 30]
3
for i in range(4):
4 print("{:>7} son {:<3} calorías.".format(mi_fruta[i], mis_calorias[i]) )

Manzanas son 4 calorías.


Naranjas son 300 calorías.
Uvas son 70 calorías.
Peras son 30 calorías.
20.3 Ceros al Principio
Lo siguiente produce una salida que no está bien del
todo:

1 for horas in range(1, 13):

2 for minutos in range(0, 60):

print( "Hora {}:{}".format(horas, minutos) )


3

Hora 8:56
Hora 8:57
Hora 8:58
Hora 8:59
Hora 9:0
Hora 9:1
Hora 9:2

Necesitamos unos ceros al principio para poder mostrar


los números como se ven en los relojes. En lugar de
emplear 2 para el ancho de campo, utiliza 02. Esto
rellenará el campo con ceros en lugar de con espacios.
Formato del tiempo horario

1 for horas in range(1, 13):

2 for minutos in range(0, 60):

print( "Hora {:02}:{:02}".format(horas, minutos) )


3

Hora 08:56
Hora 08:57
Hora 08:58
Hora 08:59
Hora 09:00
Hora 09:01
Hora 09:02

20.4 Números en Coma Flotante


(Reales)
También podemos controlar los resultados en coma
flotante. Examina el siguiente código y su salida:
Formato de números en coma flotante

1
2 x = 0.1

y = 123.456789
3
print("{:.1} {:.1}".format(x,y))
4
print("{:.2} {:.2}".format(x,y))
5 print("{:.3} {:.3}".format(x,y))

6 print("{:.4} {:.4}".format(x,y))

print("{:.5} {:.5}".format(x,y))
7
print("{:.6} {:.6}".format(x,y))
8 print()

9 print("{:.1f} {:.1f}".format(x,y))

print("{:.2f} {:.2f}".format(x,y))
10
print("{:.3f} {:.3f}".format(x,y))
11
print("{:.4f} {:.4f}".format(x,y))
12 print("{:.5f} {:.5f}".format(x,y))

13 print("{:.6f} {:.6f}".format(x,y))

14
15

0.1 1e+02
0.1 1.2e+02
0.1 1.23e+02
0.1 123.5
0.1 123.46
0.1 123.457

0.1 123.5
0.10 123.46
0.100 123.457
0.1000 123.4568
0.10000 123.45679
0.100000 123.456789

Un formato del tipo .2 significa que el número será


mostrado con una precisión de dos dígitos.
Lamentablemente, esto quiere decir, que si mostramos
el número 123, que tiene tres cifras significativas, en lugar
de redondearlo, lo imprimirá en notación
científica: 1.2e+02.

Un formato del tipo .2f (observa la f) quiere decir que


mostremos el número con dos dígitos después del
punto/coma decimal. De forma que 1 aparecerá
como 1.00 y 1.5555 como 1.56.

También podemos especificar un carácter de ancho de


campo en el programa:
1
2
x = 0.1
3 y = 123.456789
4 print("'{:10.1}' '{:10.1}'".format(x,y))

print("'{:10.2}' '{:10.2}'".format(x,y))
5
print("'{:10.3}' '{:10.3}'".format(x,y))
6
print("'{:10.4}' '{:10.4}'".format(x,y))
7 print("'{:10.5}' '{:10.5}'".format(x,y))

8 print("'{:10.6}' '{:10.6}'".format(x,y))

print()
9
print("'{:10.1f}' '{:10.1f}'".format(x,y))
10
print("'{:10.2f}' '{:10.2f}'".format(x,y))
11 print("'{:10.3f}' '{:10.3f}'".format(x,y))

12 print("'{:10.4f}' '{:10.4f}'".format(x,y))

print("'{:10.5f}' '{:10.5f}'".format(x,y))
13
print("'{:10.6f}' '{:10.6f}'".format(x,y))
14
15

El formato 10.2f no significa que sean 10 dígitos antes y


dos después del punto/coma decimal. Indica un ancho
de campo total de 10. De esta forma, habrán 7 dígitos
antes del punto/coma decimal, el punto/coma contará
como uno más, y dos dígitos después.

' 0.1' ' 1e+02'


' 0.1' ' 1.2e+02'
' 0.1' ' 1.23e+02'
' 0.1' ' 123.5'
' 0.1' ' 123.46'
' 0.1' ' 123.457'

' 0.1' ' 123.5'


' 0.10' ' 123.46'
' 0.100' ' 123.457'
' 0.1000' ' 123.4568'
' 0.10000' ' 123.45679'
' 0.100000' '123.456789'

20.5 Imprimir Dólares y Centavos


Si quieres usar un punto/coma para los precios, puedes
usar una f.Observa:

1
2 precio1 = 3.07

3 impuesto1 = precio1 * 0.06

total1 = precio1 + impuesto1


4
print("Precio: ${0:5.2f}".format(precio1) )
5 print("Impuesto: {0:5.2f}".format(impuesto1) )

6 print("------------")

print("Total: ${0:5.2f}".format(total1) )
7
8

¡Recuerda! Sería más fácil pensar que %5.2f significaría 5


dígitos, un punto/coma, seguido de dos dígitos. Pero no
es así, Esto indica un ancho de campo total de 5,
incluyendo el punto/coma decimal y los dos dígitos
siguientes. Ésta es la salida:
Precio: $ 3.07
Impuesto: 0.18
------------
Total: $ 3.25

Peligro, cuando trabajamos con transacciones


financieras, el código anterior tiene un error muy común
¿Puedes verlo? Intenta hacerlo con el código expandido
siguiente:

1
precio1 = 3.07
2
impuesto1 = precio1 * 0.06
3
total1 = precio1 + impuesto1
4 print("Precio: ${0:5.2f}".format(precio1))

5 print("Impuesto: {0:5.2f}".format(impuesto1))

print("------------")
6
print("Total: ${0:5.2f}".format(total1))
7 precio2 = 5.07
8 impuesto2 = precio2 * 0.06

9 total2 = precio2 + impuesto2

print()
10
print("Precio: ${0:5.2f}".format(precio2))
11 print("Impuesto: {0:5.2f}".format(impuesto2))

12 print("------------")

print("Total: ${0:5.2f}".format(total2))
13
print()
14
gran_total = total1 + total2
15 print("Gran total: ${0:5.2f}".format(gran_total))

16
17
18
19
20
21
22
23

Ésta es la salida:

Precio: $ 3.07
Impuesto: 0.18
------------
Total: $ 3.25

Precio: $ 5.07
Impuesto: 0.30
------------
Total: $ 5.37

Gran total: $ 8.63

¿Localizas el error? ¡Tienes que vigilar los errores de


redondeo! Observa el ejemplo, pareciera que el total
debiera ser $ 8.62, pero no es así. El formato de impresión
no modifica el número, sólo el resultado! Si
modificamos el formato de impresión para que incluya
tres dígitos después del punto/coma decimal, la razón
del error aparecerá más claramente:

Precio: $3.070
Impuesto: 0.184
------------
Total: $3.254

Precio: $5.070
Impuesto: 0.304
------------
Total: $5.374

Gran total: $8.628

Una vez más, el formato de visualización no cambia el


número. Debes usar el comando round para cambiar el
valor y redondearlo verdaderamente. Observa:

1 precio1 = 3.07

impuesto1 = round(precio1 * 0.06,2)


2
total1 = precio1 + impuesto1
3
print("Precio: ${0:5.2f}".format(precio1))
4 print("Impuesto: {0:5.2f}".format(impuesto1))

5 print("------------")

print("Total: ${0:5.2f}".format(total1))
6
precio2 = 5.07
7
impuesto2 = round(precio2 * 0.06,2)
8 total2 = precio2 + impuesto2

9 print()
10 print("Precio: ${0:5.2f}".format(precio2))

print("Impuesto: {0:5.2f}".format(impuesto2))
11
print("------------")
12 print("Total: ${0:5.2f}".format(total2))

13 print()

gran_total = total1 + total2


14
print("Gran total: ${0:5.2f}".format(gran_total))
15
16
17
18
19
20
21
22
23

Precio: $ 3.07
Impuesto: 0.18
------------
Total: $ 3.25

Precio: $ 5.07
Impuesto: 0.30
------------
Total: $ 5.37

Gran total: $ 8.62


El comando round controla cuántos dígitos después del
punto/coma decimal debemos redondear. Nos devuelve
el valor redondeado pero sin cambiar el valor original.
Observa:

1
x = 1234.5678
2 print(round(x, 2))
3 print(round(x, 1))

4 print(round(x, 0))

print(round(x, -1))
5
print(round(x, -2))
6

Observa debajo para que veas, cómo, el modo en que


alimentamos la función round() con valores como -2, para
el número de dígitos luego del punto/coma decimal,
afecta al resultado:

1234.57
1234.6
1235.0
1230.0
1200.0

20.6 Su Uso en Pygame


No tenemos que usar el formateo de cadenas solo para
declaraciones de impresión. El
ejemplo timer_es.pyutiliza formateo de cadena para
volcar en pantalla un texto que simula un reloj:

1
2 # Usamos el formateo de cadenas de Python para añadir ceros al principio

cadena_de_salida = "Hora: {0:02}:{1:02}".format(minutos,segundos)


3
# Lo volcamos en la pantalla
4 texto = font.render(cadena_de_salida, True, NEGRO)

5 screen.blit(texto, [250, 250])

20.7 Repaso
20.7.1 Test

Haz click para ir a los Ejercicios.

Das könnte Ihnen auch gefallen