Python. - Eric Matthes
Python. - Eric Matthes
PYTHON
INTRODUCCIÓN PRÁCTICA A LA PROGRAMACIÓN
BASADA EN PROYECTOS
ERIC MATTHES
, ..........
Descargado Gratis de Lectuflix.com
CONTENIDO EN DETALLE
PÁGINA DE TÍTULO
DERECHOS DE AUTOR
DEDICACIÓN
SOBRE EL AUTOR
EXPRESIONES DE GRATITUD
INTRODUCCIÓN
¿Para quién es este libro?
¿Qué puedes esperar aprender?
Recursos en línea
¿Por qué Python?
Í Ó
Descargado Gratis de Lectuflix.com
CAPÍTULO 3: INTRODUCCIÓN DE LISTAS
¿Qué es una lista?
Acceder a elementos en una lista
Las posiciones del índice comienzan en 0, no en 1
Usar valores individuales de una lista
Ejercicio 3-1: Nombres
Ejercicio 3-2: Saludos
Ejercicio 3-3: su propia lista
Modificar, agregar y eliminar elementos
Modificar elementos en una lista
Agregar elementos a una lista
Eliminar elementos de una lista
Ejercicio 3-4: Lista de invitados
Ejercicio 3-5: cambiar la lista de invitados
Ejercicio 3-6: más invitados
Ejercicio 3-7: Lista de invitados reducida
Organizar una lista
Ordenar una lista de forma permanente con el
método sort()
Ordenar una lista temporalmente con la función
sorted()
Imprimir una lista en orden inverso
Encontrar la longitud de una lista
Ejercicio 3-8: Ver el mundo
Ejercicio 3-9: Invitados a cenar
Ejercicio 3-10: cada función
Evitar errores de índice al trabajar con listas
Ejercicio 3-11: Error intencional
CAPÍTULO 5: DECLARACIONES IF
Un ejemplo sencillo
Pruebas condicionales
Comprobando la igualdad
Ignorar el caso al verificar la igualdad
Comprobando la desigualdad
Comparaciones numéricas
CAPÍTULO 6: DICCIONARIOS
Un diccionario sencillo
Trabajar con diccionarios
Acceder a valores en un diccionario
Agregar nuevos pares clave-valor
Comenzando con un diccionario vacío
Modificar valores en un diccionario
Eliminación de pares clave-valor
Un diccionario de objetos similares
Usando get() para acceder a valores
Ejercicio 6-1: Persona
Ejercicio 6-2: Números favoritos
Ejercicio 6-3: Glosario
Recorriendo un diccionario
Recorriendo todos los pares clave-valor
Recorriendo todas las claves de un diccionario
Recorrer las claves de un diccionario en un orden
particular
Recorriendo todos los valores en un diccionario
Ejercicio 6-4: Glosario 2
Ejercicio 6-5: Ríos
Ejercicio 6-6: sondeo
Anidación
Una lista de diccionarios
Una lista en un diccionario
Un diccionario en un diccionario
Ejercicio 6-7: Personas
Ejercicio 6-8: Mascotas
Ejercicio 6-9: Lugares favoritos
Ejercicio 6-10: números favoritos
Ejercicio 6-11: Ciudades
Ejercicio 6-12: Extensiones
Resumen
CAPÍTULO 8: FUNCIONES
Definiendo una función
Pasar información a una función
Argumentos y parámetros
Ejercicio 8-1: Mensaje
Ejercicio 8-2: Libro favorito
Pasar argumentos
Argumentos posicionales
Argumentos de palabras clave
Valores predeterminados
Llamadas a funciones equivalentes
Evitar errores de argumentación
Ejercicio 8-3: Camiseta
Ejercicio 8-4: Camisas grandes
Ejercicio 8-5: Ciudades
Valores de retorno
Devolver un valor simple
Hacer que un argumento sea opcional
Devolver un diccionario
Usando una función con un bucle while
Ejercicio 8-6: Nombres de ciudades
Ejercicio 8-7: Álbum
Ejercicio 8-8: Álbumes de usuario
Pasar una lista
Modificar una lista en una función
Evitar que una función modifique una lista
Ejercicio 8-9: Mensajes
Ejercicio 8-10: envío de mensajes
Ejercicio 8-11: mensajes archivados
Pasar un número arbitrario de argumentos
Mezclando argumentos posicionales y arbitrarios
Uso de argumentos de palabras clave arbitrarios
Ejercicio 8-12: Sándwiches
Ejercicio 8-13: perfil de usuario
Ejercicio 8-14: Coches
Almacenamiento de funciones en módulos
Importar un módulo completo
Importación de funciones específicas
Usar as para darle un alias a una función
Usar as para darle un alias a un módulo
Importar todas las funciones en un módulo
Funciones de estilo
Ejercicio 8-15: Modelos de impresión
Ejercicio 8-16: Importaciones
Ejercicio 8-17: Funciones de estilo
Resumen
CAPÍTULO 9: CLASES
Crear y usar una clase
Creando la clase de perro
El método __init__()
Crear una instancia a partir de una clase
Ejercicio 9-1: Restaurante
Ejercicio 9-2: Tres restaurantes
Ejercicio 9-3: Usuarios
Trabajar con clases e instancias
La clase de coche
Establecer un valor predeterminado para un
atributo
Modificar valores de atributos
Ejercicio 9-4: Número atendido
Ejercicio 9-5: intentos de inicio de sesión
Herencia
El método __init__() para una clase secundaria
Definición de atributos y métodos para la clase
secundaria
Anulación de métodos de la clase principal
Instancias como atributos
Modelado de objetos del mundo real
Ejercicio 9-6: Puesto de helados
Ejercicio 9-7: Administrador
Ejercicio 9-8: Privilegios
Ejercicio 9-9: Actualización de la batería
Importar clases
Importar una sola clase
Almacenamiento de varias clases en un módulo
Importar varias clases desde un módulo
Importar un módulo completo
Importar todas las clases desde un módulo
Importar un módulo a un módulo
Usando alias
Encontrar su propio flujo de trabajo
Ejercicio 9-10: Restaurante importado
Ejercicio 9-11: Administrador importado
Ejercicio 9-12: Múltiples módulos
La biblioteca estándar de Python
Ejercicio 9-13: Dados
Ejercicio 9-14: Lotería
Ejercicio 9-15: Análisis de lotería
Ejercicio 9-16: Módulo Python de la semana
Clases de estilismo
Resumen
ÍNDICE
ELOGIOS POR EL CURSO INTENSIVO DE
PYTHON
Recursos en línea
No Starch Press tiene más información sobre este libro disponible en
línea en https://nostarch.com/python-crash-course-3rd-edition.
También mantengo un amplio conjunto de recursos complementarios
en https://ehmatthes.github.io/pcc_3e. Estos recursos incluyen lo
siguiente:
Instrucciones de configuración Las instrucciones de configuración
en línea son idénticas a las del libro, pero incluyen enlaces activos
en los que puede hacer clic para seguir los diferentes pasos. Si
tiene algún problema de configuración, consulte este recurso.
Actualizaciones Python, como todos los lenguajes, está en
constante evolución. Mantengo un conjunto completo de
actualizaciones, por lo que si algo no funciona, consulte aquí para
ver si las instrucciones han cambiado.
Soluciones a los ejercicios Debe dedicar mucho tiempo a realizar
los ejercicios de las secciones “Pruébelo usted mismo”. Sin
embargo, si estás estancado y no puedes progresar, las soluciones
para la mayoría de los ejercicios están en línea.
Hojas de referencia También está disponible en línea un conjunto
completo de hojas de referencia descargables para una referencia
rápida a los conceptos principales.
Nota
Python en Windows
Windows normalmente no viene con Python, por lo que
probablemente necesitarás instalarlo y luego instalar VS Code.
Instalación de Python
Primero, verifique si Python está instalado en su sistema. Abra una
ventana de comando ingresando command en el menú Inicio y
haciendo clic en la aplicación Símbolo del sistema. En la ventana de
la terminal, ingrese python en minúsculas. Si recibe un mensaje de
Python (>>>) como respuesta, Python está instalado en su sistema.
Si ve un mensaje de error que le indica que python no es un
comando reconocido, o si se abre la tienda de Microsoft, Python no
está instalado. Cierra la tienda de Microsoft si se abre; Es mejor
descargar un instalador oficial que utilizar la versión de Microsoft.
Si Python no está instalado en su sistema, o si ve una versión
anterior a Python 3.9, necesita descargar un instalador de Python
para Windows. Vaya a https://python.org y coloque el cursor sobre
el enlace Descargas. Debería ver un botón para descargar la última
versión de Python. Haga clic en el botón, que debería comenzar a
descargar automáticamente el instalador correcto para su sistema.
Una vez que haya descargado el archivo, ejecute el instalador.
Asegúrese de seleccionar la opción Agregar Python a PATH, lo que
facilitará la configuración correcta de su sistema. La Figura 1-1
muestra esta opción seleccionada.
Nota
Instalación de código VS
Puede descargar un instalador de VS Code en
https://code.visualstudio.com. Haga clic en el botón Descargar para
Windows y ejecute el instalador. Omita las siguientes secciones
sobre macOS y Linux y siga los pasos en “Ejecución de un programa
Hello World” en la página 9.
Python en MacOS
Python no está instalado de forma predeterminada en las últimas
versiones de macOS, por lo que deberás instalarlo si aún no lo has
hecho. En esta sección, instalará la última versión de Python y luego
instalará VS Code y se asegurará de que esté configurado
correctamente.
Nota
$ python3
Python 3.x.x (v3.11.0:eb0004c271, Jun . . . , 10:03:01)
[Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more
information.
>>>
Nota
Instalación de código VS
Para instalar el editor de VS Code, debe descargar el instalador en
https://code.visualstudio.com. Haga clic en el botón Descargar, luego
abra una ventana del Finder y vaya a la carpeta Descargas. Arrastre
el instalador de Visual Studio Code a su carpeta Aplicaciones y luego
haga doble clic en el instalador para ejecutarlo.
Omita la siguiente sección sobre Python en Linux y siga los pasos en
“Ejecución de un programa Hello World” en la página 9.
Python es Linux
Los sistemas Linux están diseñados para la programación, por lo que
Python ya está instalado en la mayoría de las computadoras Linux.
Las personas que escriben y mantienen Linux esperan que usted
haga su propia programación en algún momento y lo alientan a
hacerlo. Por esta razón, hay muy poco que instalar y sólo algunas
configuraciones que cambiar para comenzar a programar.
$ python3
Python 3.10.4 (main, Apr . . . , 09:04:19) [GCC 11.2.0] on
linux
Type "help", "copyright", "credits" or "license" for more
information.
>>>
Ejecutando hola_mundo.py
Antes de escribir su primer programa, cree una carpeta llamada
python_work en su escritorio para sus proyectos. Es mejor usar
letras minúsculas y guiones bajos para los espacios en los nombres
de archivos y carpetas, porque Python usa estas convenciones de
nomenclatura. Puede crear esta carpeta en otro lugar que no sea el
escritorio, pero será más fácil seguir algunos pasos posteriores si
guarda la carpeta python_work directamente en su escritorio.
Abra VS Code y cierre la pestaña Comenzar si aún está abierta. Cree
un archivo nuevo haciendo clic en Archivo▶Nuevo archivo o
presionando CTRL-N (⌘-N en macOS). Guarde el archivo como
hello_world.py en su carpeta python_work. La extensión .py le dice
a VS Code que su archivo está escrito en Python y le indica cómo
ejecutar el programa y resaltar el texto de una manera útil.
Después de haber guardado su archivo, ingrese la siguiente línea en
el editor:
hola_mundo.py
Solución de problemas
Si no puede ejecutar hello_world.py, aquí hay algunos remedios que
puede probar y que también son buenas soluciones generales para
cualquier problema de programación:
Cuando un programa contiene un error importante, Python
muestra un rastreo, que es un informe de error. Python revisa el
archivo e intenta identificar el problema. Verifique el rastreo;
podría darle una pista sobre qué problema impide que se
ejecute el programa.
Aléjese de su computadora, tómese un breve descanso y luego
vuelva a intentarlo. Recuerde que la sintaxis es muy importante
en la programación, por lo que algo tan simple como comillas o
paréntesis que no coinciden pueden impedir que un programa
se ejecute correctamente. Vuelva a leer las partes relevantes de
este capítulo, revise su código e intente encontrar el error.
Empezar de nuevo. Probablemente no necesites desinstalar
ningún software, pero podría tener sentido eliminar tu archivo
hello_world.py y volver a crearlo desde cero.
Pídale a otra persona que siga los pasos de este capítulo, en su
computadora o en otra diferente, y observe atentamente lo que
hace. Es posible que hayas omitido un pequeño paso que
alguien más ha captado.
Consulte las instrucciones de instalación adicionales en el
Apéndice A; Algunos de los detalles incluidos en el Apéndice
pueden ayudarle a resolver su problema.
Busque a alguien que conozca Python y pídale que le ayude a
configurarlo. Si preguntas, es posible que descubras que
inesperadamente conoces a alguien que usa Python.
Las instrucciones de configuración de este capítulo también
están disponibles a través del sitio web complementario de este
libro en https://ehmatthes.github.io/pcc_3e. La versión en línea
de estas instrucciones podría funcionar mejor porque puede
simplemente cortar y pegar código y hacer clic en los enlaces a
los recursos que necesita.
Pide ayuda en línea. El Apéndice C proporciona una serie de
recursos, como foros y sitios de chat en vivo, donde puede
solicitar soluciones a personas que ya han solucionado el
problema al que se enfrenta actualmente.
No se preocupe si está molestando a programadores
experimentados. Todo programador se ha quedado atascado en
algún momento y la mayoría de los programadores estarán felices de
ayudarlo a configurar su sistema correctamente. Siempre que pueda
indicar claramente lo que está tratando de hacer, lo que ya ha
intentado y los resultados que está obteniendo, es muy probable
que alguien pueda ayudarlo. Como se mencionó en la introducción,
la comunidad Python es muy amigable y acogedora con los
principiantes.
Python debería funcionar bien en cualquier computadora moderna.
Los problemas de configuración temprana pueden resultar
frustrantes, pero vale la pena solucionarlos. Una vez que ejecute
hello_world.py, podrá comenzar a aprender Python y su trabajo de
programación será más interesante y satisfactorio.
En Windows
Puede usar el comando de terminal cd, para cambiar de directorio,
para navegar a través de su sistema de archivos en una ventana de
comandos. El comando dir, para directorio, le muestra todos los
archivos que existen en el directorio actual.
Abra una nueva ventana de terminal e ingrese los siguientes
comandos para ejecutar hello_world.py:
C:\> cd Desktop\python_work
C:\Desktop\python_work> dir
hello_world.py
C:\Desktop\python_work> python hello_world.py
Hello Python world!
En macOS y Linux
Ejecutar un programa Python desde una sesión de terminal es lo
mismo en Linux y macOS. Puede usar el comando de terminal cd,
para cambiar directorio, para navegar a través de su sistema de
archivos en una sesión de terminal. El comando ls, para lista, le
muestra todos los archivos no ocultos que existen en el directorio
actual.
Abra una nueva ventana de terminal e ingrese los siguientes
comandos para ejecutar hello_world.py:
~$ cd Desktop/python_work/
~/Desktop/python_work$ ls
hello_world.py
~/Desktop/python_work$ python3 hello_world.py
Hello Python world!
Los ejercicios de este capítulo son de naturaleza exploratoria. A partir del Capítulo 2,
los desafíos que resolverás se basarán en lo que hayas aprendido.
1-1. python.org: explore la página de inicio de Python (https://python.org) para
encontrar temas que le interesen. A medida que se familiarice con Python, diferentes
partes del sitio le resultarán más útiles.
1-2. Errores tipográficos de Hello World: abra el archivo hello_world.py que acaba de
crear. Haga un error tipográfico en algún lugar de la línea y ejecute el programa
nuevamente. ¿Puedes cometer un error tipográfico que genere un error? ¿Puedes
entender el mensaje de error? ¿Puedes cometer un error tipográfico que no genere un
error? ¿Por qué crees que no cometió un error?
1-3. Habilidades infinitas: si tuvieras infinitas habilidades de programación, ¿qué
construirías? Estás a punto de aprender a programar. Si tiene un objetivo final en
mente, tendrá un uso inmediato para sus nuevas habilidades; Ahora es un buen
momento para escribir breves descripciones de lo que quieres crear. Es un buen hábito
tener un cuaderno de “ideas” al que puedas consultar siempre que quieras comenzar
un nuevo proyecto. Tómate unos minutos para describir tres programas que deseas
crear.
Resumen
En este capítulo, aprendió un poco sobre Python en general e instaló
Python en su sistema si aún no estaba allí. También instaló un editor
de texto para facilitar la escritura de código Python. Ejecutó
fragmentos de código Python en una sesión de terminal y ejecutó su
primer programa, hello_world.py. Probablemente también hayas
aprendido un poco sobre la resolución de problemas.
En el próximo capítulo, aprenderá sobre los diferentes tipos de datos
con los que puede trabajar en sus programas Python y también
comenzará a usar variables.
2
Variables y tipos de datos
simples
variables
Intentemos usar una variable en hello_world.py. Agregue una nueva
línea al comienzo del archivo y modifique la segunda línea:
hola_mundo.py
Ejecute este programa para ver qué sucede. Deberías ver el mismo
resultado que viste anteriormente:
Nota
Nota
Escriba un programa separado para realizar cada uno de estos ejercicios. Guarde cada
programa con un nombre de archivo que siga las convenciones estándar de Python,
usando letras minúsculas y guiones bajos, como simple_message.py y
simple_messages.py.
2-1. Mensaje simple: asigne un mensaje a una variable y luego imprima ese mensaje.
2-2. Mensajes simples: asigne un mensaje a una variable e imprima ese mensaje.
Luego cambie el valor de la variable a un mensaje nuevo e imprima el mensaje nuevo.
Instrumentos de cuerda
Debido a que la mayoría de los programas definen y recopilan algún
tipo de datos y luego hacen algo útil con ellos, es útil clasificar
diferentes tipos de datos. El primer tipo de datos que veremos es la
cadena. Las cuerdas son bastante simples a primera vista, pero
puedes usarlas de muchas maneras diferentes.
Una cadena es una serie de caracteres. Todo lo que esté entre
comillas se considera una cadena en Python, y puedes usar comillas
simples o dobles alrededor de tus cadenas de esta manera:
"This is a string."
'This is also a string.'
Ada Lovelace
ADA LOVELACE
ada lovelace
first_name = "ada"
last_name = "lovelace"
❶ full_name = f"{first_name} {last_name}"
print(full_name)
ada lovelace
Puedes hacer mucho con las cuerdas f. Por ejemplo, puede utilizar
cadenas f para redactar mensajes completos utilizando la
información asociada a una variable, como se muestra aquí:
first_name = "ada"
last_name = "lovelace"
full_name = f"{first_name} {last_name}"
❶ print(f"Hello, {full_name.title()}!")
>>> print("Python")
Python
>>> print("\tPython")
Python
>>> print("Languages:\n\tPython\n\tC\n\tJavaScript")
Languages:
Python
C
JavaScript
Eliminación de prefijos
Cuando se trabaja con cadenas, otra tarea común es eliminar un
prefijo. Considere una URL con el prefijo común https://. Queremos
eliminar este prefijo, para poder centrarnos solo en la parte de la
URL que los usuarios deben ingresar en la barra de direcciones. Aquí
se explica cómo hacerlo:
>>> nostarch_url = 'https://nostarch.com'
>>> nostarch_url.removeprefix('https://')
'nostarch.com'
❶ ^
SyntaxError: unterminated string literal (detected at line 1)
Nota
Guarde cada uno de los siguientes ejercicios como un archivo separado, con un
nombre como name_cases.py. Si te quedas atascado, tómate un descanso o consulta
las sugerencias en el Apéndice C.
2-3. Mensaje personal: utilice una variable para representar el nombre de una persona
e imprima un mensaje para esa persona. Tu mensaje debe ser simple, como “Hola Eric,
¿te gustaría aprender algo de Python hoy?”
2-4. Casos de nombre: use una variable para representar el nombre de una persona y
luego imprima el nombre de esa persona en minúsculas, mayúsculas y título.
2-5. Cita famosa: encuentre una cita de una persona famosa que admire. Imprima la
cita y el nombre de su autor. Su resultado debería verse similar al siguiente, incluidas
las comillas:
Albert Einstein dijo una vez: "Una persona que nunca cometió un error nunca
intentó nada nuevo".
2-6. Cita famosa 2: repita el ejercicio 2-5, pero esta vez, represente el nombre de la
persona famosa usando una variable llamada famous_person. Luego redacta tu mensaje
y represéntalo con una nueva variable llamada message. Imprime tu mensaje.
2-7. Eliminación de nombres: utilice una variable para representar el nombre de una
persona e incluya algunos caracteres de espacio en blanco al principio y al final del
nombre. Asegúrese de utilizar cada combinación de caracteres, "\t" y "\n", al menos
una vez.
Imprima el nombre una vez, para que se muestre el espacio en blanco alrededor del
nombre. Luego imprima el nombre usando cada una de las tres funciones de
eliminación, lstrip(), rstrip() y strip().
2-8. Extensiones de archivo: Python tiene un método removesuffix() que funciona
exactamente como removeprefix(). Asigne el valor 'python_notes.txt' a una variable
llamada filename. Luego use el método removesuffix() para mostrar el nombre del
archivo sin la extensión del archivo, como lo hacen algunos exploradores de archivos.
Números
Los números se utilizan con bastante frecuencia en programación
para llevar la puntuación en los juegos, representar datos en
visualizaciones, almacenar información en aplicaciones web, etc.
Python trata los números de varias maneras diferentes, dependiendo
de cómo se utilicen. Primero veamos cómo Python gestiona los
números enteros, porque son los más sencillos para trabajar.
Enteros
Puede sumar (+), restar (-), multiplicar (*) y dividir (/) enteros en
Python.
>>> 2 + 3
5
>>> 3 - 2
1
>>> 2 * 3
6
>>> 3 / 2
1.5
>>> 3 ** 2
9
>>> 3 ** 3
27
>>> 10 ** 6
1000000
>>> 2 + 3*4
14
>>> (2 + 3) * 4
20
El espaciado en estos ejemplos no tiene ningún efecto sobre cómo
Python evalúa las expresiones; simplemente le ayuda a detectar más
rápidamente las operaciones que tienen prioridad cuando lee el
código.
flotadores
Python llama flotante a cualquier número con punto decimal. Este
término se utiliza en la mayoría de los lenguajes de programación y
se refiere al hecho de que un punto decimal puede aparecer en
cualquier posición de un número. Cada lenguaje de programación
debe diseñarse cuidadosamente para gestionar adecuadamente los
números decimales, de modo que los números se comporten
apropiadamente, sin importar dónde aparezca el punto decimal.
En su mayor parte, puedes utilizar flotadores sin preocuparte por
cómo se comportan. Simplemente ingrese los números que desea
usar y Python probablemente hará lo que espera:
Enteros y flotantes
Cuando divides dos números cualesquiera, incluso si son números
enteros que dan como resultado un número entero, siempre
obtendrás un flotante:
>>> 4/2
2.0
>>> 1 + 2.0
3.0
>>> 2 * 3.0
6.0
>>> 3.0 ** 2
9.0
Asignación múltiple
Puede asignar valores a más de una variable usando una sola línea
de código. Esto puede ayudar a acortar sus programas y hacerlos
más fáciles de leer; Utilizará esta técnica con mayor frecuencia al
inicializar un conjunto de números.
Por ejemplo, así es como puede inicializar las variables x, y y z a
cero:
>>> x, y, z = 0, 0, 0
Constantes
Una constante es una variable cuyo valor permanece igual durante
toda la vida de un programa. Python no tiene tipos constantes
integrados, pero los programadores de Python usan todas las letras
mayúsculas para indicar que una variable debe tratarse como una
constante y nunca modificarse:
MAX_CONNECTIONS = 5000
Cuando desee tratar una variable como una constante en su código,
escriba el nombre de la variable en letras mayúsculas.
2-9. Número ocho: escriba operaciones de suma, resta, multiplicación y división que
den como resultado el número 8. Asegúrese de encerrar sus operaciones en llamadas
print() para ver los resultados. Deberías crear cuatro líneas que se vean así:
print(5+3)
Su resultado debe ser de cuatro líneas, con el número 8 apareciendo una vez en cada
línea.
2-10. Número favorito: utilice una variable para representar su número favorito. Luego,
usando esa variable, crea un mensaje que revele tu número favorito. Imprime ese
mensaje.
Comentarios
Los comentarios son una característica extremadamente útil en la
mayoría de los lenguajes de programación. Todo lo que has escrito
en tus programas hasta ahora es código Python. A medida que sus
programas se vuelven más largos y complicados, debe agregar notas
dentro de sus programas que describan su enfoque general del
problema que está resolviendo. Un comentario le permite escribir
notas en su idioma hablado, dentro de sus programas.
2-11. Agregar comentarios: elija dos de los programas que ha escrito y agregue al
menos un comentario a cada uno. Si no tiene nada específico que escribir porque sus
programas son demasiado simples en este momento, simplemente agregue su nombre
y la fecha actual en la parte superior de cada archivo de programa. Luego escribe una
oración que describa lo que hace el programa.
El zen de Python
Los programadores experimentados de Python lo alentarán a evitar
la complejidad y buscar la simplicidad siempre que sea posible. La
filosofía de la comunidad Python está contenida en "El Zen de
Python" de Tim Peters. Puede acceder a este breve conjunto de
principios para escribir buen código Python ingresando import this
en su intérprete. No reproduciré todo el “Zen de Python” aquí, pero
compartiré algunas líneas para ayudarte a comprender por qué
deberían ser importantes para ti como programador principiante de
Python.
2-12. Zen of Python: ingrese import this en una sesión de terminal de Python y lea los
principios adicionales.
Resumen
En este capítulo aprendiste cómo trabajar con variables. Aprendió a
utilizar nombres de variables descriptivos y a resolver errores de
nombre y de sintaxis cuando surgen. Aprendió qué son las cadenas
y cómo mostrarlas usando minúsculas, mayúsculas y títulos.
Comenzó a utilizar espacios en blanco para organizar la salida de
forma ordenada y aprendió a eliminar elementos innecesarios de
una cadena. Comenzó a trabajar con números enteros y flotantes y
aprendió algunas de las formas en que puede trabajar con datos
numéricos. También aprendió a escribir comentarios explicativos
para que su código sea más fácil de leer para usted y otros.
Finalmente, lees sobre la filosofía de mantener tu código lo más
simple posible, siempre que sea posible.
En el Capítulo 3, aprenderá cómo almacenar colecciones de
información en estructuras de datos llamadas listas. También
aprenderá cómo trabajar con una lista, manipulando cualquier
información en esa lista.
3
Presentación de listas
print(message)
Pruebe estos programas breves para obtener experiencia de primera mano con las
listas de Python. Es posible que desees crear una nueva carpeta para los ejercicios de
cada capítulo, para mantenerlos organizados.
3-1. Nombres: almacena los nombres de algunos de tus amigos en una lista llamada
names. Imprima el nombre de cada persona accediendo a cada elemento de la lista,
uno a la vez.
3-2. Saludos: comience con la lista que utilizó en el ejercicio 3-1, pero en lugar de
simplemente imprimir el nombre de cada persona, imprima un mensaje para ellos. El
texto de cada mensaje debe ser el mismo, pero cada mensaje debe estar
personalizado con el nombre de la persona.
3-3. Tu propia lista: piensa en tu medio de transporte favorito, como una motocicleta o
un automóvil, y haz una lista que contenga varios ejemplos. Utilice su lista para
imprimir una serie de declaraciones sobre estos elementos, como "Me gustaría tener
una motocicleta Honda".
motorcycles[0] = 'ducati'
print(motorcycles)
motorcycles.append('ducati')
print(motorcycles)
motorcycles = []
motorcycles.append('honda')
motorcycles.append('yamaha')
motorcycles.append('suzuki')
print(motorcycles)
motorcycles.insert(0, 'ducati')
print(motorcycles)
del motorcycles[0]
print(motorcycles)
del motorcycles[1]
print(motorcycles)
❷ popped_motorcycle = motorcycles.pop()
❸ print(motorcycles)
❹ print(popped_motorcycle)
¿Cómo podría resultar útil este método pop()? Imaginemos que las
motos de la lista se almacenan en orden cronológico, según cuándo
las tuvimos. Si este es el caso, podemos usar el método pop() para
imprimir un extracto sobre la última motocicleta que compramos:
motorcycles = ['honda', 'yamaha', 'suzuki']
last_owned = motorcycles.pop()
print(f"The last motorcycle I owned was a
{last_owned.title()}.")
first_owned = motorcycles.pop(0)
print(f"The first motorcycle I owned was a
{first_owned.title()}.")
Recuerde que cada vez que usa pop(), el elemento con el que
trabaja ya no se almacena en la lista.
Si no está seguro de si utilizar la instrucción del o el método pop(),
aquí tiene una forma sencilla de decidir: cuando desee eliminar un
elemento de una lista y no utilizarlo de ninguna manera, utilice la
declaración del; Si desea utilizar un elemento mientras lo elimina,
utilice el método pop().
motorcycles.remove('ducati')
print(motorcycles)
❷ too_expensive = 'ducati'
❸ motorcycles.remove(too_expensive)
print(motorcycles)
❹ print(f"\nA {too_expensive.title()} is too expensive for
me.")
Nota
Los siguientes ejercicios son un poco más complejos que los del Capítulo 2, pero le
brindan la oportunidad de utilizar listas en todas las formas descritas.
3-4. Lista de invitados: Si pudieras invitar a cenar a alguien, vivo o fallecido, ¿a quién
invitarías? Haz una lista que incluya al menos tres personas a las que te gustaría invitar
a cenar. Luego use su lista para imprimir un mensaje para cada persona, invitándolas a
cenar.
3-5. Cambio de lista de invitados: acaba de enterarse de que uno de sus invitados no
puede asistir a la cena, por lo que debe enviar un nuevo conjunto de invitaciones.
Tendrás que pensar en alguien más a quien invitar.
Comience con su programa del Ejercicio 3-4. Agrega una llamada print() al final
de tu programa, indicando el nombre del invitado que no puede asistir.
Modifica tu lista, reemplazando el nombre del invitado que no puede asistir por el
nombre de la nueva persona a la que estás invitando.
Imprima un segundo conjunto de mensajes de invitación, uno para cada persona
que todavía esté en su lista.
3-6. Más invitados: acaba de encontrar una mesa de comedor más grande, por lo que
ahora hay más espacio disponible. Piensa en tres invitados más para invitar a cenar.
Comience con su programa del Ejercicio 3-4 o 3-5. Agrega una llamada print()
al final de tu programa, informando a las personas que encontraste una mesa
más grande.
Utilice insert() para agregar un nuevo invitado al principio de su lista.
Utilice insert() para agregar un nuevo invitado al medio de su lista.
Utilice append() para agregar un nuevo invitado al final de su lista.
Imprima un nuevo conjunto de mensajes de invitación, uno para cada persona
de su lista.
3-7. Lista de invitados cada vez más reducida: acaba de descubrir que su nueva mesa
no llegará a tiempo para la cena y ahora tiene espacio para solo dos invitados.
Comience con su programa del Ejercicio 3-6. Agregue una nueva línea que
imprima un mensaje que indique que solo puede invitar a dos personas a cenar.
Utilice pop() para eliminar invitados de su lista uno por uno hasta que solo
queden dos nombres en su lista. Cada vez que saque un nombre de su lista,
imprima un mensaje para esa persona haciéndole saber que lamenta no poder
invitarla a cenar.
Imprima un mensaje para cada una de las dos personas que aún están en su
lista, haciéndoles saber que todavía están invitadas.
Utilice del para eliminar los dos últimos nombres de su lista, de modo que tenga
una lista vacía. Imprima su lista para asegurarse de tener una lista vacía al final
de su programa.
Nota
cars.reverse()
print(cars)
Tenga en cuenta que reverse() no ordena alfabéticamente hacia
atrás; simplemente invierte el orden de la lista:
Nota
3-8. Ver el mundo: piensa en al menos cinco lugares del mundo que te gustaría visitar.
Almacene las ubicaciones en una lista. Asegúrese de que la lista no esté en
orden alfabético.
Imprima su lista en su orden original. No se preocupe por imprimir la lista de
forma ordenada; simplemente imprímalo como una lista de Python sin formato.
Utilice sorted() para imprimir su lista en orden alfabético sin modificar la lista
real.
Demuestre que su lista todavía está en su orden original imprimiéndola.
Utilice sorted() para imprimir su lista en orden alfabético inverso sin cambiar el
orden de la lista original.
Demuestre que su lista todavía está en su orden original imprimiéndola
nuevamente.
Utilice reverse() para cambiar el orden de su lista. Imprima la lista para mostrar
que su orden ha cambiado.
Utilice reverse() para cambiar el orden de su lista nuevamente. Imprima la lista
para mostrar que ha vuelto a su orden original.
Utilice sort()para cambiar su lista para que se almacene en orden alfabético.
Imprima la lista para mostrar que se ha cambiado su orden.
Utilice sort() para cambiar su lista para que se almacene en orden alfabético
inverso. Imprima la lista para mostrar que su orden ha cambiado.
3-9. Invitados a cenar: trabajando con uno de los programas de los ejercicios 3-4 al 3-
7 (páginas 41–42), use len() para imprimir un mensaje que indique la cantidad de
personas que está invitando a cenar.
3-10. Cada función: piensa en cosas que podrías almacenar en una lista. Por ejemplo,
puedes hacer una lista de montañas, ríos, países, ciudades, idiomas o cualquier otra
cosa que desees. Escriba un programa que cree una lista que contenga estos
elementos y luego use cada función presentada en este capítulo al menos una vez.
suzuki
La única vez que este enfoque causará un error es cuando solicite el
último elemento de una lista vacía:
motorcycles = []
print(motorcycles[-1])
Resumen
En este capítulo, aprendió qué son las listas y cómo trabajar con los
elementos individuales de una lista. Aprendiste cómo definir una lista
y cómo agregar y eliminar elementos. Aprendió a ordenar listas de
forma permanente y temporal para fines de visualización. También
aprendió cómo encontrar la longitud de una lista y cómo evitar
errores de índice cuando trabaja con listas.
En el Capítulo 4 aprenderá cómo trabajar con elementos de una lista
de manera más eficiente. Al recorrer cada elemento de una lista
utilizando solo unas pocas líneas de código, podrá trabajar de
manera eficiente, incluso cuando su lista contenga miles o millones
de elementos.
4
Trabajar con listas
alice
david
carolina
Una mirada más cercana al bucle
El bucle es importante porque es una de las formas más comunes en
que una computadora automatiza tareas repetitivas. Por ejemplo, en
un bucle simple como el que usamos en magicians.py, Python
inicialmente lee la primera línea del bucle:
for magician in magicians:
print(magician)
print(magician)
Las dos primeras llamadas a print() se repiten una vez para cada
mago de la lista, como viste anteriormente. Sin embargo, como la
última línea no tiene sangría, se imprime sólo una vez:
Olvidarse de sangrar
Sangra siempre la línea después de la instrucción for en un bucle. Si
lo olvidas, Python te lo recordará:
magos.py
Sangrar innecesariamente
Si accidentalmente sangras una línea que no necesita sangría,
Python te informa sobre la sangría inesperada:
hola_mundo.py
Olvidando el Colón
Los dos puntos al final de una declaración for le dicen a Python que
interprete la siguiente línea como el comienzo de un bucle.
4-1. Pizzas: piensa en al menos tres tipos de tu pizza favorita. Guarde estos nombres
de pizza en una lista y luego use un bucle for para imprimir el nombre de cada pizza.
Modifica tu bucle for para imprimir una oración usando el nombre de la pizza, en
lugar de imprimir solo el nombre de la pizza. Para cada pizza, debe tener una
línea de salida que contenga una declaración simple como Me gusta la pizza de
pepperoni.
Agrega una línea al final de tu programa, fuera del bucle for, que indique cuánto
te gusta la pizza. El resultado debe consistir en tres o más líneas sobre los tipos
de pizza que te gustan y luego una oración adicional, como ¡Realmente amo la
pizza!
4-2. Animales: Piensa en al menos tres animales diferentes que tengan una
característica común. Guarde los nombres de estos animales en una lista y luego use
un bucle for para imprimir el nombre de cada animal.
Modifique su programa para imprimir una declaración sobre cada animal, como
Un perro sería una gran mascota.
Agrega una línea al final de tu programa, indicando qué tienen estos animales en
común. Podrías imprimir una oración como: ¡Cualquiera de estos animales sería
una excelente mascota!
Aunque parece que este código debería imprimir los números del 1
al 5, no imprime el número 5:
1
2
3
4
Este es el resultado:
[1, 2, 3, 4, 5]
[2, 4, 6, 8, 10]
squares = []
for value in range(1, 11):
❶ square = value ** 2
❷ squares.append(square)
print(squares)
squares = []
for value in range(1,11):
squares.append(value**2)
print(squares)
Esta línea hace el mismo trabajo que las líneas dentro del bucle for
en el listado anterior. Cada valor en el bucle se eleva a la segunda
potencia y luego se agrega inmediatamente a la lista de cuadrados.
Puede utilizar cualquiera de estos enfoques cuando esté creando
listas más complejas. A veces, el uso de una variable temporal hace
que el código sea más fácil de leer; otras veces hace que el código
sea innecesariamente largo. Concéntrate primero en escribir código
que entiendas claramente y que haga lo que quieres que haga.
Luego busque enfoques más eficientes mientras revisa su código.
Lista de comprensiones
El enfoque descrito anteriormente para generar la lista squares
consistió en utilizar tres o cuatro líneas de código. Una lista por
comprensión le permite generar esta misma lista en una sola línea
de código. Una lista de comprensión combina el bucle for y la
creación de nuevos elementos en una línea, y agrega
automáticamente cada elemento nuevo. Las listas por comprensión
no siempre se presentan a los principiantes, pero las he incluido aquí
porque lo más probable es que las veas tan pronto como empieces a
mirar el código de otras personas.
El siguiente ejemplo crea la misma lista de números cuadrados que
vio anteriormente, pero utiliza una lista por comprensión:
cuadrados.py
4-3. Contando hasta veinte: utilice un bucle for para imprimir los números del 1 al 20,
inclusive.
4-4. Un millón: haga una lista de los números del uno al un millón y luego use un bucle
for para imprimir los números. (Si la salida tarda demasiado, deténgala presionando
CTRL-C o cerrando la ventana de salida).
4-5. Sumar un millón: haga una lista de los números del uno al un millón y luego use
min() y max() para asegurarse de que su lista realmente comience en uno y termine en
un millón. Además, utilice la función sum() para ver qué tan rápido Python puede
sumar un millón de números.
4-6. Números impares: utilice el tercer argumento de la función range() para hacer
una lista de los números impares del 1 al 20. Utilice un bucle for para imprimir cada
número.
4-7. Tres: haz una lista de los múltiplos de 3, del 3 al 30. Usa un bucle for para
imprimir los números de tu lista.
4-8. Cubos: Un número elevado a la tercera potencia se llama cubo. Por ejemplo, el
cubo de 2 se escribe como 2**3 en Python. Haga una lista de los primeros 10 cubos
(es decir, el cubo de cada número entero del 1 al 10) y use un bucle for para imprimir
el valor de cada cubo.
4-9. Comprensión de cubos: utilice una lista de comprensión para generar una lista de
los primeros 10 cubos.
Trabajar con parte de una lista
En el Capítulo 3 aprendiste cómo acceder a elementos individuales
en una lista, y en este capítulo has aprendido cómo trabajar con
todos los elementos de una lista. También puede trabajar con un
grupo específico de elementos en una lista, llamado sector en
Python.
Nota
Recorriendo un segmento
Puede utilizar un segmento en un bucle for si desea recorrer un
subconjunto de elementos de una lista. En el siguiente ejemplo,
recorremos los primeros tres jugadores e imprimimos sus nombres
como parte de una lista simple:
Primero, hacemos una lista de los alimentos que nos gustan llamada
my_foods. Luego hacemos una nueva lista llamada friend_foods.
Hacemos una copia de my_foods solicitando una porción de my_foods
sin especificar ningún índice ❶, y asignamos la copia a friend_foods.
Cuando imprimimos cada lista, vemos que ambas contienen los
mismos alimentos:
❷ my_foods.append('cannoli')
❸ friend_foods.append('ice cream')
my_foods.append('cannoli')
friend_foods.append('ice cream')
Nota
4-10. Slices: utilizando uno de los programas que escribió en este capítulo, agregue
varias líneas al final del programa que hagan lo siguiente:
Imprimir el mensaje Los primeros tres elementos de la lista son:. Luego use un
segmento para imprimir los primeros tres elementos de la lista de ese programa.
Imprimir el mensaje Tres elementos del medio de la lista son:. Luego use un
segmento para imprimir tres elementos del medio de la lista.
Imprimir el mensaje Los últimos tres elementos de la lista son:. Luego use un
segmento para imprimir los últimos tres elementos de la lista.
4-11. Mis pizzas, tus pizzas: comienza con tu programa del ejercicio 4-1 (página 56).
Haga una copia de la lista de pizzas y llámela friend_pizzas. Luego, haz lo siguiente:
Agregue una nueva pizza a la lista original.
Agrega una pizza diferente a la lista friend_pizzas.
Demuestre que tiene dos listas separadas. Imprima el mensaje Mis pizzas
favoritas son: y luego use un bucle for para imprimir la primera lista. Imprime el
mensaje Las pizzas favoritas de mi amigo son: y luego usa un bucle for para
imprimir la segunda lista. Asegúrese de que cada pizza nueva esté almacenada
en la lista adecuada.
4-12. Más bucles: todas las versiones de Food.py en esta sección han evitado el uso de
bucles for al imprimir para ahorrar espacio. Elija una versión de Foods.py y escriba dos
bucles for para imprimir cada lista de alimentos.
tuplas
Las listas funcionan bien para almacenar colecciones de elementos
que pueden cambiar a lo largo de la vida de un programa. La
capacidad de modificar listas es particularmente importante cuando
trabajas con una lista de usuarios en un sitio web o una lista de
personajes en un juego. Sin embargo, a veces querrás crear una
lista de elementos que no pueden cambiar. Las tuplas te permiten
hacer precisamente eso. Python se refiere a los valores que no
pueden cambiar como inmutables, y una lista inmutable se llama
tupla.
Nota
200
50
Modified dimensions:
400
100
4-13. Buffet: Un restaurante estilo buffet ofrece sólo cinco alimentos básicos. Piensa en
cinco alimentos sencillos y guárdalos en una tupla.
Utilice un bucle for para imprimir cada comida que ofrece el restaurante.
Intente modificar uno de los elementos y asegúrese de que Python rechace el
cambio.
El restaurante cambia su menú, reemplazando dos de los artículos con alimentos
diferentes. Agregue una línea que reescriba la tupla y luego use un bucle for
para imprimir cada uno de los elementos del menú revisado.
Diseñar su código
Ahora que está escribiendo programas más largos, es una buena
idea aprender a diseñar su código de manera consistente. Tómese el
tiempo para hacer que su código sea lo más fácil de leer posible.
Escribir código fácil de leer le ayuda a realizar un seguimiento de lo
que hacen sus programas y también ayuda a otros a comprender su
código.
Los programadores de Python han acordado una serie de
convenciones de estilo para garantizar que el código de todos esté
estructurado aproximadamente de la misma manera. Una vez que
haya aprendido a escribir código Python limpio, debería poder
comprender la estructura general del código Python de cualquier
otra persona, siempre que sigan las mismas pautas. Si esperas
convertirte en un programador profesional en algún momento, debes
comenzar a seguir estas pautas lo antes posible para desarrollar
buenos hábitos.
La guía de estilo
Cuando alguien quiere realizar un cambio en el lenguaje Python,
escribe una propuesta de mejora de Python (PEP). Uno de los PEP
más antiguos es el PEP 8, que instruye a los programadores de
Python sobre cómo diseñar su código. PEP 8 es bastante extenso,
pero gran parte se relaciona con estructuras de codificación más
complejas que las que has visto hasta ahora.
La guía de estilo de Python se escribió teniendo en cuenta que el
código se lee con más frecuencia de lo que se escribe. Escribirá su
código una vez y luego comenzará a leerlo mientras comienza a
depurar. Cuando agrega funciones a un programa, dedicará más
tiempo a leer su código. Cuando comparte su código con otros
programadores, ellos también leerán su código.
Dada la opción entre escribir código que sea más fácil de escribir o
código que sea más fácil de leer, los programadores de Python casi
siempre lo alentarán a escribir código que sea más fácil de leer. Las
siguientes pautas le ayudarán a escribir código claro desde el
principio.
Sangría
PEP 8 recomienda utilizar cuatro espacios por nivel de sangría. El
uso de cuatro espacios mejora la legibilidad y deja espacio para
múltiples niveles de sangría en cada línea.
En un documento de procesamiento de textos, la gente suele utilizar
tabulaciones en lugar de espacios para aplicar sangría. Esto funciona
bien para documentos de procesamiento de textos, pero el
intérprete de Python se confunde cuando las tabulaciones se
mezclan con espacios. Cada editor de texto proporciona una
configuración que le permite usar la tecla TAB pero luego convierte
cada pestaña en una cantidad determinada de espacios.
Definitivamente deberías usar la tecla TAB, pero también asegurarte
de que tu editor esté configurado para insertar espacios en lugar de
tabulaciones en tu documento.
Mezclar tabulaciones y espacios en su archivo puede causar
problemas que son muy difíciles de diagnosticar. Si cree que tiene
una combinación de pestañas y espacios, puede convertir todas las
pestañas de un archivo en espacios en la mayoría de los editores.
Longitud de línea
Muchos programadores de Python recomiendan que cada línea
tenga menos de 80 caracteres. Históricamente, esta directriz se
desarrolló porque la mayoría de las computadoras sólo podían
contener 79 caracteres en una sola línea en una ventana de
terminal. Actualmente, las personas pueden colocar líneas mucho
más largas en sus pantallas, pero existen otras razones para
adherirse a la longitud de línea estándar de 79 caracteres.
Los programadores profesionales suelen tener varios archivos
abiertos en la misma pantalla y el uso de la longitud de línea
estándar les permite ver líneas completas en dos o tres archivos
abiertos uno al lado del otro en la pantalla. PEP 8 también
recomienda limitar todos sus comentarios a 72 caracteres por línea,
porque algunas de las herramientas que generan documentación
automática para proyectos más grandes agregan caracteres de
formato al comienzo de cada línea comentada.
Las pautas de PEP 8 para la longitud de la línea no están escritas en
piedra y algunos equipos prefieren un límite de 99 caracteres. No se
preocupe demasiado por la longitud de las líneas de su código
mientras aprende, pero tenga en cuenta que las personas que
trabajan en colaboración casi siempre siguen las pautas de PEP 8. La
mayoría de los editores le permiten configurar una señal visual,
generalmente una línea vertical en su pantalla, que le muestra
dónde están estos límites.
Nota
Líneas en blanco
Para agrupar visualmente partes de su programa, utilice líneas en
blanco. Debes utilizar líneas en blanco para organizar tus archivos,
pero no lo hagas en exceso. Siguiendo los ejemplos proporcionados
en este libro, debería lograr el equilibrio adecuado. Por ejemplo, si
tiene cinco líneas de código que crean una lista y luego otras tres
líneas que hacen algo con esa lista, es apropiado colocar una línea
en blanco entre las dos secciones. Sin embargo, no debes colocar
tres o cuatro líneas en blanco entre las dos secciones.
Las líneas en blanco no afectarán la forma en que se ejecuta su
código, pero sí afectarán la legibilidad de su código. El intérprete de
Python utiliza sangría horizontal para interpretar el significado de su
código, pero ignora el espaciado vertical.
Resumen
En este capítulo, aprendió cómo trabajar eficientemente con los
elementos de una lista. Aprendiste cómo trabajar con una lista
usando un bucle for, cómo Python usa la sangría para estructurar
un programa y cómo evitar algunos errores de sangría comunes.
Aprendiste a hacer listas numéricas simples, así como algunas
operaciones que puedes realizar en listas numéricas. Aprendió a
dividir una lista para trabajar con un subconjunto de elementos y a
copiar listas correctamente usando una división. También aprendió
sobre las tuplas, que brindan un grado de protección a un conjunto
de valores que no deberían cambiar, y cómo diseñar su código cada
vez más complejo para que sea fácil de leer.
En el Capítulo 5, aprenderá a responder adecuadamente a diferentes
condiciones utilizando declaraciones if. Aprenderá a encadenar
conjuntos relativamente complejos de pruebas condicionales para
responder de manera adecuada exactamente al tipo de situación o
información que está buscando. También aprenderá a usar
declaraciones if mientras recorre una lista para realizar acciones
específicas con elementos seleccionados de una lista.
5
si declaraciones
Audi
BMW
Subaru
Toyota
Comprobando la igualdad
La mayoría de las pruebas condicionales comparan el valor actual de
una variable con un valor de interés específico. La prueba
condicional más simple comprueba si el valor de una variable es
igual al valor de interés:
Comprobando la desigualdad
Cuando desee determinar si dos valores no son iguales, puede
utilizar el operador de desigualdad (!=). Usemos otra declaración if
para examinar cómo usar el operador de desigualdad.
Almacenaremos un ingrediente de pizza solicitado en una variable y
luego imprimiremos un mensaje si la persona no pidió anchoas:
toppings.py
requested_topping = 'mushrooms'
if requested_topping != 'anchovies':
print("Hold the anchovies!")
Comparaciones numéricas
Probar valores numéricos es bastante sencillo. Por ejemplo, el
siguiente código comprueba si una persona tiene 18 años:
>>> age = 18
>>> age == 18
True
También puedes probar para ver si dos números no son iguales. Por
ejemplo, el siguiente código imprime un mensaje si la respuesta
dada no es correcta:
número_magico.py
answer = 17
if answer != 42:
print("That is not the correct answer. Please try
again!")
Expresiones booleanas
A medida que aprenda más sobre programación, en algún momento
escuchará el término expresión booleana. Una expresión booleana
es sólo otro nombre para una prueba condicional. Un valor booleano
es True o False, igual que el valor de una expresión condicional
después de haber sido evaluada.
Los valores booleanos se utilizan a menudo para realizar un
seguimiento de ciertas condiciones, como si un juego se está
ejecutando o si un usuario puede editar cierto contenido en un sitio
web:
game_active = True
can_edit = False
5-1. Pruebas condicionales: escriba una serie de pruebas condicionales. Imprima una
declaración que describa cada prueba y su predicción para los resultados de cada
prueba. Su código debería verse así:
car = 'subaru'
print("Is car == 'subaru'? I predict True.")
print(car == 'subaru')
si declaraciones
Cuando comprenda las pruebas condicionales, podrá comenzar a
escribir declaraciones if. Existen varios tipos diferentes de
declaraciones if y su elección de cuál usar depende de la cantidad
de condiciones que necesita probar. Viste varios ejemplos de
declaraciones if en la discusión sobre pruebas condicionales, pero
ahora profundicemos en el tema.
Declaraciones if simples
El tipo más simple de declaración if tiene una prueba y una acción:
if conditional_test:
do something
age = 19
if age >= 18:
print("You are old enough to vote!")
Declaraciones if-else
A menudo, querrás realizar una acción cuando se pasa una prueba
condicional y una acción diferente en todos los demás casos. La
sintaxis if-else de Python lo hace posible. Un bloque if-else es
similar a una declaración if simple, pero la declaración else le
permite definir una acción o un conjunto de acciones que se
ejecutan cuando se realiza la prueba condicional. falla.
Mostraremos el mismo mensaje que teníamos anteriormente si la
persona tiene edad suficiente para votar, pero esta vez agregaremos
un mensaje para cualquiera que no tenga edad suficiente para votar:
age = 17
❶ if age >= 18:
print("You are old enough to vote!")
print("Have you registered to vote yet?")
❷ else:
print("Sorry, you are too young to vote.")
print("Please register to vote as soon as you turn 18!")
Este código funciona porque sólo tiene dos situaciones posibles para
evaluar: una persona tiene edad suficiente para votar o no tiene
edad suficiente para votar. La estructura if-else funciona bien en
situaciones en las que desea que Python siempre ejecute una de dos
acciones posibles. En una cadena if-else simple como esta siempre
se ejecutará una de las dos acciones.
La cadena if-elif-else
A menudo, necesitarás probar más de dos situaciones posibles y,
para evaluarlas, puedes usar la sintaxis if-elif-else de Python.
Python ejecuta solo un bloque en una cadena if-elif-else. Ejecuta
cada prueba condicional en orden, hasta que una pasa. Cuando se
pasa una prueba, el código que sigue a esa prueba se ejecuta y
Python se salta el resto de las pruebas.
Muchas situaciones del mundo real implican más de dos condiciones
posibles. Por ejemplo, considere un parque de diversiones que cobra
tarifas diferentes para diferentes grupos de edad:
La entrada para cualquier menor de 4 años es gratuita.
La entrada para cualquier persona entre 4 y 18 años cuesta
$25.
La entrada para cualquier persona mayor de 18 años cuesta
$40.
¿Cómo podemos utilizar una declaración if para determinar la tasa
de admisión de una persona? El siguiente código prueba el grupo de
edad de una persona y luego imprime un mensaje de precio de
entrada:
parque_atracciones.py
age = 12
❶ if age < 4:
print("Your admission cost is $0.")
❷ elif age < 18:
print("Your admission cost is $25.")
❸ else:
print("Your admission cost is $40.")
Cualquier edad mayor a 17 años haría que las dos primeras pruebas
fallaran. En estas situaciones, se ejecutaría el bloque else y el precio
de entrada sería de 40$.
En lugar de imprimir el precio de entrada dentro del bloque if-elif-
else, sería más conciso establecer solo el precio dentro del bloque
if-elif -else cadena y luego tener una única llamada print() que
se ejecuta después de la cadena ha sido evaluado:
age = 12
if age < 4:
price = 0
elif age < 18:
price = 25
else:
price = 40
age = 12
if age < 4:
price = 0
elif age < 18:
price = 25
elif age < 65:
price = 40
else:
price = 20
print(f"Your admission cost is ${price}.")
if age < 4:
price = 0
elif age < 18:
price = 25
elif age < 65:
price = 40
elif age >= 65:
price = 20
if 'mushrooms' in requested_toppings:
print("Adding mushrooms.")
❶ if 'pepperoni' in requested_toppings:
print("Adding pepperoni.")
if 'extra cheese' in requested_toppings:
print("Adding extra cheese.")
if 'mushrooms' in requested_toppings:
print("Adding mushrooms.")
elif 'pepperoni' in requested_toppings:
print("Adding pepperoni.")
elif 'extra cheese' in requested_toppings:
print("Adding extra cheese.")
5-3. Alien Colors #1: Imagina que acaban de derribar a un extraterrestre en un juego.
Cree una variable llamada alien_color y asígnele un valor de 'green', 'yellow' o
'red'.
Escribe cinco declaraciones if. Cada uno debe comprobar si un determinado tipo
de fruta está en su lista. Si la fruta está en tu lista, el bloque if debería imprimir
una declaración como: ¡Realmente te gustan los plátanos!
Adding mushrooms.
Adding green peppers.
Adding extra cheese.
requested_toppings = []
if requested_toppings:
for requested_topping in requested_toppings:
print(f"Adding {requested_topping}.")
print("\nFinished making your pizza!")
else:
print("Are you sure you want a plain pizza?")
Adding mushrooms.
Sorry, we don't have french fries.
Adding extra cheese.
¡En solo unas pocas líneas de código, hemos manejado una situación
del mundo real de manera bastante efectiva!
PRUÉBELO USTED MISMO
5-8. Hola administrador: haga una lista de cinco o más nombres de usuario, incluido el
nombre 'admin'. Imagine que está escribiendo un código que imprimirá un saludo para
cada usuario después de iniciar sesión en un sitio web. Recorra la lista e imprima un
saludo para cada usuario.
Si el nombre de usuario es 'admin', imprima un saludo especial, como Hola
administrador, ¿le gustaría ver un informe de estado?
De lo contrario, imprima un saludo genérico, como Hola Jaden, gracias por iniciar
sesión nuevamente.
5-9. Sin usuarios: agregue una prueba if a hello_admin.py para asegurarse de que la
lista de usuarios no esté vacía.
Si la lista está vacía, imprima el mensaje ¡Necesitamos encontrar algunos
usuarios!
Elimine todos los nombres de usuario de su lista y asegúrese de que se imprima
el mensaje correcto.
5-10. Verificación de nombres de usuario: haga lo siguiente para crear un programa
que simule cómo los sitios web garantizan que todos tengan un nombre de usuario
único.
Haga una lista de cinco o más nombres de usuario llamados current_users.
if age < 4:
es mejor que:
if age<4:
5-12. Diseñar declaraciones if: revise los programas que escribió en este capítulo y
asegúrese de haber diseñado sus pruebas condicionales de manera adecuada.
5-13. Tus ideas: en este punto, eres un programador más capaz que cuando
empezaste este libro. Ahora que tiene una mejor idea de cómo se modelan las
situaciones del mundo real en los programas, es posible que esté pensando en algunos
problemas que podría resolver con sus propios programas. Registre cualquier idea
nueva que tenga sobre los problemas que desee resolver a medida que sus habilidades
de programación sigan mejorando. Considere los juegos que quizás desee escribir, los
conjuntos de datos que desee explorar y las aplicaciones web que desee crear.
Resumen
En este capítulo aprendiste cómo escribir pruebas condicionales, que
siempre se evalúan como True o False. Aprendiste a escribir
declaraciones if simples, cadenas if-else y cadenas if-elif-else.
Comenzó a utilizar estas estructuras para identificar condiciones
particulares que necesita probar y saber cuándo se cumplieron esas
condiciones en sus programas. Aprendió a manejar ciertos
elementos en una lista de manera diferente que todos los demás
elementos mientras continuaba utilizando la eficiencia de un bucle
for. También revisó las recomendaciones de estilo de Python para
asegurarse de que sus programas cada vez más complejos sigan
siendo relativamente fáciles de leer y comprender.
En el Capítulo 6 aprenderá sobre los diccionarios de Python. Un
diccionario es similar a una lista, pero le permite conectar piezas de
información. Aprenderá cómo crear diccionarios, recorrerlos en bucle
y usarlos en combinación con listas y declaraciones if. Aprender
sobre diccionarios le permitirá modelar una variedad aún más amplia
de situaciones del mundo real.
6
diccionarios
Un diccionario sencillo
Considere un juego en el que aparecen extraterrestres que pueden
tener diferentes colores y valores en puntos. Este sencillo diccionario
almacena información sobre un extraterrestre en particular:
extraterrestre.py
print(alien_0['color'])
print(alien_0['points'])
green
Puede tener un número ilimitado de pares clave-valor en un
diccionario. Por ejemplo, aquí está el diccionario alien_0 original con
dos pares clave-valor:
new_points = alien_0['points']
print(f"You just earned {new_points} points!")
alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)
extraterrestre.py
alien_0 = {}
alien_0['color'] = 'green'
alien_0['points'] = 5
print(alien_0)
alien_0['color'] = 'yellow'
print(f"The alien is now {alien_0['color']}.")
Primero definimos un diccionario para alien_0 que contiene sólo el
color del extraterrestre; luego cambiamos el valor asociado con la
clave 'color' a 'yellow'. El resultado muestra que el alienígena
efectivamente ha cambiado de verde a amarillo:
Original x-position: 0
New x-position: 2
alien_0['speed'] = 'fast'
❶ del alien_0['points']
print(alien_0)
Nota
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'rust',
'phil': 'python',
}
Nota
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'rust',
'phil': 'python',
}
❶ language = favorite_languages['sarah'].title()
print(f"Sarah's favorite language is {language}.")
favorite_languages['sarah']
Nota
6-1. Persona: utilice un diccionario para almacenar información sobre una persona que
conoce. Almacene su nombre, apellido, edad y la ciudad en la que vive. Debe tener
claves como first_name, last_name, age y city. Imprime cada pieza de información
almacenada en tu diccionario.
6-2. Números favoritos: utilice un diccionario para almacenar los números favoritos de
las personas. Piensa en cinco nombres y úsalos como claves en tu diccionario. Piensa
en un número favorito de cada persona y guárdalo como un valor en tu diccionario.
Imprima el nombre de cada persona y su número favorito. Para divertirse aún más,
encuesta a algunos amigos y obtén datos reales para tu programa.
6-3. Glosario: Se puede utilizar un diccionario de Python para modelar un diccionario
real. Sin embargo, para evitar confusiones, llamémoslo glosario.
Piensa en cinco palabras de programación que has aprendido en los capítulos
anteriores. Utilice estas palabras como claves en su glosario y almacene sus
significados como valores.
Imprima cada palabra y su significado como resultado cuidadosamente
formateado. Puede imprimir la palabra seguida de dos puntos y luego su
significado, o imprimir la palabra en una línea y luego imprimir su significado con
sangría en una segunda línea. Utilice el carácter de nueva línea (\n) para insertar
una línea en blanco entre cada par de palabra y significado en su salida.
Recorriendo un diccionario
Un único diccionario de Python puede contener sólo unos pocos
pares clave-valor o millones de pares. Debido a que un diccionario
puede contener grandes cantidades de datos, Python le permite
recorrer un diccionario. Los diccionarios se pueden utilizar para
almacenar información de diversas formas; por lo tanto, existen
varias formas diferentes de recorrerlos. Puede recorrer todos los
pares clave-valor de un diccionario, sus claves o sus valores.
user_0 = {
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}
user_0 = {
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}
Para escribir un bucle for para un diccionario, cree nombres para las
dos variables que contendrán la clave y el valor en cada par clave-
valor. Puede elegir los nombres que desee para estas dos variables.
Este código funcionaría igual de bien si hubiera usado abreviaturas
para los nombres de las variables, como esta:
for k, v in user_0.items()
Key: username
Value: efermi
Key: first
Value: enrico
Key: last
Value: fermi
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'rust',
'phil': 'python',
}
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'rust',
'phil': 'python',
}
Este bucle for le dice a Python que extraiga todas las claves del
diccionario favorite_languages y las asigne una por una a la variable
name. El resultado muestra los nombres de todos los que realizaron la
encuesta:
Jen
Sarah
Edward
Phil
Recorrer las claves es en realidad el comportamiento
predeterminado cuando se recorre un diccionario, por lo que este
código tendría exactamente el mismo resultado si escribiera:
for name in favorite_languages:
en vez de:
for name in favorite_languages.keys():
❶ if name in friends:
❷ language = favorite_languages[name].title()
print(f"\t{name.title()}, I see you love
{language}!")
favorite_languages = {
--snip--
}
favorite_languages = {
'jen': 'python',
'sarah': 'c',
'edward': 'rust',
'phil': 'python',
}
Este enfoque extrae todos los valores del diccionario sin comprobar
si hay repeticiones. Esto podría funcionar bien con una pequeña
cantidad de valores, pero en una encuesta con una gran cantidad de
encuestados, daría como resultado una lista muy repetitiva. Para ver
cada idioma elegido sin repetición, podemos utilizar un conjunto. Un
conjunto es una colección en la que cada elemento debe ser único:
favorite_languages = {
--snip--
}
Nota
6-4. Glosario 2: Ahora que sabe cómo recorrer un diccionario, limpie el código del
Ejercicio 6-3 (página 99) reemplazando su serie de llamadas print() con un bucle que
recorre las claves y valores del diccionario. Cuando esté seguro de que su bucle
funciona, agregue cinco términos más de Python a su glosario. Cuando vuelva a
ejecutar su programa, estas nuevas palabras y significados deberían incluirse
automáticamente en el resultado.
6-5. Ríos: haz un diccionario que contenga tres ríos principales y el país por el que
pasa cada río. Un par clave-valor podría ser 'nile': 'egypt'.
Utilice un bucle para imprimir una oración sobre cada río, como El Nilo atraviesa
Egipto.
Utilice un bucle para imprimir el nombre de cada río incluido en el diccionario.
Utilice un bucle para imprimir el nombre de cada país incluido en el diccionario.
6-6. Encuesta: use el código en favourite_languages.py (página 96).
Haga una lista de personas que deberían realizar la encuesta de idiomas
favoritos. Incluye algunos nombres que ya están en el diccionario y otros que no.
Recorra la lista de personas que deberían realizar la encuesta. Si ya realizaron la
encuesta, imprima un mensaje agradeciéndoles por responder. Si aún no han
realizado la encuesta, imprima un mensaje invitándolos a realizar la encuesta.
Anidación
A veces querrás almacenar varios diccionarios en una lista o una lista
de elementos como un valor en un diccionario. A esto se le llama
anidamiento. Puede anidar diccionarios dentro de una lista, una lista
de elementos dentro de un diccionario o incluso un diccionario
dentro de otro diccionario. El anidamiento es una característica
poderosa, como lo demostrarán los siguientes ejemplos.
Este ejemplo comienza con una lista vacía para contener todos los
extraterrestres que se crearán. La función range() ❶ devuelve una
serie de números, que simplemente le dice a Python cuántas veces
queremos que se repita el ciclo. Cada vez que se ejecuta el ciclo,
creamos un nuevo alienígena ❷ y luego agregamos cada nuevo
alienígena a la lista aliens ❸. Usamos una porción para imprimir los
primeros cinco alienígenas ❹ y, finalmente, imprimimos la longitud
de la lista para demostrar que en realidad hemos generado la flota
completa de 30 alienígenas:
favorite_languages = {
'jen': ['python', 'rust'],
'sarah': ['c'],
'edward': ['rust', 'go'],
'phil': ['python', 'haskell'],
}
Nota
Un diccionario en un diccionario
Puedes anidar un diccionario dentro de otro diccionario, pero tu
código puede complicarse rápidamente cuando lo haces. Por
ejemplo, si tiene varios usuarios para un sitio web, cada uno con un
nombre de usuario único, puede utilizar los nombres de usuario
como claves en un diccionario. Luego puede almacenar información
sobre cada usuario utilizando un diccionario como valor asociado con
su nombre de usuario. En el siguiente listado, almacenamos tres
datos sobre cada usuario: su nombre, apellido y ubicación.
Accederemos a esta información recorriendo los nombres de usuario
y el diccionario de información asociado con cada nombre de
usuario:
muchos_usuarios.py
users = {
'aeinstein': {
'first': 'albert',
'last': 'einstein',
'location': 'princeton',
},
'mcurie': {
'first': 'marie',
'last': 'curie',
'location': 'paris',
},
Username: aeinstein
Full name: Albert Einstein
Location: Princeton
Username: mcurie
Full name: Marie Curie
Location: Paris
6-7. Personas: Comience con el programa que escribió para el Ejercicio 6-1 (página
98). Cree dos diccionarios nuevos que representen a diferentes personas y almacene
los tres diccionarios en una lista llamada people. Recorre tu lista de personas. A
medida que recorra la lista, imprima todo lo que sepa sobre cada persona.
6-8. Mascotas: Crea varios diccionarios, donde cada diccionario represente una
mascota diferente. En cada diccionario incluye la clase de animal y el nombre del
dueño. Almacene estos diccionarios en una lista llamada pets. A continuación, recorra
su lista y, mientras lo hace, imprima todo lo que sabe sobre cada mascota.
6-9. Lugares favoritos: crea un diccionario llamado favorite_places. Piense en tres
nombres para utilizarlos como claves en el diccionario y guarde de uno a tres lugares
favoritos de cada persona. Para que este ejercicio sea un poco más interesante, pídele
a algunos amigos que mencionen algunos de sus lugares favoritos. Recorre el
diccionario e imprime el nombre de cada persona y sus lugares favoritos.
6-10. Números favoritos: Modifique su programa del Ejercicio 6-2 (página 98) para que
cada persona pueda tener más de un número favorito. Luego imprima el nombre de
cada persona junto con sus números favoritos.
6-11. Ciudades: crea un diccionario llamado cities. Usa los nombres de tres ciudades
como claves en tu diccionario. Cree un diccionario de información sobre cada ciudad e
incluya el país en el que se encuentra la ciudad, su población aproximada y un dato
sobre esa ciudad. Las claves para el diccionario de cada ciudad deberían ser algo así
como country, population y fact. Imprime el nombre de cada ciudad y toda la
información que tengas almacenada sobre ella.
6-12. Extensiones: ahora estamos trabajando con ejemplos que son lo suficientemente
complejos como para poder ampliarlos de varias maneras. Utilice uno de los programas
de ejemplo de este capítulo y amplíelo agregando nuevas claves y valores, cambiando
el contexto del programa o mejorando el formato de la salida.
Resumen
En este capítulo, aprendió cómo definir un diccionario y cómo
trabajar con la información almacenada en un diccionario.
Aprendiste cómo acceder y modificar elementos individuales en un
diccionario, y cómo recorrer toda la información en un diccionario.
Aprendiste a recorrer los pares clave-valor de un diccionario, sus
claves y sus valores. También aprendió a anidar varios diccionarios
en una lista, anidar listas en un diccionario y anidar un diccionario
dentro de un diccionario.
En el siguiente capítulo aprenderá sobre los bucles while y cómo
aceptar entradas de personas que utilizan sus programas. Este será
un capítulo emocionante, porque aprenderá a hacer que todos sus
programas sean interactivos: podrán responder a las entradas del
usuario.
7
Entrada del usuario y bucles
while
Nota
name = input(prompt)
print(f"\nHello, {name}!")
Hello, Eric!
El operador de módulo
Una herramienta útil para trabajar con información numérica es el
operador de módulo (%), que divide un número entre otro número y
devuelve el resto:
>>> 4 % 3
1
>>> 5 % 3
2
>>> 6 % 3
0
>>> 7 % 3
1
if number % 2 == 0:
print(f"\nThe number {number} is even.")
else:
print(f"\nThe number {number} is odd.")
Los números pares siempre son divisibles por dos, por lo que si el
módulo de un número y dos es cero (aquí, if number % 2 == 0) el
número es par. De lo contrario, es extraño.
7-1. Coche de alquiler: escriba un programa que pregunte al usuario qué tipo de coche
de alquiler le gustaría. Imprime un mensaje sobre ese automóvil, como "Déjame ver si
puedo encontrarte un Subaru".
7-2. Asientos en restaurante: escriba un programa que pregunte al usuario cuántas
personas hay en su grupo para cenar. Si la respuesta es más de ocho, imprima un
mensaje diciendo que tendrán que esperar por una mesa. En caso contrario, informe
que su mesa está lista.
7-3. Múltiplos de diez: solicite al usuario un número y luego informe si el número es
múltiplo de 10 o no.
Presentando bucles while
El bucle for toma una colección de elementos y ejecuta un bloque
de código una vez para cada elemento de la colección. Por el
contrario, el bucle while se ejecuta mientras una determinada
condición sea verdadera.
current_number = 1
while current_number <= 5:
print(current_number)
current_number += 1
message = ""
while message != 'quit':
message = input(prompt)
print(message)
message = ""
while message != 'quit':
message = input(prompt)
if message != 'quit':
print(message)
active = True
❶ while active:
message = input(prompt)
if message == 'quit':
active = False
else:
print(message)
❶ while True:
city = input(prompt)
if city == 'quit':
break
else:
print(f"I'd love to go to {city.title()}!")
Nota
current_number = 0
while current_number < 10:
❶ current_number += 1
if current_number % 2 == 0:
continue
print(current_number)
1
3
5
7
9
x = 1
while x <= 5:
print(x)
x += 1
1
1
1
1
--snip--
Nota
7-4. Ingredientes para pizza: escriba un bucle que solicite al usuario que ingrese una
serie de ingredientes para pizza hasta que ingrese un valor 'quit'. A medida que
ingresan cada ingrediente, imprima un mensaje que indique que agregará ese
ingrediente a su pizza.
7-5. Entradas de cine: una sala de cine cobra diferentes precios de entradas según la
edad de la persona. Si es menor de 3 años, la entrada es gratuita; si tienen entre 3 y
12 años, el boleto cuesta $10; y si son mayores de 12 años la entrada cuesta $15.
Escribe un bucle en el que preguntes a los usuarios su edad y luego les digas el coste
de su entrada al cine.
7-6. Tres salidas: escriba diferentes versiones del Ejercicio 7-4 o 7-5 que hagan cada
uno de los siguientes al menos una vez:
Utilice una prueba condicional en la declaración while para detener el ciclo.
Utilice una variable active para controlar cuánto tiempo se ejecuta el bucle.
Utilice una declaración break para salir del bucle cuando el usuario ingrese un
valor 'quit'.
7-7. Infinito: escribe un bucle que nunca termine y ejecútalo. (Para finalizar el bucle,
presione CTRL-C o cierre la ventana que muestra el resultado).
print(pets)
responses = {}
# Set a flag to indicate that polling is active.
polling_active = True
while polling_active:
# Prompt for the person's name and response.
❶ name = input("\nWhat is your name? ")
response = input("Which mountain would you like to climb
someday? ")
7-8. Deli: haz una lista llamada sandwich_orders y rellénala con los nombres de varios
sándwiches. Luego crea una lista vacía llamada finished_sandwiches. Recorra la lista
de pedidos de sándwiches e imprima un mensaje para cada pedido, como I made your
tuna sandwich. A medida que se prepara cada sándwich, muévalo a la lista de
sándwiches terminados. Después de que se hayan preparado todos los sándwiches,
imprima un mensaje enumerando cada sándwich que se preparó.
7-9. Sin pastrami: utilizando la lista sandwich_orders del ejercicio 7-8, asegúrese de
que el sándwich 'pastrami' aparezca en la lista al menos tres veces. Agregue código
cerca del comienzo de su programa para imprimir un mensaje que indique que la
tienda de delicatessen se ha quedado sin pastrami y luego use un bucle while para
eliminar todas las apariciones de 'pastrami' de sandwich_orders. Asegúrate de que
ningún sándwich de pastrami acabe en finished_sandwiches.
7-10. Vacaciones de ensueño: escriba un programa que encueste a los usuarios sobre
las vacaciones de sus sueños. Escribe un mensaje similar a Si pudieras visitar un lugar
en el mundo, ¿adónde irías? Incluya un bloque de código que imprima los resultados
de la encuesta.
Resumen
En este capítulo, aprendió cómo usar input() para permitir que los
usuarios proporcionen su propia información en sus programas.
Aprendió a trabajar con entradas de texto y numéricas y a utilizar
bucles while para que sus programas se ejecuten durante el tiempo
que sus usuarios quieran. Viste varias formas de controlar el flujo de
un bucle while estableciendo un indicador active, usando la
declaración break y usando la declaración continue. Aprendiste cómo
usar un bucle while para mover elementos de una lista a otra y
cómo eliminar todas las instancias de un valor de una lista. También
aprendió cómo se pueden usar los bucles while con los diccionarios.
En el Capítulo 8 aprenderá sobre las funciones. Las funciones le
permiten dividir sus programas en partes pequeñas, cada una de las
cuales realiza un trabajo específico. Puede llamar a una función
tantas veces como desee y puede almacenar sus funciones en
archivos separados. Al utilizar funciones, podrá escribir código más
eficiente que sea más fácil de solucionar problemas y mantener y
que pueda reutilizarse en muchos programas diferentes.
8
Funciones
def greet_user():
"""Display a simple greeting."""
print("Hello!")
greet_user()
Hello!
def greet_user(username):
"""Display a simple greeting."""
print(f"Hello, {username.title()}!")
greet_user('jesse')
Argumentos y parámetros
En la función greet_user() anterior, definimos greet_user() para
requerir un valor para la variable username. Una vez que llamamos a
la función y le dimos la información (el nombre de una persona),
imprimió el saludo correcto.
La variable username en la definición de greet_user() es un ejemplo
de parámetro, una información que la función necesita para realizar
su trabajo. El valor 'jesse' en greet_user('jesse') es un ejemplo
de argumento. Un argumento es una pieza de información que se
pasa de una llamada de función a una función. Cuando llamamos a
la función, colocamos entre paréntesis el valor con el que queremos
que funcione la función. En este caso, el argumento 'jesse' se pasó
a la función greet_user() y el valor se asignó al parámetro username.
Nota
8-1. Mensaje: escriba una función llamada display_message() que imprima una oración
que les cuente a todos lo que está aprendiendo en este capítulo. Llame a la función y
asegúrese de que el mensaje se muestre correctamente.
8-2. Libro favorito: escriba una función llamada favorite_book() que acepte un
parámetro, title. La función debería imprimir un mensaje, como One of my favorite
books is Alice in Wonderland. Llame a la función, asegurándose de incluir el título de
un libro como argumento en la llamada a la función.
Pasar argumentos
Debido a que la definición de una función puede tener múltiples
parámetros, una llamada a una función puede necesitar múltiples
argumentos. Puede pasar argumentos a sus funciones de varias
maneras. Puede utilizar argumentos posicionales, que deben estar
en el mismo orden en que se escribieron los parámetros;
argumentos de palabras clave, donde cada argumento consta de un
nombre de variable y un valor; y listas y diccionarios de valores.
Veamos cada uno de estos por separado.
Argumentos posicionales
Cuando llama a una función, Python debe hacer coincidir cada
argumento en la llamada a la función con un parámetro en la
definición de la función. La forma más sencilla de hacerlo se basa en
el orden de los argumentos proporcionados. Los valores
emparejados de esta manera se denominan argumentos
posicionales.
Para ver cómo funciona esto, considere una función que muestra
información sobre mascotas. La función nos dice qué tipo de animal
es cada mascota y el nombre de la mascota, como se muestra aquí:
mascotas.py
❶ def describe_pet(animal_type, pet_name):
"""Display information about a pet."""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
❷ describe_pet('hamster', 'harry')
I have a hamster.
My hamster's name is Harry.
describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')
I have a dog.
My dog's name is Willie.
describe_pet('harry', 'hamster')
I have a harry.
My harry's name is Hamster.
Si obtiene resultados divertidos como este, verifique que el orden de
los argumentos en su llamada a función coincida con el orden de los
parámetros en la definición de la función.
describe_pet(animal_type='hamster', pet_name='harry')
Valores predeterminados
Al escribir una función, puede definir un valor predeterminado para
cada parámetro. Si se proporciona un argumento para un parámetro
en la llamada a la función, Python usa el valor del argumento. De lo
contrario, utiliza el valor predeterminado del parámetro. Entonces,
cuando defines un valor predeterminado para un parámetro, puedes
excluir el argumento correspondiente que normalmente escribirías
en la llamada a la función. El uso de valores predeterminados puede
simplificar las llamadas a funciones y aclarar las formas en que se
utilizan normalmente las funciones.
Por ejemplo, si observa que la mayoría de las llamadas a
describe_pet() se utilizan para describir perros, puede establecer el
valor predeterminado de animal_type en 'dog'. Ahora cualquiera que
llame a describe_pet() para pedir un perro puede omitir esa
información:
describe_pet(pet_name='willie')
describe_pet('willie')
describe_pet()
8-3. Camiseta: Escribe una función llamada make_shirt() que acepte una talla y el
texto de un mensaje que debe imprimirse en la camiseta. La función debe imprimir una
oración que resuma el tamaño de la camiseta y el mensaje impreso en ella.
Llame a la función una vez usando argumentos posicionales para hacer una camisa.
Llame a la función por segunda vez utilizando argumentos de palabras clave.
8-4. Camisas grandes: Modifique la función make_shirt() para que las camisas sean
grandes por defecto con un mensaje que diga Me encanta Python. Haz una camiseta
grande y una camiseta mediana con el mensaje predeterminado, y una camiseta de
cualquier talla con un mensaje diferente.
8-5. Ciudades: Escribe una función llamada describe_city() que acepte el nombre de
una ciudad y su país. La función debería imprimir una oración simple, como Reykjavik
is in Iceland. Asigne al parámetro del país un valor predeterminado. Llame a su
función para tres ciudades diferentes, al menos una de las cuales no esté en el país
predeterminado.
Valores de retorno
Una función no siempre tiene que mostrar su salida directamente.
En cambio, puede procesar algunos datos y luego devolver un valor
o un conjunto de valores. El valor que devuelve la función se
denomina valor de retorno. La declaración return toma un valor
desde dentro de una función y lo envía de regreso a la línea que
llamó a la función. Los valores de retorno le permiten mover gran
parte del trabajo duro de su programa a funciones, lo que puede
simplificar el cuerpo de su programa.
print("Jimi Hendrix")
Sin embargo, cuando considera trabajar con un programa grande
que necesita almacenar muchos nombres y apellidos por separado,
funciones como get_formatted_name() se vuelven muy útiles.
Almacena los nombres y apellidos por separado y luego llama a esta
función cuando desee mostrar un nombre completo.
Devolver un diccionario
Una función puede devolver cualquier tipo de valor que necesite,
incluidas estructuras de datos más complicadas como listas y
diccionarios. Por ejemplo, la siguiente función toma partes de un
nombre y devuelve un diccionario que representa a una persona:
persona.py
8-6. Nombres de ciudades: escriba una función llamada city_country() que tome el
nombre de una ciudad y su país. La función debería devolver una cadena con el
formato siguiente:
"Santiago, Chile"
Llame a su función con al menos tres pares de ciudad-país e imprima los valores que
se devuelven.
8-7. Álbum: escriba una función llamada make_album() que cree un diccionario que
describa un álbum de música. La función debe incluir el nombre de un artista y el título
de un álbum, y debe devolver un diccionario que contenga estos dos datos. Utilice la
función para crear tres diccionarios que representen diferentes álbumes. Imprima cada
valor de retorno para mostrar que los diccionarios almacenan la información del álbum
correctamente.
Utilice None para agregar un parámetro opcional a make_album() que le permita
almacenar la cantidad de canciones en un álbum. Si la línea de llamada incluye un
valor para la cantidad de canciones, agregue ese valor al diccionario del álbum. Realice
al menos una nueva llamada de función que incluya la cantidad de canciones de un
álbum.
8-8. Álbumes de usuario: comience con su programa del Ejercicio 8-7. Escribe un bucle
while que permita a los usuarios ingresar el artista y el título de un álbum. Una vez
que tenga esa información, llame a make_album() con la entrada del usuario e imprima
el diccionario creado. Asegúrese de incluir un valor de salida en el bucle while.
def greet_users(names):
"""Print a simple greeting to each user in the list."""
for name in names:
msg = f"Hello, {name.title()}!"
print(msg)
Hello, Hannah!
Hello, Ty!
Hello, Margot!
❷ def show_completed_models(completed_models):
"""Show all the models that were printed."""
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
function_name(list_name[:])
print_models(unprinted_designs[:], completed_models)
8-9. Mensajes: haga una lista que contenga una serie de mensajes de texto cortos.
Pase la lista a una función llamada show_messages(), que imprime cada mensaje de
texto.
8-10. Envío de mensajes: comience con una copia de su programa del Ejercicio 8-9.
Escriba una función llamada send_messages() que imprima cada mensaje de texto y
mueva cada mensaje a una nueva lista llamada sent_messages a medida que se
imprime. Después de llamar a la función, imprima ambas listas para asegurarse de que
los mensajes se movieron correctamente.
8-11. Mensajes archivados: comience con su trabajo del Ejercicio 8-10. Llame a la
función send_messages() con una copia de la lista de mensajes. Después de llamar a la
función, imprima ambas listas para mostrar que la lista original ha conservado sus
mensajes.
def make_pizza(*toppings):
"""Print the list of toppings that have been
requested."""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')
def make_pizza(*toppings):
"""Summarize the pizza we are about to make."""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Nota
8-12. Sándwiches: escriba una función que acepte una lista de elementos que una
persona quiere en un sándwich. La función debe tener un parámetro que recopile
tantos elementos como proporcione la llamada a la función y debe imprimir un
resumen del sándwich que se está pidiendo. Llame a la función tres veces, utilizando
una cantidad diferente de argumentos cada vez.
8-13. Perfil de usuario: comience con una copia de user_profile.py de la página 148.
Cree un perfil suyo llamando a build_profile(), utilizando su nombre y apellido y otros
tres pares clave-valor que lo describan.
8-14. Automóviles: escriba una función que almacene información sobre un automóvil
en un diccionario. La función siempre debe recibir un nombre de fabricante y modelo.
Luego debería aceptar un número arbitrario de argumentos de palabras clave. Llame a
la función con la información requerida y otros dos pares de nombre-valor, como un
color o una característica opcional. Su función debería funcionar para una llamada
como esta:
pizza.py
import pizza
❶ pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra
cheese')
module_name.function_name()
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra
cheese')
Funciones de estilo
Debes tener en cuenta algunos detalles al diseñar funciones. Las
funciones deben tener nombres descriptivos y estos nombres deben
usar letras minúsculas y guiones bajos. Los nombres descriptivos le
ayudan a usted y a otros a comprender lo que intenta hacer su
código. Los nombres de los módulos también deben utilizar estas
convenciones.
Cada función debe tener un comentario que explique de manera
concisa qué hace la función. Este comentario debería aparecer
inmediatamente después de la definición de la función y utilizar el
formato de cadena de documentación. En una función bien
documentada, otros programadores pueden usar la función leyendo
solo la descripción en la cadena de documentación. Deberían poder
confiar en que el código funciona como se describe y, siempre que
sepan el nombre de la función, los argumentos que necesita y el tipo
de valor que devuelve, deberían poder utilizarlo en sus programas.
Si especifica un valor predeterminado para un parámetro, no se
deben utilizar espacios a ninguno de los lados del signo igual:
def function_name(parameter_0, parameter_1='default value')
function_name(value_0, parameter_1='value')
def function_name(
parameter_0, parameter_1, parameter_2,
parameter_3, parameter_4, parameter_5):
function body...
import module_name
from module_name import function_name
from module_name import function_name as fn
import module_name as mn
from module_name import *
8-17. Funciones de estilo: elija cualquiera de los tres programas que escribió para este
capítulo y asegúrese de que sigan las pautas de estilo descritas en esta sección.
Resumen
En este capítulo, aprendió a escribir funciones y a pasar argumentos
para que sus funciones tengan acceso a la información que
necesitan para realizar su trabajo. Aprendió a utilizar argumentos
posicionales y de palabras clave, y también a aceptar una cantidad
arbitraria de argumentos. Viste funciones que muestran resultados y
funciones que devuelven valores. Aprendiste a usar funciones con
listas, diccionarios, declaraciones if y bucles while. También vio
cómo almacenar sus funciones en archivos separados llamados
módulos, por lo que sus archivos de programa serán más simples y
fáciles de entender. Finalmente, aprendió a diseñar sus funciones
para que sus programas sigan estando bien estructurados y lo más
fáciles de leer posible para usted y otros.
Uno de tus objetivos como programador debería ser escribir código
simple que haga lo que quieres, y las funciones te ayudarán a
lograrlo. Le permiten escribir bloques de código y dejarlos en paz
una vez que sepa que funcionan. Cuando sabes que una función
hace su trabajo correctamente, puedes confiar en que seguirá
funcionando y pasará a tu siguiente tarea de codificación.
Las funciones le permiten escribir código una vez y luego reutilizarlo
tantas veces como desee. Cuando necesitas ejecutar el código en
una función, todo lo que necesitas hacer es escribir una llamada de
una línea y la función hará su trabajo. Cuando necesita modificar el
comportamiento de una función, solo tiene que modificar un bloque
de código y su cambio tendrá efecto en todos los lugares donde
haya realizado una llamada a esa función.
El uso de funciones hace que sus programas sean más fáciles de leer
y los buenos nombres de funciones resumen lo que hace cada parte
de un programa. Leer una serie de llamadas a funciones le brinda
una idea mucho más rápida de lo que hace un programa que leer
una larga serie de bloques de código.
Las funciones también hacen que su código sea más fácil de probar
y depurar. Cuando la mayor parte del trabajo de su programa se
realiza mediante un conjunto de funciones, cada una de las cuales
tiene una tarea específica, es mucho más fácil probar y mantener el
código que ha escrito. Puede escribir un programa separado que
llame a cada función y pruebe si cada función funciona en todas las
situaciones que pueda encontrar. Cuando hace esto, puede estar
seguro de que sus funciones funcionarán correctamente cada vez
que las llame.
En el Capítulo 9, aprenderá a escribir clases. Las clases combinan
funciones y datos en un paquete ordenado que se puede utilizar de
manera flexible y eficiente.
9
clases
perro.py
❶ class Dog:
"""A simple attempt to model a dog."""
❹ def sit(self):
"""Simulate a dog sitting in response to a
command."""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""Simulate rolling over in response to a command."""
print(f"{self.name} rolled over!")
El método __init__()
Una función que es parte de una clase es un método. Todo lo que
aprendiste sobre funciones se aplica también a los métodos; La
única diferencia práctica por ahora es la forma en que llamaremos a
los métodos. El método __init__() ❷ es un método especial que
Python ejecuta automáticamente cada vez que creamos una nueva
instancia basada en la clase Dog. Este método tiene dos guiones
bajos iniciales y dos guiones bajos finales, una convención que
ayuda a evitar que los nombres de los métodos predeterminados de
Python entren en conflicto con los nombres de sus métodos.
Asegúrese de utilizar dos guiones bajos a cada lado de __init__().
Si usa solo uno en cada lado, el método no se llamará
automáticamente cuando use su clase, lo que puede generar errores
difíciles de identificar.
Definimos el método __init__() para que tenga tres parámetros:
self, name y age. El parámetro self es obligatorio en la definición del
método y debe aparecer primero, antes que los demás parámetros.
Debe incluirse en la definición porque cuando Python llame a este
método más adelante (para crear una instancia de Dog), la llamada
al método pasará automáticamente el argumento self. Cada
llamada a un método asociado con una instancia pasa
automáticamente self, que es una referencia a la instancia misma;
le da a la instancia individual acceso a los atributos y métodos de la
clase. Cuando creamos una instancia de Dog, Python llamará al
método __init__() de la clase Dog. Pasaremos Dog() un nombre y
una edad como argumentos; self se pasa automáticamente, por lo
que no es necesario que lo pasemos. Siempre que queramos crear
una instancia de la clase Dog, proporcionaremos valores solo para los
dos últimos parámetros, name y age.
Las dos variables definidas en el cuerpo del método __init__()
tienen cada una el prefijo self ❸. Cualquier variable con el prefijo
self está disponible para todos los métodos de la clase, y también
podremos acceder a estas variables a través de cualquier instancia
creada a partir de la clase. La línea self.name = name toma el valor
asociado con el parámetro name y lo asigna a la variable name, que
luego se adjunta a la instancia que se está creando. El mismo
proceso ocurre con self.age = age. Las variables a las que se puede
acceder a través de instancias como esta se denominan atributos.
La clase Dog tiene otros dos métodos definidos: sit() y roll_over()
❹. Debido a que estos métodos no necesitan información adicional
para ejecutarse, simplemente los definimos para que tengan un
parámetro, self. Las instancias que creemos más adelante tendrán
acceso a estos métodos. En otras palabras, podrán sentarse y darse
la vuelta. Por ahora, sit() y roll_over() no hacen mucho.
Simplemente imprimen un mensaje que dice que el perro está
sentado o dándose vuelta. Pero el concepto puede extenderse a
situaciones realistas: si esta clase fuera parte de un juego de
computadora, estos métodos contendrían código para hacer que un
perro animado se sentara y se volteara. Si esta clase se escribiera
para controlar un robot, estos métodos dirigirían los movimientos
que harían que un perro robótico se sentara y se volteara.
class Dog:
--snip--
❶ my_dog = Dog('Willie', 6)
my_dog.name
Métodos de llamada
Después de crear una instancia de la clase Dog, podemos usar la
notación de puntos para llamar a cualquier método definido en Dog.
Hagamos que nuestro perro se siente y se dé vuelta:
class Dog:
--snip--
my_dog = Dog('Willie', 6)
my_dog.sit()
my_dog.roll_over()
class Dog:
--snip--
my_dog = Dog('Willie', 6)
your_dog = Dog('Lucy', 3)
9-1. Restaurante: crea una clase llamada Restaurant. El método __init__() para
Restaurant debe almacenar dos atributos: un restaurant_name y un cuisine_type. Cree
un método llamado describe_restaurant() que imprima estos dos datos y un método
llamado open_restaurant() que imprima un mensaje indicando que el restaurante está
abierto.
Crea una instancia llamada restaurant de tu clase. Imprima los dos atributos
individualmente y luego llame a ambos métodos.
9-2. Tres Restaurantes: Comience con su clase del Ejercicio 9-1. Cree tres instancias
diferentes de la clase y llame a describe_restaurant() para cada instancia.
9-3. Usuarios: Crea una clase llamada User. Cree dos atributos llamados first_name y
last_name y luego cree varios otros atributos que normalmente se almacenan en un
perfil de usuario. Cree un método llamado describe_user() que imprima un resumen
de la información del usuario. Cree otro método llamado greet_user() que imprima un
saludo personalizado para el usuario.
Cree varias instancias que representen a diferentes usuarios y llame a ambos métodos
para cada usuario.
Trabajar con clases e instancias
Puede utilizar clases para representar muchas situaciones del mundo
real. Una vez que escriba una clase, pasará la mayor parte de su
tiempo trabajando con instancias creadas a partir de esa clase. Una
de las primeras tareas que querrás realizar es modificar los atributos
asociados con una instancia en particular. Puede modificar los
atributos de una instancia directamente o escribir métodos que
actualicen los atributos de formas específicas.
La clase de coche
Escribamos una nueva clase que represente un automóvil. Nuestra
clase almacenará información sobre el tipo de automóvil con el que
estamos trabajando y tendrá un método que resuma esta
información:
auto.py
class Car:
"""A simple attempt to represent a car."""
❷ def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = f"{self.year} {self.make} {self.model}"
return long_name.title()
2024 Audi A4
class Car:
def get_descriptive_name(self):
--snip--
❷ def read_odometer(self):
"""Print a statement showing the car's mileage."""
print(f"This car has {self.odometer_reading} miles on
it.")
Esta vez, cuando Python llama al método __init__() para crear una
nueva instancia, almacena los valores de marca, modelo y año como
atributos, como lo hizo en el ejemplo anterior. Luego Python crea un
nuevo atributo llamado odometer_reading y establece su valor inicial
en 0 ❶. También tenemos un nuevo método llamado
read_odometer() ❷ que facilita la lectura del kilometraje de un
automóvil.
Nuestro coche arranca con un kilometraje de 0:
2024 Audi A4
This car has 0 miles on it.
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
2024 Audi A4
This car has 23 miles on it.
class Car:
--snip--
❶ my_new_car.update_odometer(23)
my_new_car.read_odometer()
2024 Audi A4
This car has 23 miles on it.
class Car:
--snip--
class Car:
--snip--
❷ my_used_car.update_odometer(23_500)
my_used_car.read_odometer()
my_used_car.increment_odometer(100)
my_used_car.read_odometer()
Nota
9-4. Número atendido: Comience con su programa del ejercicio 9-1 (página 162).
Agregue un atributo llamado number_served con un valor predeterminado de 0. Cree
una instancia llamada restaurant a partir de esta clase. Imprima la cantidad de
clientes que ha atendido el restaurante, luego cambie este valor e imprímalo
nuevamente.
Agregue un método llamado set_number_served() que le permita establecer la cantidad
de clientes que han sido atendidos. Llame a este método con un nuevo número e
imprima el valor nuevamente.
Agregue un método llamado increment_number_served() que le permita incrementar la
cantidad de clientes atendidos. Llame a este método con cualquier número que desee
que pueda representar cuántos clientes fueron atendidos en, digamos, un día hábil.
9-5. Intentos de inicio de sesión: agregue un atributo llamado login_attempts a su
clase User del ejercicio 9-3 (página 162). Escriba un método llamado
increment_login_attempts() que incremente el valor de login_attempts en 1. Escriba
otro método llamado reset_login_attempts() que restablezca el valor de
login_attempts a 0.
Herencia
No siempre es necesario empezar desde cero al escribir una clase. Si
la clase que estás escribiendo es una versión especializada de otra
clase que escribiste, puedes usar la herencia. Cuando una clase
hereda de otra, adquiere los atributos y métodos de la primera clase.
La clase original se llama clase principal y la nueva clase es clase
secundaria. La clase hija puede heredar cualquiera o todos los
atributos y métodos de su clase padre, pero también es libre de
definir nuevos atributos y métodos propios.
El método __init__() para una clase
secundaria
Cuando escribes una nueva clase basada en una clase existente, a
menudo querrás llamar al método __init__() desde la clase
principal. Esto inicializará todos los atributos que se definieron en el
método principal __init__() y los pondrá a disposición en la clase
secundaria.
Como ejemplo, modelemos un coche eléctrico. Un automóvil
eléctrico es solo un tipo específico de automóvil, por lo que podemos
basar nuestra nueva clase ElectricCar en la clase Car que
escribimos anteriormente. Entonces sólo tendremos que escribir
código para los atributos y comportamientos específicos de los
coches eléctricos.
Comencemos creando una versión simple de la clase ElectricCar,
que hace todo lo que hace la clase Car:
auto_electrico.py
❶ class Car:
"""A simple attempt to represent a car."""
def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""Print a statement showing the car's mileage."""
print(f"This car has {self.odometer_reading} miles on
it.")
❷ class ElectricCar(Car):
"""Represent aspects of a car, specific to electric
vehicles."""
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric
vehicles."""
❷ def describe_battery(self):
"""Print a statement describing the battery size."""
print(f"This car has a {self.battery_size}-kWh
battery.")
class ElectricCar(Car):
--snip--
def fill_gas_tank(self):
"""Electric cars don't have gas tanks."""
print("This car doesn't have a gas tank!")
class Car:
--snip--
class Battery:
"""A simple attempt to model a battery for an electric
car."""
❷ def describe_battery(self):
"""Print a statement describing the battery size."""
print(f"This car has a {self.battery_size}-kWh
battery.")
class ElectricCar(Car):
"""Represent aspects of a car, specific to electric
vehicles."""
my_leaf.battery.describe_battery()
class Battery:
--snip--
def get_range(self):
"""Print a statement about the range this battery
provides."""
if self.battery_size == 40:
range = 150
elif self.battery_size == 65:
range = 225
class ElectricCar(Car):
--snip--
Importar clases
A medida que agrega más funciones a sus clases, sus archivos
pueden alargarse, incluso cuando usa la herencia y la composición
correctamente. De acuerdo con la filosofía general de Python,
querrás mantener tus archivos lo más ordenados posible. Para
ayudar, Python te permite almacenar clases en módulos y luego
importar las clases que necesitas a tu programa principal.
Importar una sola clase
Creemos un módulo que contenga solo la clase Car. Esto plantea un
problema sutil de nomenclatura: ya tenemos un archivo llamado
car.py en este capítulo, pero este módulo debería llamarse car.py
porque contiene código que representa un automóvil. Resolveremos
este problema de nombres almacenando la clase Car en un módulo
llamado car.py, reemplazando el archivo car.py que estábamos
usando anteriormente. De ahora en adelante, cualquier programa
que utilice este módulo necesitará un nombre de archivo más
específico, como my_car.py. Aquí está car.py con solo el código de la
clase Car:
auto.py
class Car:
"""A simple attempt to represent a car."""
def get_descriptive_name(self):
"""Return a neatly formatted descriptive name."""
long_name = f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""Print a statement showing the car's mileage."""
print(f"This car has {self.odometer_reading} miles on
it.")
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
2024 Audi A4
This car has 23 miles on it.
class Car:
--snip--
class Battery:
"""A simple attempt to model a battery for an electric
car."""
def describe_battery(self):
"""Print a statement describing the battery size."""
print(f"This car has a {self.battery_size}-kWh
battery.")
def get_range(self):
"""Print a statement about the range this battery
provides."""
if self.battery_size == 40:
range = 150
elif self.battery_size == 65:
range = 225
mis_autos.py
❶ import car
class Battery:
--snip--
class ElectricCar(Car):
--snip--
class Car:
--snip--
Usando alias
Como vio en el Capítulo 8, los alias pueden ser muy útiles cuando se
utilizan módulos para organizar el código de sus proyectos. También
puede utilizar alias al importar clases.
Como ejemplo, considere un programa en el que desea fabricar
varios coches eléctricos. Puede resultar tedioso escribir (y leer)
ElectricCar una y otra vez. Puede darle a ElectricCar un alias en la
declaración de importación:
import electric_car as ec
Otra función útil es choice(). Esta función toma una lista o tupla y
devuelve un elemento elegido aleatoriamente:
Nota
9-13. Dados: crea una clase Die con un atributo llamado sides, que tiene un valor
predeterminado de 6. Escribe un método llamado roll_die() que imprima un número
aleatorio entre 1 y el número de lados de la morir tiene. Haz un dado de 6 caras y
tíralo 10 veces.
Haz un dado de 10 caras y otro de 20 caras. Lanza cada dado 10 veces.
9-14. Lotería: Haz una lista o tupla que contenga una serie de 10 números y 5 letras.
Seleccione aleatoriamente 4 números o letras de la lista e imprima un mensaje que
diga que cualquier boleto que coincida con estos 4 números o letras gana un premio.
9-15. Análisis de lotería: puedes utilizar un bucle para ver qué tan difícil podría ser
ganar el tipo de lotería que acabas de modelar. Haz una lista o tupla llamada
my_ticket. Escribe un bucle que siga extrayendo números hasta que gane tu boleto.
Imprima un mensaje informando cuántas veces tuvo que ejecutarse el ciclo para
obtener un boleto ganador.
9-16. Módulo Python de la semana: un recurso excelente para explorar la biblioteca
estándar de Python es un sitio llamado Módulo Python de la semana. Vaya a
https://pymotw.com y mire la tabla de contenido. Encuentre un módulo que le parezca
interesante y lea sobre él, tal vez comenzando con el módulo random.
Clases de estilismo
Vale la pena aclarar algunas cuestiones de estilo relacionadas con las
clases, especialmente a medida que sus programas se vuelven más
complicados.
Los nombres de las clases deben escribirse en CamelCase. Para
hacer esto, escriba en mayúscula la primera letra de cada palabra
del nombre y no utilice guiones bajos. Los nombres de instancias y
módulos deben escribirse en minúsculas, con guiones bajos entre las
palabras.
Cada clase debe tener una cadena de documentación
inmediatamente después de la definición de clase. La cadena de
documentación debe ser una breve descripción de lo que hace la
clase y debes seguir las mismas convenciones de formato que
utilizaste para escribir cadenas de documentación en funciones.
Cada módulo también debe tener una cadena de documentación que
describa para qué se pueden usar las clases de un módulo.
Puedes usar líneas en blanco para organizar el código, pero no las
uses en exceso. Dentro de una clase puedes usar una línea en
blanco entre métodos y dentro de un módulo puedes usar dos líneas
en blanco para separar clases.
Si necesita importar un módulo de la biblioteca estándar y un
módulo que usted escribió, coloque primero la declaración de
importación para el módulo de la biblioteca estándar. Luego agregue
una línea en blanco y la declaración de importación para el módulo
que escribió. En programas con múltiples declaraciones de
importación, esta convención facilita ver de dónde provienen los
diferentes módulos utilizados en el programa.
Resumen
En este capítulo, aprendió a escribir sus propias clases. Aprendiste
cómo almacenar información en una clase usando atributos y cómo
escribir métodos que le den a tus clases el comportamiento que
necesitan. Aprendiste a escribir métodos __init__() que crean
instancias de tus clases con exactamente los atributos que deseas.
Viste cómo modificar los atributos de una instancia directamente y
mediante métodos. Aprendió que la herencia puede simplificar la
creación de clases relacionadas entre sí y aprendió a usar instancias
de una clase como atributos en otra clase para mantener cada clase
simple.
Viste cómo almacenar clases en módulos e importar las clases que
necesitas en los archivos donde se usarán puede mantener tus
proyectos organizados. Comenzó a aprender sobre la biblioteca
estándar de Python y vio un ejemplo basado en el módulo random.
Finalmente, aprendiste a diseñar tus clases usando las convenciones
de Python.
En el Capítulo 10, aprenderá a trabajar con archivos para poder
guardar el trabajo que realizó en un programa y el trabajo que
permitió que hicieran los usuarios. También aprenderá sobre las
excepciones, una clase especial de Python diseñada para ayudarlo a
responder a los errores cuando surjan.
10
archivos y excepciones
Lectura de un archivo
Una cantidad increíble de datos está disponible en archivos de texto.
Los archivos de texto pueden contener datos meteorológicos, datos
de tráfico, datos socioeconómicos, obras literarias y más. La lectura
de un archivo es particularmente útil en aplicaciones de análisis de
datos, pero también es aplicable a cualquier situación en la que
desee analizar o modificar información almacenada en un archivo.
Por ejemplo, puede escribir un programa que lea el contenido de un
archivo de texto y reescriba el archivo con un formato que permita
que un navegador lo muestre.
Cuando desee trabajar con la información de un archivo de texto, el
primer paso es leer el archivo en la memoria. Luego puede revisar
todo el contenido del archivo a la vez o revisar el contenido línea por
línea.
3.1415926535
8979323846
2643383279
❶ path = Path('pi_digits.txt')
❷ contents = path.read_text()
print(contents)
3.1415926535
8979323846
2643383279
path = Path('pi_digits.txt')
contents = path.read_text()
contents = contents.rstrip()
print(contents)
Recuerde del Capítulo 2 que el método rstrip() de Python elimina o
elimina cualquier carácter de espacio en blanco del lado derecho de
una cadena. Ahora el resultado coincide exactamente con el
contenido del archivo original:
3.1415926535
8979323846
2643383279
contents = path.read_text().rstrip()
path = Path('text_files/filename.txt')
path = Path('pi_digits.txt')
❶ contents = path.read_text()
❷ lines = contents.splitlines()
for line in lines:
print(line)
path = Path('pi_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
❶ for line in lines:
pi_string += line
print(pi_string)
print(len(pi_string))
--snip--
for line in lines:
pi_string += line.lstrip()
print(pi_string)
print(len(pi_string))
3.141592653589793238462643383279
32
Nota
path = Path('pi_million_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
pi_string = ''
for line in lines:
pi_string += line.lstrip()
print(f"{pi_string[:52]}...")
print(len(pi_string))
3.14159265358979323846264338327950288419716939937510...
1000002
Nota
--snip--
for line in lines:
pi_string += line.strip()
¡Mi cumpleaños aparece en los dígitos de pi! Una vez que haya leído
un archivo, podrá analizar su contenido prácticamente de cualquier
forma que pueda imaginar.
PRUÉBELO USTED MISMO
Lea cada línea del archivo que acaba de crear, learning_python.txt, y reemplace la
palabra Python con el nombre de otro idioma, como C. Imprima cada línea modificada
en la pantalla.
10-3. Código más simple: el programa file_reader.py en esta sección usa una variable
temporal, lines, para mostrar cómo funciona splitlines(). Puede omitir la variable
temporal y recorrer directamente la lista que devuelve splitlines():
Elimina la variable temporal de cada uno de los programas de esta sección, para
hacerlos más concisos.
Escribir en un archivo
Una de las formas más sencillas de guardar datos es escribirlos en
un archivo. Cuando escribe texto en un archivo, la salida seguirá
estando disponible después de cerrar la terminal que contiene la
salida de su programa. Puede examinar el resultado después de que
un programa termine de ejecutarse y también puede compartir los
archivos de resultado con otras personas. También puedes escribir
programas que lean el texto en la memoria y vuelvan a trabajar con
él más tarde.
Escribir una sola línea
Una vez que haya definido una ruta, puede escribir en un archivo
usando el método write_text(). Para ver cómo funciona esto,
escribamos un mensaje simple y guárdelo en un archivo en lugar de
imprimirlo en la pantalla:
escribir_mensaje.py
path = Path('programming.txt')
path.write_text("I love programming.")
I love programming.
Nota
path = Path('programming.txt')
path.write_text(contents)
I love programming.
I love creating new games.
I also love working with data.
Excepciones
Python utiliza objetos especiales llamados excepciones para
gestionar los errores que surgen durante la ejecución de un
programa. Cada vez que ocurre un error que hace que Python no
esté seguro de qué hacer a continuación, crea un objeto de
excepción. Si escribe código que maneje la excepción, el programa
continuará ejecutándose. Si no maneja la excepción, el programa se
detendrá y mostrará un rastreo, que incluye un informe de la
excepción que se generó.
Las excepciones se manejan con bloques try-except. Un bloque try-
except le pide a Python que haga algo, pero también le dice a
Python qué hacer si se genera una excepción. Cuando usas bloques
try-except, tus programas continuarán ejecutándose incluso si las
cosas comienzan a ir mal. En lugar de rastreos, que pueden resultar
confusos de leer para los usuarios, los usuarios verán mensajes de
error amigables que usted haya escrito.
print(5/0)
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
while True:
❶ first_number = input("\nFirst number: ")
if first_number == 'q':
break
❷ second_number = input("Second number: ")
if second_number == 'q':
break
❸ answer = int(first_number) / int(second_number)
print(answer)
First number: 5
Second number: 0
Traceback (most recent call last):
File "division_calculator.py", line 11, in <module>
answer = int(first_number) / int(second_number)
~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
ZeroDivisionError: division by zero
--snip--
while True:
--snip--
if second_number == 'q':
break
❶ try:
answer = int(first_number) / int(second_number)
❷ except ZeroDivisionError:
print("You can't divide by 0!")
❸ else:
print(answer)
First number: 5
Second number: 0
You can't divide by 0!
First number: 5
Second number: 2
2.5
First number: q
path = Path('alice.txt')
contents = path.read_text(encoding='utf-8')
Tenga en cuenta que aquí usamos read_text() de una manera
ligeramente diferente a la que vio anteriormente. El argumento
encoding es necesario cuando la codificación predeterminada de su
sistema no coincide con la codificación del archivo que se está
leyendo. Es más probable que esto suceda al leer un archivo que no
se creó en su sistema.
Python no puede leer un archivo faltante, por lo que genera una
excepción:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/.../pathlib.py", line 1042, in open
return io.open(self, mode, buffering, encoding, errors,
newline)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
❸ FileNotFoundError: [Errno 2] No such file or directory:
'alice.txt'
Este es un rastreo más largo que los que hemos visto anteriormente,
así que veamos cómo se pueden entender rastreos más complejos.
A menudo es mejor comenzar desde el final del rastreo. En la última
línea, podemos ver que se generó una excepción FileNotFoundError
❸. Esto es importante porque nos dice qué tipo de excepción usar
en el bloque except que escribiremos.
Mirando hacia atrás cerca del comienzo del rastreo ❶, podemos ver
que el error ocurrió en la línea 4 del archivo alice.py. La siguiente
línea muestra la línea de código que causó el error ❷. El resto del
rastreo muestra algo de código de las bibliotecas que participan en
la apertura y lectura de archivos. Por lo general, no es necesario leer
ni comprender todas estas líneas en un rastreo.
Para manejar el error que se genera, el bloque try comenzará con la
línea que se identificó como problemática en el rastreo. En nuestro
ejemplo, esta es la línea que contiene read_text():
path = Path('alice.txt')
try:
contents = path.read_text(encoding='utf-8')
❶ except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
Analizando texto
Puede analizar archivos de texto que contengan libros completos.
Muchas obras literarias clásicas están disponibles como archivos de
texto simples porque son de dominio público. Los textos utilizados
en esta sección provienen del Proyecto Gutenberg
(https://gutenberg.org). El Proyecto Gutenberg mantiene una
colección de obras literarias que están disponibles en el dominio
público y es un gran recurso si está interesado en trabajar con
textos literarios en sus proyectos de programación.
Busquemos el texto de Alicia en el país de las maravillas e
intentemos contar la cantidad de palabras en el texto. Para hacer
esto, usaremos el método de cadena split(), que de forma
predeterminada divide una cadena dondequiera que encuentre
espacios en blanco:
path = Path('alice.txt')
try:
contents = path.read_text(encoding='utf-8')
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
else:
# Count the approximate number of words in the file:
❶ words = contents.split()
❷ num_words = len(words)
print(f"The file {path} has about {num_words} words.")
def count_words(path):
❶ """Count the approximate number of words in a file."""
try:
contents = path.read_text(encoding='utf-8')
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
else:
# Count the approximate number of words in the file:
words = contents.split()
num_words = len(words)
print(f"The file {path} has about {num_words}
words.")
path = Path('alice.txt')
count_words(path)
def count_words(filename):
--snip--
Fallando silenciosamente
En el ejemplo anterior, informamos a nuestros usuarios que uno de
los archivos no estaba disponible. Pero no es necesario que informe
todas las excepciones que detecte. A veces, querrás que el
programa falle silenciosamente cuando se produzca una excepción y
continúe como si nada hubiera pasado. Para hacer que un programa
falle silenciosamente, escribe un bloque try como de costumbre,
pero le dice explícitamente a Python que no haga nada en el bloque
except. Python tiene una declaración pass que le indica que no haga
nada en un bloque:
def count_words(path):
"""Count the approximate number of words in a file."""
try:
--snip--
except FileNotFoundError:
pass
else:
--snip--
10-6. Además: un problema común cuando se solicita entrada numérica ocurre cuando
las personas proporcionan texto en lugar de números. Cuando intentas convertir la
entrada a int, obtendrás un ValueError. Escriba un programa que solicite dos
números. Súmalos e imprime el resultado. Capture el ValueError si alguno de los
valores de entrada no es un número e imprima un mensaje de error amigable. Pruebe
su programa ingresando dos números y luego ingresando algún texto en lugar de un
número.
10-7. Calculadora de sumas: envuelva su código del ejercicio 10-5 en un bucle while
para que el usuario pueda continuar ingresando números, incluso si comete un error e
ingresa texto en lugar de un número.
10-8. Perros y gatos: cree dos archivos, cats.txt y dogs.txt. Guarde al menos tres
nombres de gatos en el primer archivo y tres nombres de perros en el segundo
archivo. Escriba un programa que intente leer estos archivos e imprimir el contenido
del archivo en la pantalla. Envuelva su código en un bloque try-except para detectar el
error FileNotFound e imprima un mensaje amigable si falta un archivo. Mueva uno de
los archivos a una ubicación diferente en su sistema y asegúrese de que el código en el
bloque except se ejecute correctamente.
10-9. Perros y gatos silenciosos: modifique su bloque except en el Ejercicio 10-7 para
que falle silenciosamente si falta alguno de los archivos.
10-10. Palabras comunes: visite el Proyecto Gutenberg (https://gutenberg.org) y
encuentre algunos textos que le gustaría analizar. Descargue los archivos de texto de
estos trabajos o copie el texto sin formato de su navegador a un archivo de texto en su
computadora.
Puedes utilizar el método count() para saber cuántas veces aparece una palabra o
frase en una cadena. Por ejemplo, el siguiente código cuenta la cantidad de veces que
'row' aparece en una cadena:
Nota
❶ path = Path('numbers.json')
❷ contents = json.dumps(numbers)
path.write_text(contents)
❶ path = Path('numbers.json')
❷ contents = path.read_text()
❸ numbers = json.loads(contents)
print(numbers)
❷ path = Path('username.json')
contents = json.dumps(username)
path.write_text(contents)
❸ print(f"We'll remember you when you come back, {username}!")
❶ path = Path('username.json')
contents = path.read_text()
❷ username = json.loads(contents)
path = Path('username.json')
❶ if path.exists():
contents = path.read_text()
username = json.loads(contents)
print(f"Welcome back, {username}!")
❷ else:
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
print(f"We'll remember you when you come back,
{username}!")
Hay muchos métodos útiles que puede utilizar con objetos Path. El
método exists() devuelve True si existe un archivo o carpeta y
False si no existe. Aquí usamos path.exists() para saber si ya se ha
almacenado un nombre de usuario ❶. Si nombre de usuario.json
existe, cargamos el nombre de usuario e imprimimos un saludo
personalizado para el usuario.
Si el archivo nombre de usuario.json no existe ❷, solicitamos un
nombre de usuario y almacenamos el valor que ingresa el usuario.
También imprimimos el mensaje familiar de que los recordaremos
cuando regresen.
Cualquiera que sea el bloque que se ejecute, el resultado es un
nombre de usuario y un saludo apropiado. Si es la primera vez que
se ejecuta el programa, este es el resultado:
De lo contrario:
Welcome back, Eric!
Refactorización
A menudo, llegará a un punto en el que su código funcionará, pero
reconocerá que podría mejorarlo dividiéndolo en una serie de
funciones que tienen tareas específicas. Este proceso se llama
refactorización. La refactorización hace que su código sea más
limpio, más fácil de entender y de ampliar.
Podemos refactorizar Remember_me.py moviendo la mayor parte de
su lógica a una o más funciones. El objetivo de recordar_me.py es
saludar al usuario, así que muevamos todo nuestro código existente
a una función llamada greet_user():
recuerda_me.py
def greet_user():
❶ """Greet the user by name."""
path = Path('username.json')
if path.exists():
contents = path.read_text()
username = json.loads(contents)
print(f"Welcome back, {username}!")
else:
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
print(f"We'll remember you when you come back,
{username}!")
greet_user()
Como ahora estamos usando una función, reescribimos los
comentarios como una cadena de documentación que refleja cómo
funciona actualmente el programa ❶. Este archivo es un poco más
limpio, pero la función greet_user() hace más que simplemente
saludar al usuario: también recupera un nombre de usuario
almacenado, si existe, y solicita un nuevo nombre de usuario si no
existe.
Refactoricemos greet_user() para que no realice tantas tareas
diferentes. Comenzaremos moviendo el código para recuperar un
nombre de usuario almacenado a una función separada:
def get_stored_username(path):
❶ """Get stored username if available."""
if path.exists():
contents = path.read_text()
username = json.loads(contents)
return username
else:
❷ return None
def greet_user():
"""Greet the user by name."""
path = Path('username.json')
username = get_stored_username(path)
❸ if username:
print(f"Welcome back, {username}!")
else:
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
print(f"We'll remember you when you come back,
{username}!")
greet_user()
def get_stored_username(path):
"""Get stored username if available."""
--snip--
def get_new_username(path):
"""Prompt for a new username."""
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
return username
def greet_user():
"""Greet the user by name."""
path = Path('username.json')
❶ username = get_stored_username(path)
if username:
print(f"Welcome back, {username}!")
else:
❷ username = get_new_username(path)
print(f"We'll remember you when you come back,
{username}!")
greet_user()
Cada función en esta versión final de Remember_me.py tiene un
propósito único y claro. Llamamos a greet_user() y esa función
imprime un mensaje apropiado: da la bienvenida a un usuario
existente o saluda a un nuevo usuario. Lo hace llamando a
get_stored_username() ❶, que es responsable únicamente de
recuperar un nombre de usuario almacenado, si existe. Finalmente,
si es necesario, greet_user() llama a get_new_username()❷, que se
encarga únicamente de obtener un nuevo nombre de usuario y
almacenarlo. Esta compartimentación del trabajo es una parte
esencial para escribir un código claro que sea fácil de mantener y
ampliar.
10-11. Número favorito: escriba un programa que solicite el número favorito del
usuario. Utilice json.dumps() para almacenar este número en un archivo. Escriba un
programa separado que lea este valor e imprima el mensaje “¡Conozco tu número
favorito! Es ____."
10-12. Número favorito recordado: combine los dos programas que escribió en el
ejercicio 10-11 en un solo archivo. Si el número ya está almacenado, informe el
número favorito al usuario. De lo contrario, solicite el número favorito del usuario y
guárdelo en un archivo. Ejecute el programa dos veces para ver que funciona.
10-13. Diccionario de usuario: el ejemplo de Remember_me.py solo almacena una
información, el nombre de usuario. Amplíe este ejemplo solicitando dos datos más
sobre el usuario y luego almacene toda la información que recopile en un diccionario.
Escriba este diccionario en un archivo usando json.dumps() y vuelva a leerlo usando
json.loads(). Imprima un resumen que muestre exactamente lo que su programa
recuerda sobre el usuario.
10-14. Verificar usuario: el listado final de Remember_me.py supone que el usuario ya
ingresó su nombre de usuario o que el programa se está ejecutando por primera vez.
Deberíamos modificarlo en caso de que el usuario actual no sea la persona que utilizó
el programa por última vez.
Antes de imprimir un mensaje de bienvenida en greet_user(), pregunte al usuario si
este es el nombre de usuario correcto. Si no es así, llame a get_new_username() para
obtener el nombre de usuario correcto.
Resumen
En este capítulo, aprendió a trabajar con archivos. Aprendió a leer el
contenido completo de un archivo y luego a revisar el contenido
línea por línea si es necesario. Aprendiste a escribir todo el texto que
quieras en un archivo. También leerá sobre excepciones y cómo
manejar las excepciones que probablemente verá en sus programas.
Finalmente, aprendió a almacenar estructuras de datos de Python
para poder guardar la información que brindan sus usuarios,
evitando que tengan que comenzar de nuevo cada vez que ejecutan
un programa.
En el Capítulo 11, aprenderá formas eficientes de probar su código.
Esto le ayudará a confiar en que el código que desarrolle es correcto
y le ayudará a identificar los errores que se introducen a medida que
continúa desarrollando los programas que ha escrito.
11
Probando tu código
Actualizando pip
Python incluye una herramienta llamada pip que se utiliza para
instalar paquetes de terceros. Dado que pip ayuda a instalar
paquetes de recursos externos, se actualiza con frecuencia para
solucionar posibles problemas de seguridad. Entonces,
comenzaremos actualizando pip.
Abra una nueva ventana de terminal y emita el siguiente comando:
Instalando pytest
Ahora que pip está actualizado, podemos instalar pytest:
Nota
Podemos ver que los nombres generados aquí son correctos. Pero
digamos que queremos modificar get_formatted_name() para que
también pueda manejar segundos nombres. Mientras lo hacemos,
queremos asegurarnos de no alterar la forma en que la función
maneja los nombres que solo tienen un nombre y apellido.
Podríamos probar nuestro código ejecutando nombres.py e
ingresando un nombre como Janis Joplin cada vez que
modifiquemos get_formatted_name(), pero eso resultaría tedioso.
Afortunadamente, pytest proporciona una forma eficaz de
automatizar las pruebas de la salida de una función. Si
automatizamos las pruebas de get_formatted_name(), siempre
podemos estar seguros de que la función funcionará cuando se le
den los tipos de nombres para los que hemos escrito las pruebas.
prueba_nombre_función.py
❶ def test_first_last_name():
"""Do names like 'Janis Joplin' work?"""
❷ formatted_name = get_formatted_name('janis', 'joplin')
❸ assert formatted_name == 'Janis Joplin'
❹ test_name_function.py .
[100%]
========================== 1 passed in 0.00s
==========================
$ pytest
========================= test session starts
=========================
--snip--
❶ test_name_function.py F
[100%]
❷ ============================== FAILURES
===============================
❸ ________________________ test_first_last_name
_________________________
def test_first_last_name():
"""Do names like 'Janis Joplin' work?"""
❹ > formatted_name = get_formatted_name('janis',
'joplin')
❺ E TypeError: get_formatted_name() missing 1 required
positional
argument: 'last'
test_name_function.py:5: TypeError
======================= short test summary info
=======================
FAILED test_name_function.py::test_first_last_name -
TypeError:
get_formatted_name() missing 1 required positional
argument: 'last'
========================== 1 failed in 0.04s
==========================
$ pytest
========================= test session starts
=========================
--snip--
test_name_function.py .
[100%]
========================== 1 passed in 0.00s
==========================
def test_first_last_name():
--snip--
def test_first_last_middle_name():
"""Do names like 'Wolfgang Amadeus Mozart' work?"""
❶ formatted_name = get_formatted_name(
'wolfgang', 'mozart', 'amadeus')
❷ assert formatted_name == 'Wolfgang Amadeus Mozart'
$ pytest
========================= test session starts
=========================
--snip--
collected 2 items
❶ test_name_function.py ..
[100%]
========================== 2 passed in 0.01s
==========================
11-1. Ciudad, País: escriba una función que acepte dos parámetros: un nombre de
ciudad y un nombre de país. La función debe devolver una única cadena del formato
City, Country, como Santiago, Chile. Guarde la función en un módulo llamado
city_functions.py y guarde este archivo en una nueva carpeta para que pytest no
intente ejecutar las pruebas que ya hemos escrito.
Cree un archivo llamado test_cities.py que pruebe la función que acaba de escribir.
Escriba una función llamada test_city_country() para verificar que llamar a su función
con valores como 'santiago' y 'chile' dé como resultado la cadena correcta. Ejecute
la prueba y asegúrese de que test_city_country() pase.
11-2. Población: modifique su función para que requiera un tercer parámetro,
population. Ahora debería devolver una única cadena del formato City, Country –
population xxx, como Santiago, Chile – population 50000. Ejecute la prueba
nuevamente y asegúrese de que test_city_country() falle esta vez.
Modifique la función para que el parámetro population sea opcional. Ejecute la prueba
y asegúrese de que test_city_country() pase nuevamente.
Escriba una segunda prueba llamada test_city_country_population() que verifique
que puede llamar a su función con los valores 'santiago', 'chile' y
'population=50000'. Ejecute las pruebas una vez más y asegúrese de que esta nueva
prueba pase.
Afirmación Afirmar
assert a == b Afirma que dos valores son iguales.
assert a != b Afirma que dos valores no son iguales.
assert a Afirma que a se evalúa como True.
class AnonymousSurvey:
"""Collect anonymous answers to a survey question."""
❷ def show_question(self):
"""Show the survey question."""
print(self.question)
❹ def show_results(self):
"""Show all the responses that have been given."""
print("Survey results:")
for response in self.responses:
print(f"- {response}")
Este programa define una pregunta ("What language did you first
learn to speak?") y crea un objeto AnonymousSurvey con esa
pregunta. El programa llama a show_question() para mostrar la
pregunta y luego solicita respuestas. Cada respuesta se almacena a
medida que se recibe. Cuando se hayan ingresado todas las
respuestas (el usuario ingresa q para salir), show_results() imprime
los resultados de la encuesta:
Language: English
Language: Spanish
Language: English
Language: Mandarin
Language: q
❶ def test_store_single_response():
"""Test that a single response is stored properly."""
question = "What language did you first learn to speak?"
❷ language_survey = AnonymousSurvey(question)
language_survey.store_response('English')
❸ assert 'English' in language_survey.responses
$ pytest test_survey.py
========================= test session starts
=========================
--snip--
test_survey.py .
[100%]
========================== 1 passed in 0.01s
==========================
def test_store_single_response():
--snip--
def test_store_three_responses():
"""Test that three individual responses are stored
properly."""
question = "What language did you first learn to speak?"
language_survey = AnonymousSurvey(question)
❶ responses = ['English', 'Spanish', 'Mandarin']
for response in responses:
language_survey.store_response(response)
❷ for response in responses:
assert response in language_survey.responses
$ pytest test_survey.py
========================= test session starts
=========================
--snip--
test_survey.py ..
[100%]
========================== 2 passed in 0.01s
==========================
Usando accesorios
En test_survey.py, creamos una nueva instancia de AnonymousSurvey
en cada función de prueba. Esto está bien en el breve ejemplo con
el que estamos trabajando, pero en un proyecto del mundo real con
decenas o cientos de pruebas, sería problemático.
Durante las pruebas, un dispositivo ayuda a configurar un entorno
de prueba. A menudo, esto significa crear un recurso que sea
utilizado por más de una prueba. Creamos un dispositivo en pytest
escribiendo una función con el decorador @pytest.fixture. Un
decorador es una directiva colocada justo antes de la definición de
una función; Python aplica esta directiva a la función antes de
ejecutarla, para alterar el comportamiento del código de la función.
No te preocupes si esto suena complicado; puedes empezar a utilizar
decoradores de paquetes de terceros antes de aprender a escribirlos
tú mismo.
Usemos un dispositivo para crear una única instancia de encuesta
que se pueda usar en ambas funciones de prueba en test_survey.py:
import pytest
from survey import AnonymousSurvey
❶ @pytest.fixture
❷ def language_survey():
"""A survey that will be available to all test
functions."""
question = "What language did you first learn to speak?"
language_survey = AnonymousSurvey(question)
return language_survey
❸ def test_store_single_response(language_survey):
"""Test that a single response is stored properly."""
❹ language_survey.store_response('English')
assert 'English' in language_survey.responses
❺ def test_store_three_responses(language_survey):
"""Test that three individual responses are stored
properly."""
responses = ['English', 'Spanish', 'Mandarin']
for response in responses:
❻ language_survey.store_response(response)
11-3. Empleado: escriba una clase llamada Employee. El método __init__() debe tomar
un nombre, un apellido y un salario anual, y almacenar cada uno de ellos como
atributos. Escriba un método llamado give_raise() que agregue $5,000 al salario
anual de forma predeterminada pero que también acepte un monto de aumento
diferente.
Escriba un archivo de prueba para Employee con dos funciones de prueba,
test_give_default_raise() y test_give_custom_raise(). Escriba sus pruebas una vez
sin utilizar un dispositivo y asegúrese de que ambas pasen. Luego escriba un accesorio
para no tener que crear una nueva instancia de empleado en cada función de prueba.
Ejecute las pruebas nuevamente y asegúrese de que ambas se aprueben.
Resumen
En este capítulo, aprendió a escribir pruebas para funciones y clases
usando herramientas en el módulo pytest. Aprendió a escribir
funciones de prueba que verifican comportamientos específicos que
deben exhibir sus funciones y clases. Viste cómo se pueden usar
dispositivos para crear recursos de manera eficiente que se pueden
usar en múltiples funciones de prueba en un archivo de prueba.
Las pruebas son un tema importante al que muchos programadores
nuevos no están expuestos. No es necesario que escriba pruebas
para todos los proyectos simples que prueba como nuevo
programador. Pero tan pronto como empiece a trabajar en proyectos
que impliquen un esfuerzo de desarrollo significativo, deberá probar
los comportamientos críticos de sus funciones y clases. Estará más
seguro de que el nuevo trabajo en su proyecto no dañará las partes
que funcionan y esto le dará la libertad de realizar mejoras en su
código. Si accidentalmente interrumpes una funcionalidad existente,
lo sabrás de inmediato, por lo que aún podrás solucionar el
problema fácilmente. Responder a una prueba fallida que ejecutó es
mucho más fácil que responder a un informe de error de un usuario
descontento.
Otros programadores respetarán más tus proyectos si incluyes
algunas pruebas iniciales. Se sentirán más cómodos experimentando
con su código y estarán más dispuestos a trabajar con usted en
proyectos. Si desea contribuir a un proyecto en el que están
trabajando otros programadores, se espera que demuestre que su
código pasa las pruebas existentes y, por lo general, se espera que
escriba pruebas para cualquier comportamiento nuevo que
introduzca en el proyecto.
Experimente con las pruebas para familiarizarse con el proceso de
prueba de su código. Escriba pruebas para los comportamientos más
críticos de sus funciones y clases, pero no busque una cobertura
completa en los primeros proyectos a menos que tenga una razón
específica para hacerlo.
Parte II
Proyectos
¡Felicidades! Ahora sabes lo suficiente sobre Python
para comenzar a crear proyectos interactivos y
significativos. Crear sus propios proyectos le
enseñará nuevas habilidades y solidificará su
comprensión de los conceptos introducidos en la
Parte I.
La Parte II contiene tres tipos de proyectos y usted puede elegir
realizar cualquiera o todos estos proyectos en el orden que desee.
Aquí hay una breve descripción de cada proyecto para ayudarlo a
decidir en cuál profundizar primero.
Aplicaciones web
En el proyecto de aplicación web (capítulos 18, 19 y 20), utilizará el
paquete Django para crear una aplicación web sencilla que permita a
los usuarios llevar un diario sobre diferentes temas que han estado
aprendiendo. Los usuarios crearán una cuenta con un nombre de
usuario y contraseña, ingresarán un tema y luego harán entradas
sobre lo que están aprendiendo. También implementará su
aplicación en un servidor remoto para que cualquier persona en el
mundo pueda acceder a ella.
Después de completar este proyecto, podrá comenzar a crear sus
propias aplicaciones web simples y estará listo para profundizar en
recursos más completos sobre la creación de aplicaciones con
Django.
12
Un barco que dispara balas
Nota
Planificando tu proyecto
Cuando estás creando un proyecto grande, es importante preparar
un plan antes de comenzar a escribir código. Su plan lo mantendrá
concentrado y aumentará las probabilidades de que complete el
proyecto.
Escribamos una descripción del juego general. Aunque la siguiente
descripción no cubre todos los detalles de Alien Invasion,
proporciona una idea clara de cómo empezar a construir el juego:
En Alien Invasion, el jugador controla un cohete que aparece en
la parte inferior central de la pantalla. El jugador puede mover
la nave hacia la derecha y hacia la izquierda usando las teclas
de flecha y disparar balas usando la barra espaciadora. Cuando
comienza el juego, una flota de extraterrestres llena el cielo y se
mueve a lo largo y ancho de la pantalla. El jugador dispara y
destruye a los alienígenas. Si el jugador destruye a todos los
alienígenas, aparece una nueva flota que se mueve más rápido
que la flota anterior. Si algún extraterrestre golpea la nave del
jugador o llega al final de la pantalla, el jugador pierde una
nave. Si el jugador pierde tres barcos, el juego termina.
Instalación de Pygame
Antes de comenzar a codificar, instale Pygame. Haremos esto de la
misma manera que instalamos pytest en el Capítulo 11: con pip. Si
se saltó el Capítulo 11 o necesita un repaso sobre pip, consulte
“Instalación de pytest con pip” en la página 210.
Para instalar Pygame, ingrese el siguiente comando en el símbolo del
terminal:
$ python -m pip install --user pygame
import sys
import pygame
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
❶ pygame.init()
def run_game(self):
"""Start the main loop for the game."""
❸ while True:
# Watch for keyboard and mouse events.
❹ for event in pygame.event.get():
❺ if event.type == pygame.QUIT:
sys.exit()
invasión_alienígena.py
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.clock = pygame.time.Clock()
--snip--
def run_game(self):
"""Start the main loop for the game."""
while True:
--snip--
pygame.display.flip()
self.clock.tick(60)
def __init__(self):
--snip--
pygame.display.set_caption("Alien Invasion")
def run_game(self):
--snip--
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
--snip--
import pygame
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.clock = pygame.time.Clock()
❶ self.settings = Settings()
❷ self.screen = pygame.display.set_mode(
(self.settings.screen_width,
self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
def run_game(self):
--snip--
# Redraw the screen during each pass through the
loop.
❸ self.screen.fill(self.settings.bg_color)
import pygame
class Ship:
"""A class to manage the ship."""
❺ def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
Nota
--snip--
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
--snip--
pygame.display.set_caption("Alien Invasion")
❶ self.ship = Ship(self)
def run_game(self):
--snip--
# Redraw the screen during each pass through the
loop.
self.screen.fill(self.settings.bg_color)
❷ self.ship.blitme()
Figura 12-2: Invasión alienígena con la nave en la parte inferior central de la pantalla
El método _check_events()
Moveremos el código que administra eventos a un método separado
llamado _check_events(). Esto simplificará run_game() y aislará el
ciclo de gestión de eventos. Aislar el bucle de eventos te permite
administrar eventos por separado de otros aspectos del juego, como
la actualización de la pantalla.
Aquí está la clase AlienInvasion con el nuevo método
_check_events(), que solo afecta el código en run_game():
invasión_alienígena.py
def run_game(self):
"""Start the main loop for the game."""
while True:
❶ self._check_events()
❷ def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
El método _update_screen()
Para simplificar aún más run_game(), moveremos el código para
actualizar la pantalla a un método separado llamado
_update_screen():
invasión_alienígena.py
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self._update_screen()
self.clock.tick(60)
def _check_events(self):
--snip--
def _update_screen(self):
"""Update images on the screen, and flip to the new
screen."""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
12-1. Cielo azul: crea una ventana de Pygame con un fondo azul.
12-2. Personaje del juego: busque una imagen de mapa de bits de un personaje del
juego que le guste o convierta una imagen a un mapa de bits. Cree una clase que
dibuje el personaje en el centro de la pantalla, luego haga coincidir el color de fondo
de la imagen con el color de fondo de la pantalla o viceversa.
Pilotando el barco
A continuación, le daremos al jugador la posibilidad de mover el
barco hacia la derecha y hacia la izquierda. Escribiremos un código
que responda cuando el jugador presione la tecla de flecha derecha
o izquierda. Nos centraremos primero en el movimiento hacia la
derecha y luego aplicaremos los mismos principios para controlar el
movimiento hacia la izquierda. A medida que agreguemos este
código, aprenderá cómo controlar el movimiento de las imágenes en
la pantalla y responder a las entradas del usuario.
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
❶ elif event.type == pygame.KEYDOWN:
❷ if event.key == pygame.K_RIGHT:
# Move the ship to the right.
❸ self.ship.rect.x += 1
nave.py
class Ship:
"""A class to manage the ship."""
❷ def update(self):
"""Update the ship's position based on the movement
flag."""
if self.moving_right:
self.rect.x += 1
def blitme(self):
--snip--
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
--snip--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
❶ self.ship.moving_right = True
❷ elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self._update_screen()
self.clock.tick(60)
La posición del barco se actualizará después de que hayamos
verificado los eventos del teclado y antes de actualizar la pantalla.
Esto permite que la posición del barco se actualice en respuesta a la
entrada del jugador y garantiza que la posición actualizada se
utilizará al dibujar el barco en la pantalla.
Cuando ejecuta alien_invasion.py y mantiene presionada la tecla de
flecha derecha, la nave debe moverse continuamente hacia la
derecha hasta que suelte la tecla.
def update(self):
"""Update the ship's position based on movement
flags."""
if self.moving_right:
self.rect.x += 1
if self.moving_left:
self.rect.x -= 1
invasión_alienígena.py
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
--snip--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
--snip--
# Ship settings
self.ship_speed = 1.5
class Ship:
"""A class to manage the ship."""
def update(self):
"""Update the ship's position based on movement
flags."""
# Update the ship's x value, not the rect.
if self.moving_right:
❸ self.x += self.settings.ship_speed
if self.moving_left:
self.x -= self.settings.ship_speed
def blitme(self):
--snip--
nave.py
def update(self):
"""Update the ship's position based on movement
flags."""
# Update the ship's x value, not the rect.
❶ if self.moving_right and self.rect.right <
self.screen_rect.right:
self.x += self.settings.ship_speed
❷ if self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
Refactorización _check_events()
El método _check_events() aumentará en longitud a medida que
sigamos desarrollando el juego, así que dividamos _check_events()
en dos métodos separados: uno que maneja eventos KEYDOWN y otro
que maneja KEYUP eventos:
invasión_alienígena.py
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
Un resumen rápido
En la siguiente sección, agregaremos la capacidad de disparar balas,
lo que implica agregar un nuevo archivo llamado bullet.py y realizar
algunas modificaciones en algunos de los archivos que ya estamos
usando. En este momento, tenemos tres archivos que contienen
varias clases y métodos. Para tener claro cómo está organizado el
proyecto, revisemos cada uno de estos archivos antes de agregar
más funciones.
invasión_alienígena.py
El archivo principal, alien_invasion.py, contiene la clase
AlienInvasion. Esta clase crea una serie de atributos importantes
que se utilizan en todo el juego: la configuración se asigna a
settings, la superficie de visualización principal se asigna a screen y
se crea una instancia de ship en este archivo. también. El bucle
principal del juego, un bucle while, también se almacena en este
módulo. El bucle while llama a _check_events(), ship.update() y
_update_screen(). También marca el reloj en cada paso por el
circuito.
El método _check_events() detecta eventos relevantes, como
pulsaciones y liberaciones de teclas, y procesa cada uno de estos
tipos de eventos a través de los métodos _check_keydown_events() y
_check_keyup_events(). Por ahora, estos métodos gestionan el
movimiento del barco. La clase AlienInvasion también contiene
_update_screen(), que vuelve a dibujar la pantalla en cada paso por
el bucle principal.
El archivo alien_invasion.py es el único archivo que necesitas
ejecutar cuando quieras jugar Alien Invasion. Los otros archivos,
settings.py y ship.py, contienen código que se importa a este
archivo.
configuración.py
El archivo settings.py contiene la clase Settings. Esta clase solo
tiene un método __init__(), que inicializa los atributos que
controlan la apariencia del juego y la velocidad del barco.
nave.py
El archivo ship.py contiene la clase Ship. La clase Ship tiene un
método __init__(), un método update() para gestionar la posición
del barco y un método blitme() para dibujar el barco en la pantalla.
La imagen del barco se almacena en ship.bmp, que se encuentra en
la carpeta de imágenes.
PRUÉBELO USTED MISMO
Disparar balas
Ahora agreguemos la capacidad de disparar balas. Escribiremos un
código que dispare una bala, representada por un pequeño
rectángulo, cuando el jugador presione la barra espaciadora. Luego,
las balas viajarán hacia arriba en la pantalla hasta que desaparezcan
de la parte superior de la pantalla.
def __init__(self):
--snip--
# Bullet settings
self.bullet_speed = 2.0
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship."""
bala.py
def update(self):
"""Move the bullet up the screen."""
# Update the exact position of the bullet.
❶ self.y -= self.settings.bullet_speed
# Update the rect position.
❷ self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
❸ pygame.draw.rect(self.screen, self.color, self.rect)
invasión_alienígena.py
--snip--
from ship import Ship
from bullet import Bullet
invasión_alienígena.py
def __init__(self):
--snip--
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self._update_screen()
self.clock.tick(60)
Disparando balas
En AlienInvasion, necesitamos modificar _check_keydown_events()
para disparar una bala cuando el jugador presiona la barra
espaciadora. No necesitamos cambiar _check_keyup_events() porque
no sucede nada cuando se suelta la barra espaciadora. También
necesitamos modificar _update_screen() para asegurarnos de que
cada viñeta aparezca en la pantalla antes de llamar a flip().
Habrá un poco de trabajo que hacer cuando disparemos una bala,
así que escribamos un nuevo método, _fire_bullet(), para manejar
este trabajo:
invasión_alienígena.py
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets
group."""
❷ new_bullet = Bullet(self)
❸ self.bullets.add(new_bullet)
def _update_screen(self):
"""Update images on the screen, and flip to the new
screen."""
self.screen.fill(self.settings.bg_color)
❹ for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.ship.blitme()
pygame.display.flip()
--snip--
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self._update_screen()
self.clock.tick(60)
# Bullet settings
--snip--
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets
group."""
if len(self.bullets) < self.settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update position of bullets and get rid of old
bullets."""
# Update bullet positions.
self.bullets.update()
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_screen()
self.clock.tick(60)
12-6. Sideways Shooter: escribe un juego que coloque un barco en el lado izquierdo de
la pantalla y permita al jugador mover el barco hacia arriba y hacia abajo. Haz que la
nave dispare una bala que recorra la pantalla cuando el jugador presione la barra
espaciadora. Asegúrese de que las viñetas se eliminen una vez que desaparezcan de la
pantalla.
Resumen
En este capítulo, aprendiste a hacer un plan para un juego y
aprendiste la estructura básica de un juego escrito en Pygame.
Aprendió a establecer un color de fondo y almacenar la
configuración en una clase separada donde puede ajustarla más
fácilmente. Viste cómo dibujar una imagen en la pantalla y darle al
jugador control sobre el movimiento de los elementos del juego.
Creaste elementos que se mueven por sí solos, como balas que
vuelan por una pantalla, y eliminaste objetos que ya no son
necesarios. También aprendió a refactorizar el código de un proyecto
de forma regular para facilitar el desarrollo continuo.
En el Capítulo 13, agregaremos extraterrestres a Alien Invasion. Al
final del capítulo, podrás derribar alienígenas, ¡con suerte antes de
que lleguen a tu nave!
13
¡Extraterrestres!
Revisando el proyecto
Cuando comienzas una nueva fase de desarrollo en un proyecto
grande, siempre es una buena idea revisar tu plan y aclarar lo que
quieres lograr con el código que estás a punto de escribir. En este
capítulo, haremos lo siguiente:
Agrega un solo alienígena a la esquina superior izquierda de la
pantalla, con el espacio apropiado alrededor.
Llena la parte superior de la pantalla con tantos alienígenas
como podamos que quepan en horizontal. Luego crearemos filas
adicionales de alienígenas hasta que tengamos una flota
completa.
Haz que la flota se mueva hacia los lados y hacia abajo hasta
que toda la flota sea derribada, un extraterrestre golpee la nave
o un extraterrestre llegue al suelo. Si derriban toda la flota,
crearemos una nueva flota. Si un extraterrestre golpea la nave o
el suelo, destruiremos la nave y crearemos una nueva flota.
Limite la cantidad de barcos que el jugador puede usar y finalice
el juego cuando el jugador haya agotado la cantidad asignada
de barcos.
Perfeccionaremos este plan a medida que implementemos funciones,
pero es lo suficientemente específico como para comenzar a escribir
código.
También debes revisar tu código existente cuando comiences a
trabajar en una nueva serie de funciones en un proyecto. Debido a
que cada nueva fase normalmente hace que un proyecto sea más
complejo, es mejor limpiar cualquier código desordenado o
ineficiente. Hemos estado refactorizando a medida que avanzamos,
por lo que no hay ningún código que debamos refactorizar en este
momento.
class Alien(Sprite):
"""A class to represent a single alien in the fleet."""
--snip--
from bullet import Bullet
from alien import Alien
def __init__(self):
--snip--
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
invasión_alienígena.py
def _create_fleet(self):
"""Create the fleet of aliens."""
# Make an alien.
alien = Alien(self)
self.aliens.add(alien)
invasión_alienígena.py
def _update_screen(self):
--snip--
self.ship.blitme()
self.aliens.draw(self.screen)
pygame.display.flip()
def _create_fleet(self):
"""Create the fleet of aliens."""
# Create an alien and keep adding aliens until
there's no room left.
# Spacing between aliens is one alien width.
alien = Alien(self)
alien_width = alien.rect.width
❶ current_x = alien_width
❷ while current_x < (self.settings.screen_width - 2 *
alien_width):
❸ new_alien = Alien(self)
❹ new_alien.x = current_x
new_alien.rect.x = current_x
self.aliens.add(new_alien)
❺ current_x += 2 * alien_width
Nota
Refactorización _create_fleet()
Si el código que hemos escrito hasta ahora fuera todo lo que
necesitábamos para crear una flota, probablemente dejaríamos
_create_fleet() como está. Pero tenemos más trabajo por hacer, así
que limpiemos un poco el método. Agregaremos un nuevo método
auxiliar, _create_alien(), y lo llamaremos desde _create_fleet():
invasión_alienígena.py
def _create_fleet(self):
--snip--
while current_x < (self.settings.screen_width - 2 *
alien_width):
self._create_alien(current_x)
current_x += 2 * alien_width
Agregar filas
Para terminar la flota, seguiremos agregando más filas hasta que
nos quedemos sin espacio. Usaremos un bucle anidado;
envolveremos otro bucle while alrededor del actual. El bucle interior
colocará a los extraterrestres horizontalmente en una fila
centrándose en los valores x de los extraterrestres. El bucle exterior
colocará a los extraterrestres verticalmente centrándose en los
valores de y. Dejaremos de agregar filas cuando nos acerquemos a
la parte inferior de la pantalla, dejando suficiente espacio para la
nave y algo de espacio para comenzar a disparar a los alienígenas.
A continuación se explica cómo anidar los dos bucles while en
_create_fleet():
def _create_fleet(self):
"""Create the fleet of aliens."""
# Create an alien and keep adding aliens until
there's no room left.
# Spacing between aliens is one alien width and one
alien height.
alien = Alien(self)
❶ alien_width, alien_height = alien.rect.size
13-1. Estrellas: Encuentra una imagen de una estrella. Haz que aparezca una
cuadrícula de estrellas en la pantalla.
13-2. Mejores estrellas: puedes crear un patrón de estrellas más realista introduciendo
aleatoriedad al colocar cada estrella. Recuerde del Capítulo 9 que puede obtener un
número aleatorio como este:
Este código devuelve un número entero aleatorio entre −10 y 10. Usando el código del
ejercicio 13-1, ajusta la posición de cada estrella en una cantidad aleatoria.
Hacer que la flota se mueva
Ahora hagamos que la flota de alienígenas se mueva hacia la
derecha a lo largo de la pantalla hasta que llegue al borde, y luego
hagamos que caiga una cantidad determinada y se mueva en la otra
dirección. Continuaremos este movimiento hasta que todos los
alienígenas hayan sido derribados, uno choque con la nave o llegue
al final de la pantalla. Comencemos haciendo que la flota se mueva
hacia la derecha.
def __init__(self):
--snip--
# Alien settings
self.alien_speed = 1.0
def update(self):
"""Move the alien to the right."""
❶ self.x += self.settings.alien_speed
❷ self.rect.x = self.x
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
self.clock.tick(60)
invasión_alienígena.py
def _update_aliens(self):
"""Update the positions of all aliens in the
fleet."""
self.aliens.update()
# Alien settings
self.alien_speed = 1.0
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1
represents left.
self.fleet_direction = 1
def check_edges(self):
"""Return True if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
❶ return (self.rect.right >= screen_rect.right) or
(self.rect.left <= 0)
def update(self):
"""Move the alien right or left."""
❷ self.x += self.settings.alien_speed *
self.settings.fleet_direction
self.rect.x = self.x
def _check_fleet_edges(self):
"""Respond appropriately if any aliens have reached
an edge."""
❶ for alien in self.aliens.sprites():
if alien.check_edges():
❷ self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Drop the entire fleet and change the fleet's
direction."""
for alien in self.aliens.sprites():
❸ alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
invasión_alienígena.py
def _update_aliens(self):
"""Check if the fleet is at an edge, then update
positions."""
self._check_fleet_edges()
self.aliens.update()
13-3. Gotas de lluvia: busque una imagen de una gota de lluvia y cree una cuadrícula
de gotas de lluvia. Haz que las gotas de lluvia caigan hacia la parte inferior de la
pantalla hasta que desaparezcan.
13-4. Lluvia constante: Modifique su código en el Ejercicio 13-3 para que cuando una
fila de gotas de lluvia desaparezca de la parte inferior de la pantalla, aparezca una
nueva fila en la parte superior de la pantalla y comience a caer.
Disparar extraterrestres
Hemos construido nuestra nave y una flota de extraterrestres, pero
cuando las balas alcanzan a los extraterrestres, simplemente pasan
porque no estamos comprobando si hay colisiones. En la
programación de juegos, las colisiones ocurren cuando los elementos
del juego se superponen. Para hacer que las balas derriben a los
extraterrestres, usaremos la función sprite.groupcollide() para
buscar colisiones entre miembros de dos grupos.
def _update_bullets(self):
"""Update position of bullets and get rid of old
bullets."""
--snip--
Repoblación de la flota
Una característica clave de Alien Invasion es que los alienígenas son
implacables: cada vez que se destruye la flota, debería aparecer una
nueva flota.
Para hacer que aparezca una nueva flota de alienígenas después de
que una flota haya sido destruida, primero verificamos si el grupo
aliens está vacío. Si es así, hacemos una llamada a
_create_fleet(). Realizaremos esta verificación al final de
_update_bullets(), porque ahí es donde se destruyen los
extraterrestres individuales.
invasión_alienígena.py
def _update_bullets(self):
--snip--
❶ if not self.aliens:
# Destroy existing bullets and create new fleet.
❷ self.bullets.empty()
self._create_fleet()
Ahora aparece una nueva flota tan pronto como destruyes la flota
actual.
configuración.py
# Bullet settings
self.bullet_speed = 2.5
self.bullet_width = 3
--snip--
El mejor valor para esta configuración depende de tu experiencia en
el juego, así que busca un valor que funcione para ti. También
puedes ajustar otras configuraciones.
Refactorización _update_bullets()
Refactoricemos _update_bullets() para que no realice tantas tareas
diferentes. Moveremos el código para lidiar con colisiones entre
balas y extraterrestres a un método separado:
invasión_alienígena.py
def _update_bullets(self):
--snip--
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
13-5. Tirador lateral Parte 2: Hemos recorrido un largo camino desde el Ejercicio 12-6,
Tirador lateral. Para este ejercicio, intenta desarrollar Sideways Shooter hasta el mismo
punto al que llevamos Alien Invasion. Añade una flota de alienígenas y haz que se
muevan de lado hacia la nave. O escriba un código que coloque extraterrestres en
posiciones aleatorias a lo largo del lado derecho de la pantalla y luego los envíe hacia
la nave. Además, escribe código que haga que los alienígenas desaparezcan cuando
sean golpeados.
Terminando el juego
¿Cuál es la diversión y el desafío de jugar un juego que no puedes
perder? Si el jugador no derriba la flota lo suficientemente rápido,
haremos que los alienígenas destruyan la nave cuando hagan
contacto. Al mismo tiempo, limitaremos la cantidad de naves que un
jugador puede usar y destruiremos la nave cuando un extraterrestre
llegue al final de la pantalla. El juego terminará cuando el jugador
haya agotado todos sus barcos.
def _update_aliens(self):
--snip--
self.aliens.update()
class GameStats:
"""Track statistics for Alien Invasion."""
def reset_stats(self):
"""Initialize statistics that can change during the
game."""
self.ships_left = self.settings.ship_limit
# Ship settings
self.ship_speed = 1.5
self.ship_limit = 3
import sys
from time import sleep
import pygame
invasión_alienígena.py
def __init__(self):
--snip--
self.screen = pygame.display.set_mode(
(self.settings.screen_width,
self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
self.ship = Ship(self)
--snip--
invasión_alienígena.py
def _ship_hit(self):
"""Respond to the ship being hit by an alien."""
# Decrement ships_left.
❶ self.stats.ships_left -= 1
# Pause.
❹ sleep(0.5)
def _update_aliens(self):
--snip--
if pygame.sprite.spritecollideany(self.ship,
self.aliens):
self._ship_hit()
def center_ship(self):
"""Center the ship on the screen."""
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
Nota
def _check_aliens_bottom(self):
"""Check if any aliens have reached the bottom of the
screen."""
for alien in self.aliens.sprites():
❶ if alien.rect.bottom >=
self.settings.screen_height:
# Treat this the same as if the ship got hit.
self._ship_hit()
break
invasión_alienígena.py
def _update_aliens(self):
--snip--
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(self.ship,
self.aliens):
self._ship_hit()
¡Juego terminado!
Alien Invasion se siente más completo ahora, pero el juego nunca
termina. El valor de ships_left se vuelve cada vez más negativo.
Agreguemos una bandera game_active, para que podamos finalizar
el juego cuando el jugador se quede sin barcos. Estableceremos esta
bandera al final del método __init__() en AlienInvasion:
invasión_alienígena.py
def __init__(self):
--snip--
# Start Alien Invasion in an active state.
self.game_active = True
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
# Decrement ships_left.
self.stats.ships_left -= 1
--snip--
# Pause.
sleep(0.5)
else:
self.game_active = False
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
if self.game_active:
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
self.clock.tick(60)
13-6. Fin del juego: en Sideways Shooter, lleva la cuenta del número de veces que el
barco es golpeado y el número de veces que el barco golpea a un alienígena. Decida
una condición adecuada para finalizar el juego y deténgalo cuando ocurra esta
situación.
Resumen
En este capítulo, aprendiste cómo agregar una gran cantidad de
elementos idénticos a un juego creando una flota de alienígenas.
Usaste bucles anidados para crear una cuadrícula de elementos e
hiciste que un gran conjunto de elementos del juego se movieran
llamando al método update() de cada elemento. Aprendiste a
controlar la dirección de los objetos en la pantalla y a responder a
situaciones específicas, como cuando la flota llega al borde de la
pantalla. Detectaste y respondiste a colisiones cuando las balas
alcanzaron a los extraterrestres y los extraterrestres impactaron en
la nave. También aprendió a realizar un seguimiento de las
estadísticas de un juego y a utilizar una bandera game_active para
determinar cuándo termina el juego.
En el siguiente y último capítulo de este proyecto, agregaremos un
botón Jugar para que el jugador pueda elegir cuándo comenzar su
primer juego y si desea volver a jugar cuando finalice el juego.
Aceleraremos el juego cada vez que el jugador derribe a toda la flota
y agregaremos un sistema de puntuación. ¡El resultado final será un
juego totalmente jugable!
14
Puntuación
invasión_alienígena.py
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
--snip--
botón.py
import pygame.font
class Button:
"""A class to build buttons for the game."""
botón.py
def draw_button(self):
"""Draw blank button and then draw message."""
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
--snip--
from game_stats import GameStats
from button import Button
def __init__(self):
--snip--
self.game_active = False
Este código crea una instancia de Button con la etiqueta Play, pero
no dibuja el botón en la pantalla. Para hacer esto, llamaremos al
método draw_button() del botón en _update_screen():
invasión_alienígena.py
def _update_screen(self):
--snip--
self.aliens.draw(self.screen)
pygame.display.flip()
Para que el botón Reproducir sea visible sobre todos los demás
elementos de la pantalla, lo dibujamos después de que se hayan
dibujado todos los demás elementos, pero antes de pasar a una
nueva pantalla. Lo incluimos en un bloque if, por lo que el botón
solo aparece cuando el juego está inactivo.
Ahora, cuando ejecutes Alien Invasion, deberías ver un botón
Reproducir en el centro de la pantalla, como se muestra en la Figura
14-1.
Comenzando el juego
Para iniciar un nuevo juego cuando el jugador hace clic en Jugar,
agregue el siguiente bloque elif al final de _check_events() para
monitorear los eventos del mouse sobre el botón:
invasión_alienígena.py
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip--
❶ elif event.type == pygame.MOUSEBUTTONDOWN:
❷ mouse_pos = pygame.mouse.get_pos()
❸ self._check_play_button(mouse_pos)
invasión_alienígena.py
Reiniciar el juego
El código del botón Reproducir que acabamos de escribir funciona la
primera vez que el jugador hace clic en Reproducir. Pero no funciona
después de que termina el primer juego, porque las condiciones que
causaron que el juego terminara no se han restablecido.
Para restablecer el juego cada vez que el jugador hace clic en Jugar,
necesitamos restablecer las estadísticas del juego, eliminar los viejos
alienígenas y las balas, construir una nueva flota y centrar la nave,
como se muestra aquí:
invasión_alienígena.py
invasión_alienígena.py
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
--snip--
else:
self.game_active = False
pygame.mouse.set_visible(True)
Hacemos que el cursor vuelva a ser visible tan pronto como el juego
se vuelve inactivo, lo que sucede en _ship_hit(). La atención a
detalles como este hace que tu juego tenga un aspecto más
profesional y permite al jugador concentrarse en jugar, en lugar de
descubrir la interfaz de usuario.
14-1. Presiona P para jugar: debido a que Alien Invasion usa la entrada del teclado
para controlar la nave, sería útil comenzar el juego presionando una tecla. Agregue
código que le permita al jugador presionar P para comenzar. Podría ser útil mover algo
de código de _check_play_button() a un método _start_game() que se pueda llamar
desde _check_play_button() y _check_keydown_events().
14-2. Práctica de tiro: cree un rectángulo en el borde derecho de la pantalla que se
mueva hacia arriba y hacia abajo a un ritmo constante. Luego, en el lado izquierdo de
la pantalla, crea un barco que el jugador puede mover hacia arriba y hacia abajo
mientras dispara balas al objetivo rectangular. Agrega un botón Jugar que inicia el
juego, y cuando el jugador falla el objetivo tres veces, finaliza el juego y haz que el
botón Jugar vuelva a aparecer. Deje que el jugador reinicie el juego con este botón
Jugar.
Subir de nivel
En nuestro juego actual, una vez que un jugador derriba a toda la
flota alienígena, alcanza un nuevo nivel, pero la dificultad del juego
no cambia. Animemos un poco las cosas y hagamos que el juego
sea más desafiante aumentando la velocidad del juego cada vez que
un jugador borra la pantalla.
def __init__(self):
"""Initialize the game's static settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_limit = 3
# Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
# Alien settings
self.fleet_drop_speed = 10
❷ self.initialize_dynamic_settings()
Continuamos inicializando configuraciones que permanecen
constantes en el método __init__(). Agregamos una configuración
speedup_scale ❶ para controlar qué tan rápido se acelera el juego:
un valor de 2 duplicará la velocidad del juego cada vez que el
jugador alcance un nuevo nivel; un valor de 1 mantendrá la
velocidad constante. Un valor como 1.1 debería aumentar la
velocidad lo suficiente como para que el juego sea desafiante pero
no imposible. Finalmente, llamamos al método
initialize_dynamic_settings() para inicializar los valores de los
atributos que deben cambiar a lo largo del juego ❷.
Aquí está el código de initialize_dynamic_settings():
configuración.py
def initialize_dynamic_settings(self):
"""Initialize settings that change throughout the
game."""
self.ship_speed = 1.5
self.bullet_speed = 2.5
self.alien_speed = 1.0
Este método establece los valores iniciales para las velocidades del
barco, la bala y el alienígena. Aumentaremos estas velocidades a
medida que el jugador avance en el juego y las restableceremos
cada vez que el jugador comience un nuevo juego. Incluimos
fleet_direction en este método para que los alienígenas siempre se
muevan justo al comienzo de un nuevo juego. No necesitamos
aumentar el valor de fleet_drop_speed, porque cuando los
alienígenas se mueven más rápido por la pantalla, también bajarán
más rápido por la pantalla.
Para aumentar las velocidades de la nave, las balas y los alienígenas
cada vez que el jugador alcanza un nuevo nivel, escribiremos un
nuevo método llamado increase_speed():
configuración.py
def increase_speed(self):
"""Increase speed settings."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
def _check_bullet_alien_collisions(self):
--snip--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
Restablecer la velocidad
Ahora necesitamos devolver cualquier configuración modificada a
sus valores iniciales cada vez que el jugador comience un nuevo
juego; de lo contrario, cada nuevo juego comenzaría con la
configuración de velocidad aumentada del juego anterior:
invasión_alienígena.py
14-3. Práctica de tiro desafiante: comience con su trabajo del ejercicio 14-2 (página
283). Haga que el objetivo se mueva más rápido a medida que avanza el juego y
reinicie el objetivo a la velocidad original cuando el jugador haga clic en Jugar.
14-4. Niveles de dificultad: crea un conjunto de botones para Alien Invasion que
permitan al jugador seleccionar un nivel de dificultad inicial apropiado para el juego.
Cada botón debe asignar los valores apropiados para los atributos en Settings
necesarios para crear diferentes niveles de dificultad.
Tanteo
Implementemos un sistema de puntuación para realizar un
seguimiento de la puntuación del juego en tiempo real y mostrar la
puntuación más alta, el nivel y la cantidad de barcos restantes.
La puntuación es una estadística del juego, por lo que agregaremos
un atributo score a GameStats:
juego_stats.py
class GameStats:
--snip--
def reset_stats(self):
"""Initialize statistics that can change during the
game."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
Mostrando la puntuación
Para mostrar la puntuación en la pantalla, primero creamos una
nueva clase, Scoreboard. Por ahora, esta clase solo mostrará la
puntuación actual. Con el tiempo, también lo usaremos para
informar la puntuación más alta, el nivel y la cantidad de barcos
restantes. Aquí está la primera parte de la clase; guárdelo como
marcador.py:
marcador.py
import pygame.font
class Scoreboard:
"""A class to report scoring information."""
marcador.py
def prep_score(self):
"""Turn the score into a rendered image."""
❶ score_str = str(self.stats.score)
❷ self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
Hacer un marcador
Para mostrar la puntuación, crearemos una instancia Scoreboard en
AlienInvasion. Primero, actualicemos las declaraciones import:
invasión_alienígena.py
--snip--
from game_stats import GameStats
from scoreboard import Scoreboard
--snip--
invasión_alienígena.py
def __init__(self):
--snip--
pygame.display.set_caption("Alien Invasion")
invasión_alienígena.py
def _update_screen(self):
--snip--
self.aliens.draw(self.screen)
def initialize_dynamic_settings(self):
--snip--
# Scoring settings
self.alien_points = 50
Aumentaremos el valor de puntos de cada alienígena a medida que
avance el juego. Para asegurarnos de que este valor de puntos se
restablezca cada vez que comienza un nuevo juego, configuramos el
valor en initialize_dynamic_settings().
Actualicemos la puntuación en _check_bullet_alien_collisions()
cada vez que derriben a un extraterrestre:
invasión_alienígena.py
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if collisions:
self.stats.score += self.settings.alien_points
self.sb.prep_score()
--snip--
Restablecer la puntuación
En este momento, solo estamos preparando una nueva puntuación
después de que un alienígena haya sido golpeado, lo cual funciona
durante la mayor parte del juego. Pero cuando comenzamos un
nuevo juego, seguiremos viendo nuestra puntuación del juego
anterior hasta que golpeemos al primer alienígena.
Podemos solucionar este problema preparando la puntuación al
iniciar un nuevo juego:
invasión_alienígena.py
def _check_bullet_alien_collisions(self):
--snip--
if collisions:
for aliens in collisions.values():
self.stats.score +=
self.settings.alien_points * len(aliens)
self.sb.prep_score()
--snip--
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
--snip--
# How quickly the game speeds up
self.speedup_scale = 1.1
# How quickly the alien point values increase
❶ self.score_scale = 1.5
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
--snip--
def increase_speed(self):
"""Increase speed settings and alien point values."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
❷ self.alien_points = int(self.alien_points *
self.score_scale)
def increase_speed(self):
--snip--
self.alien_points = int(self.alien_points *
self.score_scale)
print(self.alien_points)
Nota
Redondeando la puntuación
La mayoría de los juegos de disparos estilo arcade reportan
puntuaciones como múltiplos de 10, así que sigamos ese ejemplo
con nuestras puntuaciones. Además, formateemos la partitura para
incluir separadores de coma en números grandes. Haremos este
cambio en Scoreboard:
marcador.py
def prep_score(self):
"""Turn the score into a rendered image."""
rounded_score = round(self.stats.score, -1)
score_str = f"{rounded_score:,}"
self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
--snip--
Puntajes altos
Todos los jugadores quieren superar la puntuación más alta de un
juego, así que hagamos un seguimiento e informemos las
puntuaciones altas para darles a los jugadores algo por lo que
trabajar. Almacenaremos puntuaciones altas en GameStats:
juego_stats.py
marcador.py
def prep_high_score(self):
"""Turn the high score into a rendered image."""
❶ high_score = round(self.stats.high_score, -1)
high_score_str = f"{high_score:,}"
❷ self.high_score_image =
self.font.render(high_score_str, True,
self.text_color, self.settings.bg_color)
def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image,
self.high_score_rect)
marcador.py
def check_high_score(self):
"""Check to see if there's a new high score."""
if self.stats.score > self.stats.high_score:
self.stats.high_score = self.stats.score
self.prep_high_score()
invasión_alienígena.py
def _check_bullet_alien_collisions(self):
--snip--
if collisions:
for aliens in collisions.values():
self.stats.score +=
self.settings.alien_points * len(aliens)
self.sb.prep_score()
self.sb.check_high_score()
--snip--
Llamamos a check_high_score() cuando el diccionario collisions
está presente y lo hacemos después de actualizar la puntuación de
todos los alienígenas que han sido alcanzados.
La primera vez que juegues Alien Invasion, tu puntuación será la
puntuación más alta, por lo que se mostrará como la puntuación
actual y la puntuación más alta. Pero cuando comienzas un segundo
juego, tu puntuación más alta debería aparecer en el medio y tu
puntuación actual debería aparecer a la derecha, como se muestra
en la Figura 14-4.
Figura 14-4: La puntuación más alta se muestra en la parte superior central de la pantalla.
Mostrando el nivel
Para mostrar el nivel del jugador en el juego, primero necesitamos
un atributo en GameStats que represente el nivel actual. Para
restablecer el nivel al comienzo de cada nuevo juego, inicialízalo en
reset_stats():
juego_stats.py
def reset_stats(self):
"""Initialize statistics that can change during the
game."""
self.ships_left = self.settings.ship_limit
self.score = 0
self.level = 1
marcador.py
def prep_level(self):
"""Turn the level into a rendered image."""
level_str = str(self.stats.level)
❶ self.level_image = self.font.render(level_str, True,
self.text_color, self.settings.bg_color)
marcador.py
def show_score(self):
"""Draw scores and level to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image,
self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
def _check_bullet_alien_collisions(self):
--snip--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
# Increase level.
self.stats.level += 1
self.sb.prep_level()
Nota
import pygame
from pygame.sprite import Sprite
❶ class Ship(Sprite):
"""A class to manage the ship."""
import pygame.font
from pygame.sprite import Group
marcador.py
marcador.py
def prep_ships(self):
"""Show how many ships are left."""
❶ self.ships = Group()
❷ for ship_number in range(self.stats.ships_left):
ship = Ship(self.ai_game)
❸ ship.rect.x = 10 + ship_number * ship.rect.width
❹ ship.rect.y = 10
❺ self.ships.add(ship)
def show_score(self):
"""Draw scores, level, and ships to the screen."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image,
self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
self.ships.draw(self.screen)
def _ship_hit(self):
"""Respond to ship being hit by alien."""
if self.stats.ships_left > 0:
# Decrement ships_left, and update scoreboard.
self.stats.ships_left -= 1
self.sb.prep_ships()
--snip--
14-5. Puntuación más alta de todos los tiempos: la puntuación más alta se restablece
cada vez que un jugador cierra y reinicia Alien Invasion. Solucione este problema
escribiendo la puntuación más alta en un archivo antes de llamar a sys.exit() y
leyendo la puntuación más alta al inicializar su valor en GameStats.
14-6. Refactorización: busque métodos que realicen más de una tarea y refactorícelos
para organizar su código y hacerlo eficiente. Por ejemplo, mueva parte del código en
_check_bullet_alien_collisions(), que inicia un nuevo nivel cuando la flota alienígena
ha sido destruida, a una función llamada start_new_level(). Además, mueva las cuatro
llamadas a métodos separados en el método __init__() en Scoreboard a un método
llamado prep_images() para acortar __init__(). El método prep_images() también
podría ayudar a simplificar _check_play_button() o start_game() si ya ha refactorizado
_check_play_button().
NOTA
Antes de intentar refactorizar el proyecto, consulte el Apéndice D para
aprender cómo restaurar el proyecto a un estado de funcionamiento si
introduce errores durante la refactorización.
14-7. Ampliando el juego: piensa en una manera de expandir Alien Invasion. Por
ejemplo, puedes programar a los extraterrestres para que disparen balas a tu nave.
También puedes agregar escudos para que tu nave se esconda detrás, los cuales
pueden ser destruidos por balas desde cualquier lado. O puedes usar algo como el
módulo pygame.mixer para agregar efectos de sonido, como explosiones y sonidos de
disparos.
14-8. Sideways Shooter, versión final: continúa desarrollando Sideways Shooter,
utilizando todo lo que hemos hecho en este proyecto. Agregue un botón Jugar, haga
que el juego se acelere en los puntos apropiados y desarrolle un sistema de
puntuación. Asegúrate de refactorizar tu código mientras trabajas y busca
oportunidades para personalizar el juego más allá de lo que se ha mostrado en este
capítulo.
Resumen
En este capítulo, aprendiste cómo implementar un botón Jugar para
iniciar un nuevo juego. También aprendiste cómo detectar eventos
del mouse y ocultar el cursor en juegos activos. Puedes usar lo que
has aprendido para crear otros botones, como un botón de Ayuda
para mostrar instrucciones sobre cómo jugar. También aprendió a
modificar la velocidad de un juego a medida que avanza,
implementar un sistema de puntuación progresivo y mostrar
información en forma textual y no textual.
15
Generando datos
Instalación de Matplotlib
Para usar Matplotlib para su conjunto inicial de visualizaciones,
deberá instalarlo usando pip, tal como lo hicimos con pytest en el
Capítulo 11 (consulte “Instalación de pytest con pip” en la página
210).
Para instalar Matplotlib, ingrese el siguiente comando en el símbolo
del terminal:
$ python -m pip install --user matplotlib
❶ fig, ax = plt.subplots()
ax.plot(squares)
plt.show()
plt.show()
Corrigiendo la trama
Ahora que podemos leer mejor el gráfico, podemos ver que los datos
no están trazados correctamente. ¡Observe que al final del gráfico el
cuadrado de 4,0 se muestra como 25! Arreglemos eso.
Cuando le das a plot() una única secuencia de números, se supone
que el primer punto de datos corresponde a un valor x de 0, pero
nuestro primer punto corresponde a un valor x de 1. Podemos anular
el comportamiento predeterminado dando plot() los valores de
entrada y salida utilizados para calcular los cuadrados:
mpl_cuadrados.py
input_values = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
fig, ax = plt.subplots()
ax.plot(input_values, squares, linewidth=3)
input_values = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
plt.style.use('seaborn')
fig, ax = plt.subplots()
--snip--
scatter_squares.py
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(2, 4)
plt.show()
plt.style.use('seaborn')
fig, ax = plt.subplots()
❶ ax.scatter(2, 4, s=200)
plt.show()
x_values = [1, 2, 3, 4, 5]
y_values = [1, 4, 9, 16, 25]
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(x_values, y_values, s=100)
plt.show()
--snip--
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues,
s=10)
Nota
plt.savefig('squares_plot.png', bbox_inches='tight')
15-1. Cubos: Un número elevado a la tercera potencia es un cubo. Traza los primeros
cinco números cúbicos y luego traza los primeros 5000 números cúbicos.
15-2. Cubos de colores: aplique un mapa de colores al gráfico de sus cubos.
Paseos aleatorios
En esta sección, usaremos Python para generar datos para un
recorrido aleatorio y luego usaremos Matplotlib para crear una
representación visualmente atractiva de esos datos. Un paseo
aleatorio es un camino determinado por una serie de decisiones
simples, cada una de las cuales se deja enteramente al azar.
Podríamos imaginar un paseo aleatorio como el camino que tomaría
una hormiga confundida si diera cada paso en una dirección
aleatoria.
Los paseos aleatorios tienen aplicaciones prácticas en la naturaleza,
la física, la biología, la química y la economía. Por ejemplo, un grano
de polen que flota en una gota de agua se mueve a través de la
superficie del agua porque las moléculas de agua lo empujan
constantemente. El movimiento molecular en una gota de agua es
aleatorio, por lo que el camino que traza un grano de polen en la
superficie es un paseo aleatorio. El código que escribiremos a
continuación modela muchas situaciones del mundo real.
class RandomWalk:
"""A class to generate random walks."""
Elegir direcciones
Usaremos el método fill_walk() para determinar la secuencia
completa de puntos en la caminata. Agregue este método a
random_walk.py:
paseo_aleatorio.py
def fill_walk(self):
"""Calculate all the points in the walk."""
# Keep taking steps until the walk reaches the
desired length.
❶ while len(self.x_values) < self.num_points:
self.x_values.append(x)
self.y_values.append(y)
Diseñar la caminata
En esta sección, personalizaremos nuestras tramas para enfatizar las
características importantes de cada caminata y restar importancia a
los elementos que distraen. Para ello, identificamos las
características que queremos resaltar, como dónde empezó el paseo,
dónde terminó y el camino recorrido. A continuación, identificamos
las características a restar importancia, como marcas y etiquetas. El
resultado debe ser una representación visual sencilla que comunique
claramente el camino seguido en cada paseo aleatorio.
--snip--
while True:
# Make a random walk.
rw = RandomWalk()
rw.fill_walk()
--snip--
while True:
--snip--
ax.scatter(rw.x_values, rw.y_values, c=point_numbers,
cmap=plt.cm.Blues,
edgecolors='none', s=15)
ax.set_aspect('equal')
--snip--
while True:
--snip--
ax.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
edgecolors='none',
s=100)
plt.show()
--snip--
--snip--
while True:
# Make a random walk.
rw = RandomWalk(50_000)
rw.fill_walk()
Este ejemplo crea un paseo aleatorio con 50.000 puntos y traza cada
punto con un tamaño s=1. La caminata resultante es tenue y
parecida a una nube, como se muestra en la Figura 15-11. ¡Hemos
creado una obra de arte a partir de un simple diagrama de
dispersión!
Experimente con este código para ver cuánto puede aumentar la
cantidad de puntos en una caminata antes de que su sistema
comience a disminuir significativamente o la trama pierda su
atractivo visual.
Figura 15-11: Un paseo con 50.000 puntos
x_step = self.get_step()
y_step = self.get_step()
Instalación de trama
Instale Plotly usando pip, tal como lo hizo con Matplotlib:
class Die:
"""A class representing a single die."""
def roll(self):
""""Return a random value between 1 and number of
sides."""
❷ return randint(1, self.num_sides)
# Create a D6.
❶ die = Die()
print(results)
[4, 6, 5, 6, 1, 5, 6, 3, 5, 3, 5, 3, 2, 2, 1, 3, 1, 5, 3, 6,
3, 6, 5, 4, 1, 1, 4, 2, 3, 6, 4, 2, 6, 4, 1, 3, 2, 5, 6, 3,
6, 2, 1, 1, 3, 4, 1, 4, 3, 5, 1, 4, 5, 5, 2, 3, 3, 1, 2, 3,
5, 6, 2, 5, 6, 1, 3, 2, 1, 1, 1, 6, 5, 5, 2, 2, 6, 4, 1, 4,
5, 1, 1, 1, 4, 5, 3, 3, 1, 3, 5, 4, 5, 6, 5, 4, 1, 5, 1, 2]
--snip--
# Make some rolls, and store results in a list.
results = []
❶ for roll_num in range(1000):
result = die.roll()
results.append(result)
print(frequencies)
Hacer un histograma
Ahora que tenemos los datos que queremos, podemos generar una
visualización en solo un par de líneas de código usando Plotly
Express:
die_visual.py
import plotly.express as px
Personalizando la trama
Ahora que sabemos que tenemos el tipo correcto de gráfico y que
nuestros datos se representan con precisión, podemos
concentrarnos en agregar las etiquetas y estilos apropiados para el
gráfico.
La primera forma de personalizar un gráfico con Plotly es utilizar
algunos parámetros opcionales en la llamada inicial que genera el
gráfico, en este caso, px.bar(). A continuación se explica cómo
agregar un título general y una etiqueta para cada eje:
die_visual.py
--snip--
# Visualize the results.
❶ title = "Results of Rolling One D6 1,000 Times"
❷ labels = {'x': 'Result', 'y': 'Frequency of Result'}
fig = px.bar(x=poss_results, y=frequencies, title=title,
labels=labels)
fig.show()
import plotly.express as px
Más personalizaciones
Hay un problema que deberíamos abordar con la trama que
acabamos de generar. Ahora que hay 11 barras, la configuración de
diseño predeterminada para el eje x deja algunas de las barras sin
etiquetar. Si bien la configuración predeterminada funciona bien para
la mayoría de las visualizaciones, este gráfico se vería mejor con
todas las barras etiquetadas.
Plotly tiene un método update_layout() que se puede utilizar para
realizar una amplia variedad de actualizaciones a una figura después
de haberla creado. Aquí se explica cómo decirle a Plotly que le dé a
cada barra su propia etiqueta:
dados_visual.py
--snip--
fig = px.bar(x=poss_results, y=frequencies, title=title,
labels=labels)
fig.show()
import plotly.express as px
Ahorro de cifras
Cuando tengas una figura que te guste, siempre puedes guardar el
gráfico como un archivo HTML a través de tu navegador. Pero
también puedes hacerlo mediante programación. Para guardar su
gráfico como un archivo HTML, reemplace la llamada a fig.show()
con una llamada a fig.write_html():
fig.write_html('dice_visual_d6d10.html')
El método write_html() requiere un argumento: el nombre del
archivo en el que escribir. Si solo proporciona un nombre de archivo,
el archivo se guardará en el mismo directorio que el archivo .py.
También puede llamar a write_html() con un objeto Path y escribir
el archivo de salida en cualquier lugar que desee de su sistema.
15-6. Dos D8: crea una simulación que muestre lo que sucede cuando lanzas dos
dados de ocho caras 1000 veces. Intente imaginarse cómo cree que se verá la
visualización antes de ejecutar la simulación, luego vea si su intuición fue correcta.
Aumente gradualmente la cantidad de tiradas hasta que comience a ver los límites de
las capacidades de su sistema.
15-7. Tres dados: cuando tiras tres dados D6, el número más pequeño que puedes tirar
es 3 y el número más grande es 18. Crea una visualización que muestre lo que sucede
cuando tiras tres dados D6.
15-8. Multiplicación: cuando tiras dos dados, normalmente sumas los dos números
para obtener el resultado. Crea una visualización que muestre lo que sucede si
multiplicas estos números entre sí.
15-9. Comprensiones: para mayor claridad, los listados de esta sección utilizan la forma
larga de bucles for. Si se siente cómodo usando listas por comprensión, intente
escribir una comprensión para uno o ambos bucles en cada uno de estos programas.
15-10. Practicando con ambas bibliotecas: intente usar Matplotlib para hacer una
visualización de lanzamiento de dados y use Plotly para hacer la visualización de un
recorrido aleatorio. (Deberá consultar la documentación de cada biblioteca para
completar este ejercicio).
Resumen
En este capítulo, aprendió a generar conjuntos de datos y crear
visualizaciones de esos datos. Creaste gráficos simples con
Matplotlib y usaste un diagrama de dispersión para explorar paseos
aleatorios. También creaste un histograma con Plotly y lo usaste
para explorar los resultados de lanzar dados de diferentes tamaños.
Generar sus propios conjuntos de datos con código es una forma
interesante y poderosa de modelar y explorar una amplia variedad
de situaciones del mundo real. A medida que continúe trabajando en
los proyectos de visualización de datos que siguen, esté atento a las
situaciones que podría modelar con código. Mire las visualizaciones
que ve en los medios de comunicación y vea si puede identificar
aquellas que se generaron utilizando métodos similares a los que
está aprendiendo en estos proyectos.
En el Capítulo 16, descargará datos de fuentes en línea y continuará
usando Matplotlib y Plotly para explorar esos datos.
16
Descargando datos
❶ path = Path('weather_data/sitka_weather_07-2021_simple.csv')
lines = path.read_text().splitlines()
❷ reader = csv.reader(lines)
❸ header_row = next(reader)
print(header_row)
--snip--
reader = csv.reader(lines)
header_row = next(reader)
for index, column_header in enumerate(header_row):
print(index, column_header)
0 STATION
1 NAME
2 DATE
3 TAVG
4 TMAX
5 TMIN
--snip--
reader = csv.reader(lines)
header_row = next(reader)
Hacemos una lista vacía llamada highs ❶ y luego recorremos las filas
restantes del archivo ❷. El objeto reader continúa desde donde lo
dejó en el archivo CSV y devuelve automáticamente cada línea
siguiendo su posición actual. Como ya leímos la fila del encabezado,
el bucle comenzará en la segunda línea donde comienzan los datos
reales. En cada paso por el bucle extraemos los datos del índice 4,
correspondiente al encabezado TMAX, y los asignamos a la variable
high ❸. Usamos la función int() para convertir los datos, que se
almacenan como una cadena, a un formato numérico para que
podamos usarlos. Luego agregamos este valor a highs.
La siguiente lista muestra los datos ahora almacenados en highs:
[61, 60, 66, 60, 65, 59, 58, 58, 57, 60, 60, 60, 57, 58, 60,
61, 63, 63, 70, 64, 59, 63, 61, 58, 59, 64, 62, 70, 70, 73,
66]
path = Path('weather_data/sitka_weather_07-2021_simple.csv')
lines = path.read_text().splitlines()
--snip--
# Plot the high temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
❶ ax.plot(highs, color='red')
# Format plot.
❷ ax.set_title("Daily High Temperatures, July 2021",
fontsize=24)
❸ ax.set_xlabel('', fontsize=16)
ax.set_ylabel("Temperature (F)", fontsize=16)
ax.tick_params(labelsize=16)
plt.show()
Los datos se leerán como una cadena, por lo que necesitamos una
forma de convertir la cadena "2021-07-01" en un objeto que
represente esta fecha. Podemos construir un objeto que represente
el 1 de julio de 2021 utilizando el método strptime() del módulo
datetime. Veamos cómo funciona strptime() en una sesión de
terminal:
>>> from datetime import datetime
>>> first_date = datetime.strptime('2021-07-01', '%Y-%m-%d')
>>> print(first_date)
2021-07-01 00:00:00
Argumento Significado
%A Nombre del día de la semana, como lunes
%B Nombre del mes, como enero
%m Mes, como número (01 a 12)
%d Día del mes, como número (01 a 31)
%Y Año de cuatro dígitos, como 2019
%y Año de dos dígitos, como 19
%H Hora, en formato de 24 horas (00 a 23)
%I Hora, en formato de 12 horas (01 a 12)
%p mañana o tarde
%M Minutos (00 a 59)
%S Segundos (00 a 61)
Trazar fechas
Podemos mejorar nuestro gráfico extrayendo fechas para las lecturas
diarias de temperatura alta y usando estas fechas en el eje x:
sitka_highs.py
from pathlib import Path
import csv
from datetime import datetime
path = Path('weather_data/sitka_weather_07-2021_simple.csv')
lines = path.read_text().splitlines()
reader = csv.reader(lines)
header_row = next(reader)
# Format plot.
ax.set_title("Daily High Temperatures, July 2021",
fontsize=24)
ax.set_xlabel('', fontsize=16)
❹ fig.autofmt_xdate()
ax.set_ylabel("Temperature (F)", fontsize=16)
ax.tick_params(labelsize=16)
plt.show()
--snip--
path = Path('weather_data/sitka_weather_2021_simple.csv')
lines = path.read_text().splitlines()
--snip--
# Format plot.
ax.set_title("Daily High Temperatures, 2021", fontsize=24)
ax.set_xlabel('', fontsize=16)
--snip--
Modificamos el nombre del archivo para usar el nuevo archivo de
datos sitka_weather_2021_simple.csv y actualizamos el título de
nuestro gráfico para reflejar el cambio en su contenido. La Figura
16-3 muestra el gráfico resultante.
--snip--
reader = csv.reader(lines)
header_row = next(reader)
# Format plot.
❹ ax.set_title("Daily High and Low Temperatures, 2021",
fontsize=24)
--snip--
--snip--
# Plot the high and low temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
❶ ax.plot(dates, highs, color='red', alpha=0.5)
ax.plot(dates, lows, color='blue', alpha=0.5)
❷ ax.fill_between(dates, highs, lows, facecolor='blue',
alpha=0.1)
--snip--
El argumento alpha controla la transparencia de un color ❶. Un valor
alpha de 0 es completamente transparente y un valor de 1 (el valor
predeterminado) es completamente opaco. Al establecer alpha en
0,5, hacemos que las líneas gráficas rojas y azules parezcan más
claras.
Pasamos fill_between() la lista dates para los valores de x y luego
las dos series de valores de y highs y lows ❷. El argumento
facecolor determina el color de la región sombreada; le damos un
valor bajo de alpha de 0,1 para que la región rellena conecte las dos
series de datos sin distraer la atención de la información que
representan. La Figura 16-5 muestra el gráfico con la región
sombreada entre los máximos y mínimos.
Figura 16-5: La región entre los dos conjuntos de datos está sombreada.
path = Path('weather_data/death_valley_2021_simple.csv')
lines = path.read_text().splitlines()
reader = csv.reader(lines)
header_row = next(reader)
0 STATION
1 NAME
2 DATE
3 TMAX
4 TMIN
5 TOBS
La fecha está en la misma posición, en el índice 2. Pero las
temperaturas máximas y mínimas están en los índices 3 y 4, por lo
que necesitaremos cambiar los índices en nuestro código para
reflejar estas nuevas posiciones. En lugar de incluir una lectura de
temperatura promedio para el día, esta estación incluye TOBS, una
lectura para un tiempo de observación específico.
Cambie sitka_highs_lows.py para generar un gráfico para el Valle de
la Muerte usando los índices que acabamos de anotar y vea qué
sucede:
valle_muerte_altos_bajos.py
--snip--
path = Path('weather_data/death_valley_2021_simple.csv')
lines = path.read_text().splitlines()
--snip--
# Extract dates, and high and low temperatures.
dates, highs, lows = [], [], []
for row in reader:
current_date = datetime.strptime(row[2], '%Y-%m-%d')
high = int(row[3])
low = int(row[4])
dates.append(current_date)
--snip--
--snip--
for row in reader:
current_date = datetime.strptime(row[2], '%Y-%m-%d')
❶ try:
high = int(row[3])
low = int(row[4])
except ValueError:
❷ print(f"Missing data for {current_date}")
❸ else:
dates.append(current_date)
highs.append(high)
lows.append(low)
# Format plot.
❹ title = "Daily High and Low Temperatures, 2021\nDeath Valley,
CA"
ax.set_title(title, fontsize=20)
ax.set_xlabel('', fontsize=16)
--snip--
A muchos conjuntos de datos con los que trabaja les faltarán datos,
estarán formateados incorrectamente o serán incorrectos. Puede
utilizar las herramientas que aprendió en la primera mitad de este
libro para manejar estas situaciones. Aquí utilizamos un bloque try-
except-else para manejar los datos faltantes. A veces usarás
continue para omitir algunos datos, o usarás remove() o del para
eliminar algunos datos después de haberlos extraído. Utilice
cualquier enfoque que funcione, siempre que el resultado sea una
visualización significativa y precisa.
16-1. Precipitaciones de Sitka: Sitka se encuentra en una selva tropical templada, por
lo que recibe una buena cantidad de lluvia. En el archivo de datos
sitka_weather_2021_full.csv hay un encabezado llamado PRCP, que representa las
cantidades de lluvia diarias. Haga una visualización centrándose en los datos de esta
columna. Puedes repetir el ejercicio para el Valle de la Muerte si tienes curiosidad sobre
la poca lluvia que ocurre en un desierto.
16-2. Comparación de Sitka y el Valle de la Muerte: Las escalas de temperatura en los
gráficos de Sitka y el Valle de la Muerte reflejan los diferentes rangos de datos. Para
comparar con precisión el rango de temperatura en Sitka con el del Valle de la Muerte,
se necesitan escalas idénticas en el eje y. Cambie la configuración para el eje y en uno
o ambos gráficos en las Figuras 16-5 y 16-6. Luego haga una comparación directa
entre los rangos de temperatura en Sitka y el Valle de la Muerte (o dos lugares
cualesquiera que desee comparar).
16-3. San Francisco: ¿Las temperaturas en San Francisco se parecen más a las
temperaturas de Sitka o a las temperaturas del Valle de la Muerte? Descargue algunos
datos de San Francisco y genere un gráfico de temperaturas altas y bajas para San
Francisco para hacer una comparación.
16-4. Índices automáticos: en esta sección, codificamos los índices correspondientes a
las columnas TMIN y TMAX. Utilice la fila del encabezado para determinar los índices de
estos valores, de modo que su programa pueda funcionar para Sitka o Death Valley.
Utilice también el nombre de la estación para generar automáticamente un título
apropiado para su gráfico.
16-5. Explorar: genere algunas visualizaciones más que examinen cualquier otro
aspecto meteorológico que le interese para cualquier ubicación que le interese.
{"type":"FeatureCollection","metadata":
{"generated":1649052296000,...
{"type":"Feature","properties":{"mag":1.6,"place":"63 km SE
of Ped...
{"type":"Feature","properties":{"mag":2.2,"place":"27 km SSE
of Ca...
{"type":"Feature","properties":{"mag":3.7,"place":"102 km SSE
of S...
{"type":"Feature","properties":{"mag":2.92000008,"place":"49
km SE...
{"type":"Feature","properties":{"mag":1.4,"place":"44 km NE
of Sus...
--snip--
--snip--
{
"type": "Feature",
❶ "properties": {
"mag": 1.6,
--snip--
❷ "title": "M 1.6 - 27 km NNW of Susitna,
Alaska"
},
❸ "geometry": {
"type": "Point",
"coordinates": [
❹ -150.7585,
❺ 61.7591,
56.3
]
},
"id": "ak0224bju1jx"
},
Extrayendo magnitudes
Podemos recorrer la lista que contiene datos sobre cada terremoto y
extraer cualquier información que queramos. Saquemos la magnitud
de cada terremoto:
eq_explore_data.py
--snip--
all_eq_dicts = all_eq_data['features']
❶ mags = []
for eq_dict in all_eq_dicts:
❷ mag = eq_dict['properties']['mag']
mags.append(mag)
print(mags[:10])
[1.6, 1.6, 2.2, 3.7, 2.92000008, 1.4, 4.6, 4.5, 1.9, 1.8]
A continuación, obtendremos los datos de ubicación de cada
terremoto y luego podremos hacer un mapa de los terremotos.
--snip--
all_eq_dicts = all_eq_data['features']
print(mags[:10])
print(lons[:5])
print(lats[:5])
[1.6, 1.6, 2.2, 3.7, 2.92000008, 1.4, 4.6, 4.5, 1.9, 1.8]
[-150.7585, -153.4716, -148.7531, -159.6267,
-155.248336791992]
[61.7591, 59.3152, 63.1633, 54.5612, 18.7551670074463]
import plotly.express as px
--snip--
for eq_dict in all_eq_dicts:
--snip--
Representando Magnitudes
Un mapa de actividad sísmica debe mostrar la magnitud de cada
terremoto. También podemos incluir más datos, ahora que sabemos
que los datos se están trazando correctamente.
--snip--
# Read data as a string and convert to a Python object.
path = Path('eq_data/eq_data_30_day_m1.geojson')
contents = path.read_text()
--snip--
title = 'Global Earthquakes'
fig = px.scatter_geo(lat=lats, lon=lons, size=mags,
title=title)
fig.show()
--snip--
fig = px.scatter_geo(lat=lats, lon=lons, size=mags,
title=title,
❶ color=mags,
❷ color_continuous_scale='Viridis',
❸ labels={'color':'Magnitude'},
❹ projection='natural earth',
)
fig.show()
--snip--
❶ mags, lons, lats, eq_titles = [], [], [], []
mag = eq_dict['properties']['mag']
lon = eq_dict['geometry']['coordinates'][0]
lat = eq_dict['geometry']['coordinates'][1]
❷ eq_title = eq_dict['properties']['title']
mags.append(mag)
lons.append(lon)
lats.append(lat)
eq_titles.append(eq_title)
16-6. Refactorización: el bucle que extrae datos de all_eq_dicts utiliza variables para
la magnitud, longitud, latitud y título de cada terremoto antes de agregar estos valores
a sus listas apropiadas. Este enfoque se eligió para aclarar cómo extraer datos de un
archivo GeoJSON, pero no es necesario en su código. En lugar de utilizar estas
variables temporales, extraiga cada valor de eq_dict y agréguelo a la lista adecuada en
una línea. Hacerlo debería acortar el cuerpo de este bucle a solo cuatro líneas.
16-7. Título automatizado: en esta sección utilizamos el título genérico Global
Earthquakes. En su lugar, puede utilizar el título del conjunto de datos en la parte de
metadatos del archivo GeoJSON. Extraiga este valor y asígnelo a la variable title.
16-8. Terremotos recientes: puede encontrar archivos de datos en línea que contienen
información sobre los terremotos más recientes durante períodos de 1 hora, 1 día, 7
días y 30 días. Vaya a https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php
y verá una lista de enlaces a conjuntos de datos para varios períodos de tiempo,
centrándose en terremotos de diferentes magnitudes. Descargue uno de estos
conjuntos de datos y cree una visualización de la actividad sísmica más reciente.
16-9. Incendios mundiales: en los recursos de este capítulo, encontrará un archivo
llamado world_fires_1_day.csv. Este archivo contiene información sobre incendios que
arden en diferentes lugares del mundo, incluida la latitud, longitud y brillo de cada
incendio. Utilizando el trabajo de procesamiento de datos de la primera parte de este
capítulo y el trabajo de mapeo de esta sección, haga un mapa que muestre qué partes
del mundo se ven afectadas por los incendios.
Puede descargar versiones más recientes de estos datos en
https://earthdata.nasa.gov/earth-observation-data/near-real-time/firms/active-fire-
data. Puede encontrar enlaces a los datos en formato CSV en la sección Archivos SHP,
KML y TXT.
Resumen
En este capítulo, aprendió a trabajar con conjuntos de datos del
mundo real. Procesó archivos CSV y GeoJSON y extrajo los datos en
los que desea centrarse. Utilizando datos meteorológicos históricos,
aprendió más sobre cómo trabajar con Matplotlib, incluido cómo usar
el módulo datetime y cómo trazar múltiples series de datos en un
gráfico. Trazó datos geográficos en un mapa mundial en Plotly y
aprendió a personalizar el estilo del mapa.
A medida que adquiera experiencia trabajando con archivos CSV y
JSON, podrá procesar casi cualquier dato que desee analizar. Puede
descargar la mayoría de los conjuntos de datos en línea en uno o
ambos formatos. Al trabajar con estos formatos, también podrá
aprender a trabajar con otros formatos de datos más fácilmente.
En el siguiente capítulo, escribirá programas que recopilen
automáticamente sus propios datos de fuentes en línea y luego
creará visualizaciones de esos datos. Estas son habilidades divertidas
si quieres programar como pasatiempo y son habilidades críticas si
estás interesado en programar profesionalmente.
17
Trabajar con API
Git y GitHub
Basaremos nuestra visualización en información de GitHub
(https://github.com), un sitio que permite a los programadores
colaborar en proyectos de codificación. Usaremos la API de GitHub
para solicitar información sobre proyectos de Python en el sitio y
luego generaremos una visualización interactiva de la popularidad
relativa de estos proyectos usando Plotly.
GitHub toma su nombre de Git, un sistema de control de versiones
distribuido. Git ayuda a las personas a administrar su trabajo en un
proyecto de una manera que evita que los cambios realizados por
una persona interfieran con los cambios que realizan otras personas.
Cuando implementas una nueva característica en un proyecto, Git
rastrea los cambios que realizas en cada archivo. Cuando su nuevo
código funciona, confirma los cambios que ha realizado y Git registra
el nuevo estado de su proyecto. Si comete un error y desea revertir
los cambios, puede volver fácilmente a cualquier estado de
funcionamiento anterior. (Para obtener más información sobre el
control de versiones usando Git, consulte el Apéndice D.) Los
proyectos en GitHub se almacenan en repositorios, que contienen
todo lo asociado con el proyecto: su código, información sobre sus
colaboradores, cualquier problema o informe de error, etc.
Cuando a los usuarios de GitHub les gusta un proyecto, pueden
destacarlo para mostrar su apoyo y realizar un seguimiento de los
proyectos que tal vez quieran utilizar. En este capítulo, escribiremos
un programa para descargar automáticamente información sobre los
proyectos de Python más destacados en GitHub y luego crearemos
una visualización informativa de estos proyectos.
Solicitar datos mediante una llamada API
La API de GitHub te permite solicitar una amplia gama de
información a través de llamadas API. Para ver cómo se ve una
llamada API, ingrese lo siguiente en la barra de direcciones de su
navegador y presione ENTER:
https://api.github.com/search/repositories?
q=language:python+sort:stars
{
❶ "total_count": 8961993,
❷ "incomplete_results": true,
❸ "items": [
{
"id": 54346799,
"node_id": "MDEwOlJlcG9zaXRvcnk1NDM0Njc5OQ==",
"name": "public-apis",
"full_name": "public-apis/public-apis",
--snip--
Puede ver en la respuesta que esta URL no está destinada
principalmente a ser ingresada por humanos, porque está en un
formato destinado a ser procesado por un programa. GitHub
encontró poco menos de nueve millones de proyectos de Python al
momento de escribir este artículo ❶. El valor de
"incomplete_results" es true, lo que nos indica que GitHub no
procesó completamente la consulta ❷. GitHub limita el tiempo que
se puede ejecutar cada consulta para mantener la API receptiva para
todos los usuarios. En este caso encontró algunos de los repositorios
de Python más populares, pero no tuvo tiempo de encontrarlos
todos; Lo arreglaremos en un momento. Los "items" devueltos se
muestran en la lista siguiente, que contiene detalles sobre los
proyectos de Python más populares en GitHub ❸.
Instalar solicitudes
El paquete Solicitudes permite que un programa Python solicite
fácilmente información de un sitio web y examine la respuesta.
Utilice pip para instalar Solicitudes:
import requests
# Make an API call and check the response.
❶ url = "https://api.github.com/search/repositories"
url += "?q=language:python+sort:stars+stars:>10000"
# Process results.
print(response_dict.keys())
import requests
❸ Keys: 78
allow_forking
archive_url
archived
--snip--
url
visiblity
watchers
watchers_count
python_repos.py
--snip--
# Examine the first repository.
repo_dict = repo_dicts[0]
Name: public-apis
Owner: public-apis
Stars: 191494
Repository: https://github.com/public-apis/public-apis
Description: A collective list of free APIs
Name: system-design-primer
Owner: donnemartin
Stars: 179952
Repository: https://github.com/donnemartin/system-design-
primer
Description: Learn how to design large-scale systems. Prep
for the system
design interview. Includes Anki flashcards.
--snip--
Name: PayloadsAllTheThings
Owner: swisskyrepo
Stars: 37227
Repository:
https://github.com/swisskyrepo/PayloadsAllTheThings
Description: A list of useful payloads and bypass for Web
Application Security
and Pentest/CTF
{
"resources": {
--snip--
❶ "search": {
❷ "limit": 10,
❸ "remaining": 9,
❹ "reset": 1652338832,
"used": 1,
"resource": "search"
},
--snip--
Nota
import requests
import plotly.express as px
# Make visualization.
❹ fig = px.bar(x=repo_names, y=stars)
fig.show()
--snip--
# Make visualization.
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
fig = px.bar(x=repo_names, y=stars, title=title,
labels=labels)
❶ fig.update_layout(title_font_size=28,
xaxis_title_font_size=20,
yaxis_title_font_size=20)
fig.show()
--snip--
# Process repository information.
repo_dicts = response_dict['items']
❶ repo_names, stars, hover_texts = [], [], []
for repo_dict in repo_dicts:
repo_names.append(repo_dict['name'])
stars.append(repo_dict['stargazers_count'])
# Make visualization.
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
❹ fig = px.bar(x=repo_names, y=stars, title=title,
labels=labels,
hover_name=hover_texts)
fig.update_layout(title_font_size=28,
xaxis_title_font_size=20,
yaxis_title_font_size=20)
fig.show()
Figura 17-3: Al pasar el cursor sobre una barra se muestra el propietario y la descripción del
proyecto.
--snip--
# Process repository information.
repo_dicts = response_dict['items']
❶ repo_links, stars, hover_texts = [], [], []
for repo_dict in repo_dicts:
# Turn repo names into active links.
repo_name = repo_dict['name']
❷ repo_url = repo_dict['html_url']
❸ repo_link = f"<a href='{repo_url}'>{repo_name}</a>"
repo_links.append(repo_link)
stars.append(repo_dict['stargazers_count'])
--snip--
# Make visualization.
title = "Most-Starred Python Projects on GitHub"
labels = {'x': 'Repository', 'y': 'Stars'}
fig = px.bar(x=repo_links, y=stars, title=title,
labels=labels,
hover_name=hover_texts)
fig.update_layout(title_font_size=28,
xaxis_title_font_size=20,
yaxis_title_font_size=20)
fig.show()
--snip--
fig.update_layout(title_font_size=28,
xaxis_title_font_size=20,
yaxis_title_font_size=20)
fig.update_traces(marker_color='SteelBlue',
marker_opacity=0.6)
fig.show()
https://hacker-news.firebaseio.com/v0/item/31353677.json
import requests
import json
{
"by": "sohkamyung",
❶ "descendants": 302,
"id": 31353677,
❷ "kids": [
31354987,
31354235,
--snip--
],
"score": 785,
"time": 1652361401,
❸ "title": "Astronomers reveal first image of the black
hole
at the heart of our galaxy",
"type": "story",
❹ "url": "https://public.nrao.edu/news/.../"
}
https://hacker-news.firebaseio.com/v0/topstories.json
import requests
❼ submission_dicts = sorted(submission_dicts,
key=itemgetter('comments'),
reverse=True)
Nota
17-1. Otros idiomas: modifique la llamada API en python_repos.py para que genere un
gráfico que muestre los proyectos más populares en otros idiomas. Pruebe lenguajes
como JavaScript, Ruby, C, Java, Perl, Haskell y Go.
17-2. Discusiones activas: utilizando los datos de hn_submissions.py, cree un gráfico
de barras que muestre las discusiones más activas que tienen lugar actualmente en
Hacker News. La altura de cada barra debe corresponder a la cantidad de comentarios
que tiene cada envío. La etiqueta de cada barra debe incluir el título del envío y actuar
como un enlace a la página de discusión de ese envío. Si obtiene un KeyError al crear
un gráfico, use un bloque try-except para omitir las publicaciones promocionales.
17-3. Prueba de python_repos.py: en python_repos.py, imprimimos el valor de
status_code para asegurarnos de que la llamada a la API fuera exitosa. Escriba un
programa llamado test_python_repos.py que use pytest para afirmar que el valor de
status_code es 200. Descubra algunas otras afirmaciones que pueda hacer: por
ejemplo, que la cantidad de elementos devueltos es la esperada y que el número total
de repositorios es mayor que una cierta cantidad.
17-4. Exploración adicional: visite la documentación de Plotly y la API de GitHub o la
API de Hacker News. Utilice parte de la información que encuentre allí para
personalizar el estilo de los gráficos que ya hemos creado o extraer información
diferente y crear sus propias visualizaciones. Si tiene curiosidad por explorar otras API,
eche un vistazo a las API mencionadas en el repositorio de GitHub en
https://github.com/public-apis.
Resumen
En este capítulo, aprendió a usar API para escribir programas
autónomos que recopilan automáticamente los datos que necesitan
y los usan para crear una visualización. Utilizó la API de GitHub para
explorar los proyectos de Python más destacados en GitHub y
también analizó brevemente la API de Hacker News. Aprendiste
cómo usar el paquete Requests para emitir automáticamente una
llamada API y cómo procesar los resultados de esa llamada. También
introdujimos algunas configuraciones de Plotly que personalizan aún
más la apariencia de los gráficos que genera.
En el siguiente capítulo, utilizará Django para crear una aplicación
web como su proyecto final.
18
Comenzando con Django
Configurar un proyecto
Al comenzar a trabajar en algo tan importante como una aplicación
web, primero debe describir los objetivos del proyecto en una
especificación. Una vez que tenga un conjunto claro de objetivos,
puede comenzar a identificar tareas manejables para lograr esos
objetivos.
En esta sección, escribiremos una especificación para el Registro de
aprendizaje y comenzaremos a trabajar en la primera fase del
proyecto. Esto implicará configurar un entorno virtual y desarrollar
los aspectos iniciales de un proyecto Django.
Nota
(ll_env)learning_log$ deactivate
learning_log$
Instalando Django
Con el entorno virtual activado, ingresa lo siguiente para actualizar
pip e instalar Django:
Nota
Django lanza una nueva versión cada ocho meses, por lo que
es posible que veas una versión más nueva cuando instales
Django. Lo más probable es que este proyecto funcione tal
como está escrito aquí, incluso en versiones más nuevas de
Django. Si desea asegurarse de utilizar la misma versión de
Django que ve aquí, utilice el comando pip install
django==4.1.*. Esto instalará la última versión de Django 4.1.
Si tiene algún problema relacionado con la versión que está
utilizando, consulte los recursos en línea de este libro en
https://ehmatthes.github.io/pcc_3e.
Nota
Cada vez que modificamos una base de datos, decimos que estamos
migrando la base de datos. Emitir el comando migrate por primera
vez le dice a Django que se asegure de que la base de datos
coincida con el estado actual del proyecto. La primera vez que
ejecutamos este comando en un nuevo proyecto usando SQLite
(más sobre SQLite en un momento), Django creará una nueva base
de datos para nosotros. Aquí, Django informa que preparará la base
de datos para almacenar la información que necesita para manejar
las tareas administrativas y de autenticación ❶.
La ejecución del comando ls muestra que Django creó otro archivo
llamado db.sqlite3 ❷. SQLite es una base de datos que se ejecuta en
un solo archivo; Es ideal para escribir aplicaciones sencillas porque
no tendrás que prestar mucha atención a la gestión de la base de
datos.
Nota
Ver el proyecto
Asegurémonos de que Django haya configurado el proyecto
correctamente. Ingrese el comando runserver para ver el proyecto
en su estado actual:
Nota
18-1. Nuevos proyectos: para tener una mejor idea de lo que hace Django, cree un par
de proyectos vacíos y observe lo que crea Django. Cree una nueva carpeta con un
nombre simple, como tik_gram o insta_tok (fuera de su directorio learning_log),
navegue hasta esa carpeta en una terminal y cree un entorno virtual. Instale Django y
ejecute el comando django-admin.py startproject tg_project . (asegurándose de
incluir el punto al final del comando).
Mire los archivos y carpetas que crea este comando y compárelos con el Registro de
aprendizaje. Haga esto varias veces, hasta que esté familiarizado con lo que crea
Django al iniciar un nuevo proyecto. Luego elimine los directorios del proyecto si lo
desea.
Definición de modelos
Pensemos en nuestros datos por un momento. Cada usuario deberá
crear una serie de temas en su registro de aprendizaje. Cada
entrada que realicen estará vinculada a un tema y estas entradas se
mostrarán como texto. También necesitaremos almacenar la marca
de tiempo de cada entrada para poder mostrar a los usuarios cuándo
hicieron cada una.
Abra el archivo models.py y observe su contenido existente:
modelos.py
class Topic(models.Model):
"""A topic the user is learning about."""
❶ text = models.CharField(max_length=200)
❷ date_added = models.DateTimeField(auto_now_add=True)
❸ def __str__(self):
"""Return a string representation of the model."""
return self.text
Hemos creado una clase llamada Topic, que hereda de Model, una
clase principal incluida en Django que define la funcionalidad básica
de un modelo. Agregamos dos atributos a la clase Topic: text y
date_added.
--snip--
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
--snip--
--snip--
INSTALLED_APPS = [
# My apps.
'learning_logs',
Configurar un superusuario
Django te permite crear un superusuario, un usuario que tiene todos
los privilegios disponibles en el sitio. Los privilegios de un usuario
controlan las acciones que puede realizar. La configuración de
privilegios más restrictiva permite al usuario leer únicamente
información pública en el sitio. Los usuarios registrados suelen tener
el privilegio de leer sus propios datos privados y cierta información
seleccionada que está disponible sólo para los miembros. Para
administrar eficazmente un proyecto, el propietario del sitio
normalmente necesita acceso a toda la información almacenada en
el sitio. Un buen administrador tiene cuidado con la información
confidencial de sus usuarios, porque los usuarios confían mucho en
las aplicaciones a las que acceden.
Para crear un superusuario en Django, ingrese el siguiente comando
y responda a las indicaciones:
(ll_env)learning_log$ python manage.py createsuperuser
❶ Username (leave blank to use 'eric'): ll_admin
❷ Email address:
❸ Password:
Password (again):
Superuser created successfully.
(ll_env)learning_log$
Nota
admin.site.register(Topic)
Nota
Agregar temas
Ahora que Topic se ha registrado en el sitio de administración,
agreguemos nuestro primer tema. Haga clic en Temas para ir a la
página Temas, que está prácticamente vacía porque todavía no
tenemos temas que administrar. Haga clic en Agregar tema y
aparecerá un formulario para agregar un nuevo tema. Ingrese Chess
en el primer cuadro y haga clic en Guardar. Volverás a la página de
administración de Temas y verás el tema que acabas de crear.
Creemos un segundo tema para tener más datos con los que
trabajar. Haga clic en Agregar tema nuevamente e ingrese Rock
Climbing. Haga clic en Guardar y volverá a la página principal de
Temas. Ahora verás Ajedrez y Escalada en roca en la lista.
class Topic(models.Model):
--snip--
❶ class Entry(models.Model):
"""Something specific learned about a topic."""
❷ topic = models.ForeignKey(Topic,
on_delete=models.CASCADE)
❸ text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
❹ class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a simple string representing the entry."""
❺ return f"{self.text[:50]}..."
La clase Entry hereda de la clase base Model de Django, tal como lo
hizo Topic ❶. El primer atributo, topic, es una instancia de
ForeignKey ❷. Una clave externa es un término de base de datos; es
una referencia a otro registro en la base de datos. Este es el código
que conecta cada entrada con un tema específico. A cada tema se le
asigna una clave o ID cuando se crea. Cuando Django necesita
establecer una conexión entre dos datos, utiliza las claves asociadas
con cada dato. Usaremos estas conexiones en breve para recuperar
todas las entradas asociadas con un tema determinado. El
argumento on_delete=models.CASCADE le dice a Django que cuando
se elimina un tema, todas las entradas asociadas con ese tema
también deben eliminarse. Esto se conoce como eliminación en
cascada.
El siguiente es un atributo llamado text, que es una instancia de
TextField ❸. Este tipo de campo no necesita un límite de tamaño
porque no queremos limitar el tamaño de las entradas individuales.
El atributo date_added nos permite presentar las entradas en el
orden en que fueron creadas y colocar una marca de tiempo al lado
de cada entrada.
La clase Meta está anidada dentro de la clase Entry ❹. La clase Meta
contiene información adicional para gestionar un modelo; aquí, nos
permite establecer un atributo especial que le indica a Django que
use Entries cuando necesite hacer referencia a más de una entrada.
Sin esto, Django se referiría a varias entradas como Entrys.
El método __str__() le dice a Django qué información mostrar
cuando se refiere a entradas individuales. Debido a que una entrada
puede ser un cuerpo de texto largo, __str__() devuelve solo los
primeros 50 caracteres de text ❺. También agregamos puntos
suspensivos para aclarar que no siempre mostramos la entrada
completa.
Migrar el modelo de entrada
Debido a que agregamos un nuevo modelo, necesitamos migrar la
base de datos nuevamente. Este proceso le resultará bastante
familiar: modifica models.py, ejecuta el comando python manage.py
makemigrations app_name y luego ejecuta el comando python
manage.py migrate.
Estas tres entradas nos darán algo con qué trabajar mientras
continuamos desarrollando el Registro de aprendizaje.
El caparazón de Django
Ahora que hemos ingresado algunos datos, podemos examinarlos
mediante programación a través de una sesión de terminal
interactiva. Este entorno interactivo se llama Django Shell y es un
excelente entorno para probar y solucionar problemas de su
proyecto. A continuación se muestra un ejemplo de una sesión de
shell interactiva:
(ll_env)learning_log$ python manage.py shell
❶ >>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2022, 5, 20, 3, 33, 36, 928759,
tzinfo=datetime.timezone.utc)
❶ >>> t.entry_set.all()
<QuerySet [<Entry: The opening is the first part of the game,
roughly...>, <Entry:
In the opening phase of the game, it's important t...>]>
❷ urlpatterns = [
❸ path('admin/', admin.site.urls),
]
Las dos primeras líneas importan el módulo admin y una función para
crear rutas URL ❶. El cuerpo del archivo define la variable
urlpatterns ❷. En este archivo urls.py, que define las URL para el
proyecto en su conjunto, la variable urlpatterns incluye conjuntos
de URL de las aplicaciones del proyecto. La lista incluye el módulo
admin.site.urls, que define todas las URL que se pueden solicitar
desde el sitio de administración ❸.
Necesitamos incluir las URL de learning_logs, así que agregue lo
siguiente:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('learning_logs.urls')),
]
❹ app_name = 'learning_logs'
❺ urlpatterns = [
# Home page
❻ path('', views.index, name='index'),
]
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
<p>Learning Log</p>
18-5. Planificador de comidas: considere una aplicación que ayude a las personas a
planificar sus comidas durante la semana. Cree una nueva carpeta llamada
food_planner e inicie un nuevo proyecto Django dentro de esta carpeta. Luego crea
una nueva aplicación llamada meal_plans. Haga una página de inicio sencilla para este
proyecto.
18-6. Página de inicio de Pizzeria: agregue una página de inicio al proyecto de Pizzeria
que inició en el Ejercicio 18-4 (página 388).
La plantilla principal
Crearemos una plantilla llamada base.html en el mismo directorio
que index.html. Este archivo contendrá elementos comunes a todas
las páginas; todas las demás plantillas heredarán de base.html. El
único elemento que queremos repetir en cada página ahora es el
título en la parte superior. Debido a que incluiremos esta plantilla en
cada página, hagamos del título un enlace a la página de inicio:
base.html
<p>
❶ <a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>
Nota
❶ {% extends 'learning_logs/base.html' %}
❷ {% block content %}
<p>Learning Log helps you keep track of your learning, for
any topic you're
interested in.</p>
❸ {% endblock content %}
La página de temas
Ahora que tenemos un enfoque eficiente para crear páginas,
podemos centrarnos en las dos páginas siguientes: la página de
temas generales y la página para mostrar entradas para un solo
tema. La página de temas mostrará todos los temas que los usuarios
han creado y es la primera página que implicará trabajar con datos.
La vista de temas
La función topics() necesita recuperar algunos datos de la base de
datos y enviarlos a la plantilla. Agregue lo siguiente a views.py:
vistas.py
def index(request):
--snip--
❷ def topics(request):
"""Show all topics."""
❸ topics = Topic.objects.order_by('date_added')
❹ context = {'topics': topics}
❺ return render(request, 'learning_logs/topics.html',
context)
La plantilla de temas
La plantilla para la página de temas recibe el diccionario context, por
lo que la plantilla puede usar los datos que proporciona topics().
Cree un archivo llamado topic.html en el mismo directorio que
index.html. Así es como podemos mostrar los temas en la plantilla:
temas.html
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topics</p>
❶ <ul>
❷ {% for topic in topics %}
❸ <li>{{ topic.text }}</li>
❹ {% empty %}
<li>No topics have been added yet.</li>
❺ {% endfor %}
❻ </ul>
{% endblock content %}
--snip--
urlpatterns = [
--snip--
# Detail page for a single topic.
path('topics/<int:topic_id>/', views.topic,
name='topic'),
]
--snip--
❶ def topic(request, topic_id):
"""Show a single topic and all its entries."""
❷ topic = Topic.objects.get(id=topic_id)
❸ entries = topic.entry_set.order_by('-date_added')
❹ context = {'topic': topic, 'entries': entries}
❺ return render(request, 'learning_logs/topic.html',
context)
La plantilla de tema
La plantilla debe mostrar el nombre del tema y las entradas.
También debemos informar al usuario si aún no se han realizado
entradas para este tema.
tema.html
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Entries:</p>
❷ <ul>
❸ {% for entry in entries %}
<li>
❹ <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
❺ <p>{{ entry.text|linebreaks }}</p>
</li>
❻ {% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
Ampliamos base.html, como haremos con todas las páginas del
proyecto. A continuación mostramos el atributo text del tema que se
ha solicitado ❶. La variable topic está disponible porque está
incluida en el diccionario context. Luego comenzamos una lista con
viñetas ❷ para mostrar cada una de las entradas y las recorremos
❸, como hicimos con los temas anteriormente.
Cada viñeta enumera dos datos: la marca de tiempo y el texto
completo de cada entrada. Para la marca de tiempo ❹, mostramos el
valor del atributo date_added. En las plantillas de Django, una línea
vertical (|) representa un filtro de plantilla, una función que modifica
el valor de una variable de plantilla durante el proceso de
renderizado. El filtro date:'M d, Y H:i' muestra marcas de tiempo
en el formato 1 de enero de 2022 23:00. La siguiente línea muestra
el valor del atributo text de la entrada actual. El filtro linebreaks ❺
garantiza que las entradas de texto largas incluyan saltos de línea en
un formato comprensible para los navegadores, en lugar de mostrar
un bloque de texto ininterrumpido. Usamos nuevamente la etiqueta
de plantilla {% empty %} ❻ para imprimir un mensaje informando al
usuario que no se han realizado entradas.
--snip--
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">
{{ topic.text }}</a></li>
</li>
{% empty %}
--snip--
Usamos la etiqueta de plantilla de URL para generar el enlace
adecuado, según el patrón de URL en learning_logs con el nombre
'topic'. Este patrón de URL requiere un argumento topic_id, por lo
que agregamos el atributo topic.id a la etiqueta de la plantilla de
URL. Ahora cada tema en la lista de temas es un enlace a una
página de tema, como http://localhost:8000/topics/1/.
Cuando actualiza la página de temas y hace clic en un tema, debería
ver una página similar a la Figura 18-5.
Nota
Resumen
En este capítulo, aprendió cómo comenzar a crear una aplicación
web simple utilizando el marco Django. Viste una breve
especificación del proyecto, instalaste Django en un entorno virtual,
configuraste un proyecto y verificaste que el proyecto estaba
configurado correctamente. Usted configura una aplicación y define
modelos para representar los datos de su aplicación. Aprendiste
sobre las bases de datos y cómo Django te ayuda a migrar tu base
de datos después de realizar un cambio en tus modelos. Creó un
superusuario para el sitio de administración y utilizó el sitio de
administración para ingresar algunos datos iniciales.
También exploraste el shell de Django, que te permite trabajar con
los datos de tu proyecto en una sesión de terminal. Aprendió a
definir URL, crear funciones de visualización y escribir plantillas para
crear páginas para su sitio. También utilizó la herencia de plantillas
para simplificar la estructura de las plantillas individuales y facilitar la
modificación del sitio a medida que evoluciona el proyecto.
En el Capítulo 19, creará páginas intuitivas y fáciles de usar que
permitirán a los usuarios agregar nuevos temas y entradas y editar
las entradas existentes sin tener que pasar por el sitio de
administración. También agregará un sistema de registro de
usuarios, que les permitirá crear una cuenta y crear su propio
registro de aprendizaje. Este es el corazón de una aplicación web: la
capacidad de crear algo con lo que cualquier número de usuarios
pueda interactuar.
19
cuentas de usuario
❶ class TopicForm(forms.ModelForm):
class Meta:
❷ model = Topic
❸ fields = ['text']
❹ labels = {'text': ''}
La URL nuevo_tema
La URL de una página nueva debe ser breve y descriptiva. Cuando el
usuario quiera agregar un tema nuevo, lo enviaremos a
http://localhost:8000/new_topic/. Este es el patrón de URL para la
página new_topic; agregue esto a learning_logs/urls.py:
logs_aprendizaje/urls.py
--snip--
urlpatterns = [
--snip--
# Page for adding a new topic.
path('new_topic/', views.new_topic, name='new_topic'),
]
vistas.py
--snip--
def new_topic(request):
"""Add a new topic."""
❶ if request.method != 'POST':
# No data submitted; create a blank form.
❷ form = TopicForm()
else:
# POST data submitted; process data.
❸ form = TopicForm(data=request.POST)
❹ if form.is_valid():
❺ form.save()
❻ return redirect('learning_logs:topics')
La plantilla new_topic
Ahora crearemos una nueva plantilla llamada new_topic.html para
mostrar el formulario que acabamos de crear:
nuevo_tema.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Add a new topic:</p>
{% endblock content %}
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
--snip--
</ul>
{% endblock content %}
class TopicForm(forms.ModelForm):
--snip--
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
❶ labels = {'text': ''}
❷ widgets = {'text': forms.Textarea(attrs={'cols':
80})}
La URL nueva_entrada
Las nuevas entradas deben estar asociadas con un tema en
particular, por lo que debemos incluir un argumento topic_id en la
URL para agregar una nueva entrada. Aquí está la URL, que agregas
a learning_logs/urls.py:
logs_aprendizaje/urls.py
--snip--
urlpatterns = [
--snip--
# Page for adding a new entry.
path('new_entry/<int:topic_id>/', views.new_entry,
name='new_entry'),
]
Este patrón de URL coincide con cualquier URL con el formato
http://localhost:8000/new_entry/id/, donde id es un número que
coincide con el ID del tema. El código <int:topic_id> captura un
valor numérico y lo asigna a la variable topic_id. Cuando se solicita
una URL que coincide con este patrón, Django envía la solicitud y el
ID del tema a la función de vista new_entry().
--snip--
def new_entry(request, topic_id):
"""Add a new entry for a particular topic."""
❶ topic = Topic.objects.get(id=topic_id)
❷ if request.method != 'POST':
# No data submitted; create a blank form.
❸ form = EntryForm()
else:
# POST data submitted; process data.
❹ form = EntryForm(data=request.POST)
if form.is_valid():
❺ new_entry = form.save(commit=False)
❻ new_entry.topic = topic
new_entry.save()
❼ return redirect('learning_logs:topic',
topic_id=topic_id)
La plantilla new_entry
Como puede ver en el siguiente código, la plantilla para new_entry es
similar a la plantilla para new_topic:
nueva_entrada.html
{% extends "learning_logs/base.html" %}
{% block content %}
{% endblock content %}
{% block content %}
<p>Entries:</p>
<p>
<a href="{% url 'learning_logs:new_entry' topic.id
%}">Add new entry</a>
</p>
<ul>
--snip--
</ul>
{% endblock content %}
Editar entradas
Ahora crearemos una página para que los usuarios puedan editar las
entradas que agregaron.
La URL editar_entrada
La URL de la página debe pasar el ID de la entrada que se va a
editar. Aquí está learning_logs/urls.py:
URL.py
--snip--
urlpatterns = [
--snip--
# Page for editing an entry.
path('edit_entry/<int:entry_id>/', views.edit_entry,
name='edit_entry'),
]
Este patrón de URL coincide con URL como
http://localhost:8000/edit_entry/id/. Aquí el valor de id se asigna al
parámetro entry_id. Django envía solicitudes que coinciden con este
formato a la función de vista edit_entry().
if request.method != 'POST':
# Initial request; pre-fill form with the current
entry.
❷ form = EntryForm(instance=entry)
else:
# POST data submitted; process data.
❸ form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
❹ form.save()
❺ return redirect('learning_logs:topic',
topic_id=topic.id)
La plantilla edit_entry
A continuación, creamos una plantilla edit_entry.html, que es similar
a new_entry.html:
editar_entrada.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Edit entry:</p>
{% endblock content %}
--snip--
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
<p>
<a href="{% url 'learning_logs:edit_entry' entry.id
%}">
Edit entry</a></p>
</li>
--snip--
19-1. Blog: inicie un nuevo proyecto de Django llamado Blog. Cree una aplicación
llamada blogs, con un modelo que represente un blog general y un modelo que
represente una publicación de blog individual. Proporcione a cada modelo un conjunto
apropiado de campos. Cree un superusuario para el proyecto y utilice el sitio de
administración para crear un blog y un par de publicaciones breves. Cree una página
de inicio que muestre todas las publicaciones en el orden apropiado.
Cree páginas para crear un blog, crear publicaciones nuevas y editar publicaciones
existentes. Utilice sus páginas para asegurarse de que funcionen.
La aplicación de cuentas
Comenzaremos creando una nueva aplicación llamada accounts,
usando el comando startapp:
--snip--
INSTALLED_APPS = [
# My apps
'learning_logs',
'accounts',
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('', include('learning_logs.urls')),
]
Agregamos una línea para incluir el archivo urls.py de accounts. Esta
línea coincidirá con cualquier URL que comience con la palabra
cuentas, como http://localhost:8000/accounts/login/.
app_name = 'accounts'
urlpatterns = [
# Include default auth urls.
path('', include('django.contrib.auth.urls')),
]
{% extends 'learning_logs/base.html' %}
{% block content %}
❶ {% if form.errors %}
<p>Your username and password didn't match. Please try
again.</p>
{% endif %}
{% endblock content %}
La configuración LOGIN_REDIRECT_URL
Una vez que un usuario inicia sesión exitosamente, Django necesita
saber dónde enviar a ese usuario. Controlamos esto en el archivo de
configuración.
Agregue el siguiente código al final de settings.py:
configuración.py
--snip--
# My settings.
LOGIN_REDIRECT_URL = 'learning_logs:index'
Cerrar sesión
Ahora debemos proporcionar una forma para que los usuarios
cierren sesión. Las solicitudes de cierre de sesión deben enviarse
como solicitudes POST, por lo que agregaremos un pequeño
formulario de cierre de sesión a base.html. Cuando los usuarios
hacen clic en el botón de cerrar sesión, irán a una página que
confirma que han cerrado sesión.
--snip--
{% block content %}{% endblock content %}
{% if user.is_authenticated %}
❶ <hr />
❷ <form action="{% url 'accounts:logout' %}" method='post'>
{% csrf_token %}
<button name='submit'>Log out</button>
</form>
{% endif %}
La configuración de LOGOUT_REDIRECT_URL
Cuando el usuario hace clic en el botón de cerrar sesión, Django
necesita saber dónde enviarlos. Controlamos este comportamiento
en settings.py:
configuración.py
--snip--
# My settings.
LOGIN_REDIRECT_URL = 'learning_logs:index'
LOGOUT_REDIRECT_URL = 'learning_logs:index'
La página de registro
A continuación, crearemos una página para que los nuevos usuarios
puedan registrarse. Usaremos el UserCreationForm predeterminado
de Django, pero escribiremos nuestra propia función de vista y
plantilla.
La URL de registro
El siguiente código proporciona el patrón de URL para la página de
registro, que debe colocarse en cuentas/urls.py:
cuentas/urls.py
app_name = accounts
urlpatterns = [
# Include default auth urls.
path('', include('django.contrib.auth.urls')),
# Registration page.
path('register/', views.register, name='register'),
]
def register(request):
"""Register a new user."""
if request.method != 'POST':
# Display blank registration form.
❶ form = UserCreationForm()
else:
# Process completed form.
❷ form = UserCreationForm(data=request.POST)
❸ if form.is_valid():
❹ new_user = form.save()
# Log the user in and then redirect to home page.
❺ login(request, new_user)
❻ return redirect('learning_logs:index')
La plantilla de registro
Ahora cree una plantilla para la página de registro, que será similar a
la página de inicio de sesión. Asegúrese de guardarlo en el mismo
directorio que login.html:
registrarse.html
{% extends "learning_logs/base.html" %}
{% block content %}
{% endblock content %}
--snip--
{% if user.is_authenticated %}
Hello, {{ user.username }}.
{% else %}
<a href="{% url 'accounts:register' %}">Register</a> -
<a href="{% url 'accounts:login' %}">Log in</a>
{% endif %}
--snip--
@login_required
def topics(request):
"""Show all topics."""
--snip--
--snip--
# My settings.
LOGIN_REDIRECT_URL = 'learning_logs:index'
LOGOUT_REDIRECT_URL = 'learning_logs:index'
LOGIN_URL = 'accounts:login'
--snip--
@login_required
def topics(request):
--snip--
@login_required
def topic(request, topic_id):
--snip--
@login_required
def new_topic(request):
--snip--
@login_required
def new_entry(request, topic_id):
--snip--
@login_required
def edit_entry(request, entry_id):
--snip--
class Topic(models.Model):
"""A topic the user is learning about."""
Text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""Return a string representing the topic."""
Return self.text
class Entry(models.Model):
--snip--
Nota
--snip--
@login_required
def topics(request):
"""Show all topics."""
topics =
Topic.objects.filter(owner=request.user).order_by('date_added
')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html',
context)
--snip--
--snip--
@login_required
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = Topic.objects.get(id=topic_id)
# Make sure the topic belongs to the current user.
❷ if topic.owner != request.user:
raise Http404
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html',
context)
--snip--
--snip--
@login_required
def edit_entry(request, entry_id):
"""Edit an existing entry."""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if topic.owner != request.user:
raise Http404
if request.method != 'POST':
--snip--
19-3. Refactorización: hay dos lugares en views.py donde nos aseguramos de que el
usuario asociado con un tema coincida con el usuario actualmente conectado. Coloque
el código para esta verificación en una función llamada check_topic_owner() y llame a
esta función cuando corresponda.
19-4. Protección de new_entry: actualmente, un usuario puede agregar una nueva
entrada al registro de aprendizaje de otro usuario ingresando una URL con el ID de un
tema que pertenece a otro usuario. Evite este ataque verificando que el usuario actual
sea el propietario del tema de la entrada antes de guardar la nueva entrada.
19-5. Blog protegido: en su proyecto de blog, asegúrese de que cada publicación de
blog esté conectada a un usuario en particular. Asegúrese de que todas las
publicaciones sean de acceso público, pero que solo los usuarios registrados puedan
agregar publicaciones y editar publicaciones existentes. En la vista que permite a los
usuarios editar sus publicaciones, asegúrese de que el usuario esté editando su propia
publicación antes de procesar el formulario.
Resumen
En este capítulo, aprendió cómo los formularios permiten a los
usuarios agregar nuevos temas y entradas, y editar entradas
existentes. Luego aprendió cómo implementar cuentas de usuario.
Les diste a los usuarios existentes la posibilidad de iniciar y cerrar
sesión, y usaste el UserCreationForm predeterminado de Django para
permitir que las personas crearan nuevas cuentas.
Después de crear un sistema simple de registro y autenticación de
usuarios, restringió el acceso a los usuarios que iniciaron sesión en
ciertas páginas utilizando el decorador @login_required. Luego
asignó datos a usuarios específicos a través de una relación de clave
externa. También aprendió a migrar la base de datos cuando la
migración requiere que especifique algunos datos predeterminados.
Finalmente, aprendió cómo asegurarse de que un usuario solo pueda
ver los datos que le pertenecen modificando las funciones de
visualización. Recuperaste los datos apropiados usando el método
filter() y comparaste al propietario de los datos solicitados con el
usuario actualmente conectado.
Puede que no siempre sea inmediatamente obvio qué datos debe
poner a disposición y qué datos debe proteger, pero esta habilidad
se adquirirá con la práctica. Las decisiones que hemos tomado en
este capítulo para proteger los datos de nuestros usuarios también
ilustran por qué trabajar con otros es una buena idea al construir un
proyecto: hacer que alguien más revise su proyecto hace que sea
más probable que detecte áreas vulnerables.
Ahora tiene un proyecto en pleno funcionamiento ejecutándose en
su máquina local. En el capítulo final, diseñará el Registro de
aprendizaje para que sea visualmente atractivo e implementará el
proyecto en un servidor para que cualquier persona con acceso a
Internet pueda registrarse y crear una cuenta.
20
Diseñar e implementar una
aplicación
La aplicación django-bootstrap5
Usaremos django-bootstrap5 para integrar Bootstrap en nuestro
proyecto. Esta aplicación descarga los archivos Bootstrap necesarios,
los coloca en una ubicación adecuada de su proyecto y hace que las
directivas de estilo estén disponibles en las plantillas de su proyecto.
Para instalar django-bootstrap5, ejecute el siguiente comando en un
entorno virtual activo:
(ll_env)learning_log$ pip install django-bootstrap5
--snip--
Successfully installed beautifulsoup4-4.11.1 django-
bootstrap5-21.3
soupsieve-2.3.2.post1
configuración.py
--snip--
INSTALLED_APPS = [
# My apps.
'learning_logs',
'accounts',
Modificando base.html
Necesitamos reescribir base.html usando la plantilla Bootstrap.
Desarrollaremos el nuevo base.html en secciones. Este es un archivo
grande; es posible que desee copiar este archivo de los recursos en
línea, disponibles en https://ehmatthes.github.io/pcc_3e. Si copia el
archivo, aún debe leer la siguiente sección para comprender los
cambios que se realizaron.
❶ <!doctype html>
❷ <html lang="en">
❸ <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-
scale=1">
❹ <title>Learning Log</title>
❺ {% load django_bootstrap5 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
</head>
--snip--
</head>
<body>
</body>
</html>
--snip--
</ul> <!-- End of links on left side of navbar -->
❷ {% if user.is_authenticated %}
<li class="nav-item">
❸ <span class="navbar-text me-2">Hello, {{
user.username }}.
</span></li>
❹ {% else %}
<li class="nav-item">
<a class="nav-link" href="{% url
'accounts:register' %}">
Register</a></li>
<li class="nav-item">
<a class="nav-link" href="{% url
'accounts:login' %}">
Log in</a></li>
{% endif %}
</ul> <!-- End of account-related links -->
--snip--
</ul> <!-- End of account-related links -->
{% if user.is_authenticated %}
<form action="{% url 'accounts:logout' %}"
method='post'>
{% csrf_token %}
❶ <button name='submit' class='btn btn-outline-
secondary btn-sm'>
Log out</button>
</form>
{% endif %}
--snip--
</nav> <!-- End of navbar -->
❶ <main class="container">
❷ <div class="pb-2 mb-2 border-bottom">
{% block page_header %}{% endblock page_header %}
</div>
❸ <div>
{% block content %}{% endblock content %}
</div>
</main>
</body>
</html>
{% extends "learning_logs/base.html" %}
❶ {% block page_header %}
❷ <div class="p-3 mb-4 bg-light border rounded-3">
<div class="container-fluid py-4">
❸ <h1 class="display-3">Track your learning.</h1>
{% extends 'learning_logs/base.html' %}
❶ {% load django_bootstrap5 %}
❷ {% block page_header %}
<h2>Log in to your account.</h2>
{% endblock page_header %}
{% block content %}
{% endblock content %}
{% extends 'learning_logs/base.html' %}
{% block page_header %}
❶ <h1>Topics</h1>
{% endblock page_header %}
{% block content %}
{% endblock content %}
{% extends 'learning_logs/base.html' %}
❶ {% block page_header %}
<h1>{{ topic.text }}</h1>
{% endblock page_header %}
{% block content %}
<p>
<a href="{% url 'learning_logs:new_entry' topic.id
%}">Add new entry</a>
</p>
{% endblock content %}
Primero colocamos el tema en el bloque page_header ❶. Luego
eliminamos la estructura de lista desordenada utilizada
anteriormente en esta plantilla. En lugar de convertir cada entrada
en un elemento de la lista, abrimos un elemento div con el selector
card ❷. Esta tarjeta tiene dos elementos anidados: uno para
contener la marca de tiempo y el enlace para editar la entrada, y
otro para contener el cuerpo de la entrada. El selector card se
encarga de la mayor parte del estilo que necesitamos para este div;
Personalizamos la tarjeta agregando un pequeño margen en la parte
inferior de cada tarjeta (mb-3).
El primer elemento de la tarjeta es un encabezado, que es un
elemento <h4> con el selector card-header ❸. Este encabezado
contiene la fecha en que se realizó la entrada y un enlace para editar
la entrada. La etiqueta <small> alrededor del enlace edit_entry hace
que parezca un poco más pequeño que la marca de tiempo ❹. El
segundo elemento es un div con el selector card-body ❺, que coloca
el texto de la entrada en un cuadro simple en la tarjeta. Observe
que el código de Django para incluir la información en la página no
ha cambiado; sólo tienen elementos que afectan la apariencia de la
página. Como ya no tenemos una lista desordenada, reemplazamos
las etiquetas de elementos de la lista alrededor del mensaje de la
lista vacía con etiquetas de párrafo simples ❻.
La Figura 20-3 muestra la página del tema con su nueva apariencia.
La funcionalidad de Learning Log no ha cambiado, pero parece
mucho más profesional y atractiva para los usuarios.
Si desea utilizar una plantilla Bootstrap diferente para un proyecto,
siga un proceso similar al que hemos hecho hasta ahora en este
capítulo. Copie la plantilla que desea utilizar en base.html y
modifique los elementos que contienen contenido real para que la
plantilla muestre la información de su proyecto. Luego use las
herramientas de estilo individuales de Bootstrap para diseñar el
contenido de cada página.
Nota
20-1. Otros formularios: aplicamos los estilos de Bootstrap a la página login. Realice
cambios similares en el resto de las páginas basadas en formularios, incluidas
new_topic, new_entry, edit_entry y register.
20-2. Blog elegante: use Bootstrap para diseñar el proyecto de blog que creó en el
Capítulo 19.
Implementación del registro de aprendizaje
Ahora que tenemos un proyecto de aspecto profesional,
implementémoslo en un servidor activo para que cualquiera con
conexión a Internet pueda usarlo. Usaremos Platform.sh, una
plataforma basada en web que le permite gestionar la
implementación de aplicaciones web. Pondremos Learning Log en
funcionamiento en Platform.sh.
Nota
Nota
Instalación de plataformashconfig
También necesitarás instalar un paquete adicional,
platformshconfig. Este paquete ayuda a detectar si el proyecto se
está ejecutando en su sistema local o en un servidor Platform.sh. En
un entorno virtual activo, emita el siguiente comando:
asgiref==3.5.2
beautifulsoup4==4.11.1
Django==4.1
django-bootstrap5==21.3
platformshconfig==2.4.0
soupsieve==2.3.2.post1
sqlparse==0.4.2
❶ name: "ll_project"
type: "python:3.10"
❷ relationships:
database: "db:postgresql"
--snip--
disk: 512
# Set a local read/write mount for logs.
❶ mounts:
"logs":
source: local
source_path: logs
mkdir logs
❹ python manage.py collectstatic
rm -rf logs
❺ deploy: |
python manage.py migrate
"https://{default}/":
type: upstream
upstream: "ll_project:http"
"https://www.{default}/":
type: redirect
to: "https://{default}/"
db:
type: postgresql:12
disk: 1024
Este archivo define un servicio, una base de datos Postgres.
--snip--
# Platform.sh settings.
❶ from platformshconfig import Config
config = Config()
❷ if config.is_valid_platform():
❸ ALLOWED_HOSTS.append('.platformsh.site')
❹ if config.appDir:
STATIC_ROOT = Path(config.appDir) / 'static'
❺ if config.projectEntropy:
SECRET_KEY = config.projectEntropy
if not config.in_build():
❻ db_settings = config.credentials('database')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': db_settings['path'],
'USER': db_settings['username'],
'PASSWORD': db_settings['password'],
'HOST': db_settings['host'],
'PORT': db_settings['port'],
},
}
Configurando Git
Git realiza un seguimiento de quién realiza cambios en un proyecto,
incluso cuando solo hay una persona trabajando en el proyecto. Para
hacer esto, Git necesita saber su nombre de usuario y correo
electrónico. Debes proporcionar un nombre de usuario, pero puedes
crear un correo electrónico para tus proyectos de práctica:
Ignorar archivos
No necesitamos que Git rastree todos los archivos del proyecto, por
lo que le diremos que ignore algunos archivos. Cree un archivo
llamado .gitignore en la carpeta que contiene Manage.py. Observe
que este nombre de archivo comienza con un punto y no tiene
extensión de archivo. Aquí está el código que va en .gitignore:
.gitignore
ll_env/
__pycache__/
*.sqlite3
Le decimos a Git que ignore todo el directorio ll_env, porque
podemos recrearlo automáticamente en cualquier momento.
Tampoco realizamos un seguimiento del directorio __pycache__, que
contiene los archivos .pyc que se crean automáticamente cuando se
ejecutan los archivos .py. No realizamos un seguimiento de los
cambios en la base de datos local porque es un mal hábito: si alguna
vez usa SQLite en un servidor, podría sobrescribir accidentalmente la
base de datos activa con su base de datos de prueba local cuando
envíe el proyecto al servidor. El asterisco en *.sqlite3 le dice a Git
que ignore cualquier archivo que termine con la extensión .sqlite3.
Nota
Comprometer el proyecto
Necesitamos inicializar un repositorio Git para el Registro de
aprendizaje, agregar todos los archivos necesarios al repositorio y
confirmar el estado inicial del proyecto. Aquí se explica cómo
hacerlo:
* Region (--region)
The region where the project will be hosted
--snip--
[us-3.platform.sh] Moses Lake, United States (AZURE) [514
gC02eq/kWh]
❷ > us-3.platform.sh
* Plan (--plan)
Default: development
Enter a number to choose:
[0] development
--snip--
❸ > 0
* Environments (--environments)
The number of environments
Default: 3
❹ > 3
* Storage (--storage)
The amount of storage per environment, in GiB
Default: 5
❺ > 5
▀▄ ▄▀
█▄█▀███▀█▄█
▀█████████▀
▄▀ ▀▄
Nota
___ _ _ __ _
| _ \ |__ _| |_ / _|___ _ _ _ __ __| |_
| _/ / _` | _| _/ _ \ '_| ' \ _(_-< ' \
|_| |_\__,_|\__|_| \___/_| |_|_|_(_)__/_||_|
Welcome to Platform.sh.
❶ web@ll_project.0:~$ ls
accounts learning_logs ll_project logs manage.py
requirements.txt
requirements_remote.txt static
❷ web@ll_project.0:~$ python manage.py createsuperuser
❸ Username (leave blank to use 'web'): ll_admin_live
Email address:
Password:
Password (again):
Superuser created successfully.
❹ web@ll_project.0:~$ exit
logout
Connection to ssh.us-3.platform.sh closed.
❺ (ll_env)learning_log$
Cuando ejecuta por primera vez el comando platform
environment:ssh, es posible que reciba otro mensaje sobre la
autenticidad de este host. Si ve este mensaje, ingrese Y y debería
iniciar sesión en una sesión de terminal remota.
Después de ejecutar el comando ssh, su terminal actúa como un
terminal en el servidor remoto. Tenga en cuenta que su mensaje ha
cambiado para indicar que está en una sesión web asociada con el
proyecto llamado ll_project ❶. Si emite el comando ls, verá los
archivos que se han enviado al servidor Platform.sh.
Emita el mismo comando createsuperuser que utilizamos en el
Capítulo 18 ❷. Esta vez, ingresé un nombre de usuario de
administrador, ll_admin_live, que es distinto del que usé localmente
❸. Cuando haya terminado de trabajar en la sesión de terminal
remota, ingrese el comando exit ❹. Su mensaje le indicará que está
trabajando en su sistema local nuevamente ❺.
Ahora puede agregar /admin/ al final de la URL de la aplicación en
vivo e iniciar sesión en el sitio de administración. Si otras personas
ya han comenzado a utilizar tu proyecto, ¡ten en cuenta que tendrás
acceso a todos sus datos! Tome esta responsabilidad en serio y los
usuarios seguirán confiándole sus datos.
Nota
--snip--
if config.is_valid_platform():
ALLOWED_HOSTS.append('.platformsh.site')
DEBUG = False
--snip--
{% extends "learning_logs/base.html" %}
{% block page_header %}
<h2>The item you requested is not available. (404)</h2>
{% endblock page_header %}
Esta plantilla simple proporciona información genérica de la página
de error 404, pero tiene un estilo que coincide con el resto del sitio.
Cree otro archivo llamado 500.html usando el siguiente código:
500.html
{% extends "learning_logs/base.html" %}
{% block page_header %}
<h2>There has been an internal error. (500)</h2>
{% endblock page_header %}
--snip--
TEMPLATES = [
{
'BACKEND':
'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
--snip--
},
]
--snip--
Desarrollo continuo
Es posible que desee desarrollar aún más el Registro de aprendizaje
después de su envío inicial a un servidor en vivo, o tal vez desee
desarrollar sus propios proyectos para implementar. Al hacerlo,
existe un proceso bastante consistente para actualizar sus proyectos.
Primero, realizará los cambios necesarios en su proyecto local. Si sus
cambios dan como resultado archivos nuevos, agréguelos al
repositorio de Git usando el comando git add . (asegurándose de
incluir el punto al final del comando). Cualquier cambio que requiera
una migración de la base de datos necesitará este comando, porque
cada migración genera un nuevo archivo de migración.
En segundo lugar, confirme los cambios en su repositorio usando git
commit -am "commit message". Luego envíe sus cambios a
Platform.sh, usando el comando platform push. Visite su proyecto
en vivo y asegúrese de que los cambios que espera ver hayan
surtido efecto.
Es fácil cometer errores durante este proceso, así que no se
sorprenda si algo sale mal. Si el código no funciona, revisa lo que
has hecho e intenta detectar el error. Si no puede encontrar el error
o no sabe cómo deshacerlo, consulte las sugerencias para obtener
ayuda en el Apéndice C. No dude en pedir ayuda: todos los demás
aprendieron a crear proyectos preguntándole al las mismas
preguntas que usted probablemente hará, por lo que alguien estará
encantado de ayudarle. Resolver cada problema que surja lo
ayudará a desarrollar constantemente sus habilidades hasta que esté
construyendo proyectos significativos y confiables y respondiendo
también las preguntas de otras personas.
Nota
Python en Windows
Las instrucciones del Capítulo 1 le muestran cómo instalar Python
utilizando el instalador oficial en https://python.org. Si no pudo
ejecutar Python después de usar el instalador, las instrucciones de
solución de problemas en esta sección le ayudarán a poner Python
en funcionamiento.
Usando py en lugar de python
Si ejecuta un instalador reciente de Python y luego emite el
comando python en una terminal, debería ver el mensaje de Python
para una sesión de terminal (>>>). Cuando Windows no reconoce el
comando python, abrirá Microsoft Store porque cree que Python no
está instalado o recibirá un mensaje como "No se encontró Python".
Si se abre Microsoft Store, ciérrela; Es mejor utilizar el instalador
oficial de Python de https://python.org que el que mantiene
Microsoft.
La solución más sencilla, sin realizar ningún cambio en su sistema,
es probar el comando py. Esta es una utilidad de Windows que
encuentra la última versión de Python instalada en su sistema y
ejecuta ese intérprete. Si este comando funciona y desea usarlo,
simplemente use py en cualquier lugar donde vea el comando python
o python3 en este libro.
Python en MacOS
Las instrucciones de instalación del Capítulo 1 utilizan el instalador
oficial de Python en https://python.org. El instalador oficial ha
estado funcionando bien durante años, pero hay algunas cosas que
pueden desviarlo. Esta sección le ayudará si algo no funciona de
manera sencilla.
Python es Linux
Python está incluido de forma predeterminada en casi todos los
sistemas Linux. Sin embargo, si la versión predeterminada de su
sistema es anterior a Python 3.9, debe instalar la última versión.
También puede instalar la última versión si desea las funciones más
recientes, como los mensajes de error mejorados de Python. Las
siguientes instrucciones deberían funcionar para la mayoría de los
sistemas basados en apt.
$ python3.11
>>>
Nota
$ python --version
Python 3.11.0
Configurar el código VS
Hay algunas formas de cambiar los ajustes de configuración
predeterminados para VS Code. Algunos cambios se pueden realizar
a través de la interfaz y otros requerirán cambios en los archivos de
configuración. A veces, estos cambios tendrán efecto para todo lo
que haga en VS Code, mientras que otros afectarán solo a los
archivos dentro de la carpeta que contiene el archivo de
configuración.
Por ejemplo, si tiene un archivo de configuración en su carpeta
python_work, esa configuración solo afectará los archivos en esa
carpeta (y sus subcarpetas). Esta es una buena característica,
porque significa que puede tener configuraciones específicas del
proyecto que anulan su configuración global.
configuración.json
"editor.rulers": [
80,
]
Esto agregará una línea vertical en la ventana de edición en la
posición de 80 caracteres. Puedes tener más de una línea vertical;
por ejemplo, si desea una línea adicional de 120 caracteres, el valor
de su configuración sería [80, 120]. Si no ve las líneas verticales,
asegúrese de haber guardado el archivo de configuración; Es posible
que también deba salir y volver a abrir VS Code para que los
cambios surtan efecto en algunos sistemas.
Simplificando la salida
De forma predeterminada, VS Code muestra el resultado de sus
programas en una ventana de terminal integrada. Esta salida incluye
los comandos que se utilizan para ejecutar el archivo. Para muchas
situaciones, esto es ideal, pero puede distraer más de lo deseado
cuando estás aprendiendo Python por primera vez.
Para simplificar el resultado, cierre todas las pestañas que están
abiertas en VS Code y luego salga de VS Code. Inicie VS Code
nuevamente y abra la carpeta que contiene los archivos de Python
en los que está trabajando; Esta podría ser simplemente la carpeta
python_work donde se guarda hello_world.py.
Haga clic en el ícono Ejecutar/Depurar (que parece un triángulo con
un pequeño error) y luego haga clic en Crear un archivo launch.json.
Seleccione las opciones de Python en las indicaciones que aparecen.
En el archivo launch.json que se abre, realice el siguiente cambio:
lanzamiento.json
{
--snip--
"configurations": [
{
--snip--
"console": "internalConsole",
"justMyCode": true
}
]
}
Aquí, estamos cambiando la configuración console de
integratedTerminal a internalConsole. Después de guardar el
archivo de configuración, abra un archivo .py como hello_world.py y
ejecútelo presionando CTRL-F5. En el panel de salida de VS Code,
haga clic en Consola de depuración si aún no está seleccionado.
Debería ver solo el resultado de su programa, y el resultado debe
actualizarse cada vez que ejecute un programa.
Nota
Atajos de código VS
Todos los editores e IDE ofrecen formas eficientes de realizar tareas
comunes que todos deben realizar al escribir y mantener código. Por
ejemplo, puedes sangrar fácilmente una sola línea de código o un
bloque de código completo; puedes mover con la misma facilidad un
bloque de líneas hacia arriba o hacia abajo en un archivo.
Hay demasiados atajos para describirlos completamente aquí. Esta
sección compartirá solo algunos que probablemente le resulten útiles
mientras escribe sus primeros archivos Python. Si terminas usando
un editor diferente a VS Code, asegúrate de aprender cómo realizar
estas mismas tareas de manera eficiente en el editor que has
elegido.
INACTIVO
IDLE es un editor de texto incluido con Python. Es un poco menos
intuitivo trabajar con él que con otros editores más modernos. Sin
embargo, verás referencias a él en otros tutoriales dirigidos a
principiantes, por lo que quizás quieras probarlo.
Geany
Geany es un editor de texto simple que muestra todos sus
resultados en una ventana de terminal separada, lo que le ayuda a
sentirse cómodo usando los terminales. Geany tiene una interfaz
muy minimalista, pero es lo suficientemente potente como para que
un número significativo de programadores experimentados todavía la
utilicen.
Si encuentra que VS Code le distrae demasiado y está lleno de
demasiadas funciones, considere usar Geany en su lugar.
Texto sublime
Sublime Text es otro editor minimalista que deberías considerar usar
si encuentras que VS Code está demasiado ocupado. Sublime Text
tiene una interfaz realmente limpia y es conocido por funcionar bien
incluso con archivos muy grandes. Es un editor que se apartará de
tu camino y te permitirá concentrarte en el código que estás
escribiendo.
Sublime Text tiene una prueba gratuita ilimitada, pero no es gratuita
ni de código abierto. Si decide que le gusta y puede permitirse el
lujo de comprar una licencia completa, debe hacerlo. La compra es
una tarifa única; no es una suscripción de software.
Emacs y Vim
Emacs y Vim son dos editores populares preferidos por muchos
programadores experimentados porque están diseñados para que
puedas usarlos sin que tus manos tengan que dejar el teclado. Esto
hace que escribir, leer y modificar código sea muy eficiente, una vez
que aprenda cómo funciona el editor. También significa que ambos
editores tienen una curva de aprendizaje bastante pronunciada. Vim
está incluido en la mayoría de las máquinas Linux y macOS, y tanto
Emacs como Vim se pueden ejecutar completamente dentro de una
terminal. Por esta razón, se suelen utilizar para escribir código en
servidores a través de sesiones de terminal remotas.
Los programadores a menudo le recomendarán que los pruebe, pero
muchos programadores competentes olvidan cuánto están tratando
de aprender los nuevos programadores. Es bueno conocer estos
editores, pero debes posponer su uso hasta que te sientas cómodo
trabajando con código en un editor más fácil de usar que te permita
concentrarte en aprender a programar, en lugar de aprender a usar
un editor.
PyCharm
PyCharm es un IDE popular entre los programadores de Python
porque fue creado para funcionar específicamente con Python. La
versión completa requiere una suscripción paga, pero también está
disponible una versión gratuita llamada PyCharm Community Edition,
y muchos desarrolladores la encuentran útil.
Si prueba PyCharm, tenga en cuenta que, de forma predeterminada,
configura un entorno aislado para cada uno de sus proyectos. Esto
suele ser algo bueno, pero puede provocar un comportamiento
inesperado si no comprende lo que hace por usted.
Cuadernos Jupyter
Jupyter Notebook es un tipo de herramienta diferente a los editores
de texto o IDE tradicionales, ya que es una aplicación web
construida principalmente a partir de bloques; cada bloque es un
bloque de código o un bloque de texto. Los bloques de texto se
representan en Markdown, por lo que puedes incluir un formato
simple en tus bloques de texto.
Los Jupyter Notebooks se desarrollaron para admitir el uso de
Python en aplicaciones científicas, pero desde entonces se han
ampliado para volverse útiles en una amplia variedad de situaciones.
En lugar de simplemente escribir comentarios dentro de un archivo
.py, puede escribir texto claro con un formato simple, como
encabezados, listas con viñetas e hipervínculos entre secciones de
código. Cada bloque de código se puede ejecutar de forma
independiente, lo que le permite probar pequeñas partes de su
programa, o puede ejecutar todos los bloques de código a la vez.
Cada bloque de código tiene su propia área de salida y puede activar
o desactivar las áreas de salida según sea necesario.
Los Jupyter Notebooks pueden resultar confusos a veces debido a
las interacciones entre diferentes celdas. Si define una función en
una celda, esa función también estará disponible para otras celdas.
Esto es beneficioso la mayor parte del tiempo, pero puede resultar
confuso en portátiles más largos y si no comprende completamente
cómo funciona el entorno de Notebook.
Si está realizando algún trabajo científico o centrado en datos en
Python, es casi seguro que verá Jupyter Notebooks en algún
momento.
C
Obteniendo ayuda
Pinitos
Cuando esté estancado, su primer paso debería ser evaluar su
situación. Antes de pedir ayuda a otra persona, responda claramente
las siguientes tres preguntas:
¿Qué estás intentando hacer?
¿Qué has probado hasta ahora?
¿Qué resultados has estado obteniendo?
Haga sus respuestas lo más específicas posible. Para la primera
pregunta, declaraciones explícitas como "Estoy intentando instalar la
última versión de Python en mi nueva computadora portátil con
Windows" son lo suficientemente detalladas como para que otros
miembros de la comunidad Python puedan ayudarlo. Declaraciones
como "Estoy intentando instalar Python" no proporcionan suficiente
información para que otros puedan ofrecer mucha ayuda.
Su respuesta a la segunda pregunta debe proporcionar suficientes
detalles para que no le aconsejen que repita lo que ya intentó: “Fui
a https://python.org/downloads e hice clic en el botón Descargar de
mi sistema. Luego ejecuté el instalador” es más útil que “Fui al sitio
web de Python y descargué algo”.
Para la tercera pregunta, es útil saber los mensajes de error exactos
que recibió, de modo que pueda usarlos para buscar una solución en
línea o proporcionarlos cuando solicite ayuda.
A veces, simplemente responder estas tres preguntas antes de pedir
ayuda a los demás te permite ver algo que te estás perdiendo y te
ayuda a despegarte sin tener que ir más lejos. Los programadores
incluso tienen un nombre para esto: depuración con pato de goma.
La idea es que si explicas claramente tu situación a un pato de goma
(o cualquier objeto inanimado) y le haces una pregunta específica, a
menudo podrás responder tu propia pregunta. Algunos equipos de
programación incluso tienen un pato de goma real para animar a la
gente a "hablar con el pato".
Inténtalo de nuevo
Simplemente volver al principio y volver a intentarlo puede ser
suficiente para resolver muchos problemas. Digamos que estás
intentando escribir un bucle for basado en un ejemplo de este libro.
Es posible que solo se haya perdido algo simple, como dos puntos al
final de la línea for. Seguir los pasos nuevamente podría ayudarlo a
evitar repetir el mismo error.
Hacer una pausa
Si llevas tiempo trabajando en el mismo problema, tomarte un
descanso es una de las mejores tácticas que puedes probar. Cuando
trabajamos en la misma tarea durante largos períodos de tiempo,
nuestro cerebro comienza a concentrarse en una sola solución.
Perdemos de vista las suposiciones que hemos hecho y tomar un
descanso nos ayuda a tener una nueva perspectiva del problema. No
es necesario que sea un descanso largo, sólo algo que te saque de
tu forma de pensar actual. Si has estado sentado durante mucho
tiempo, haz algo físico: da un paseo corto, sal un rato al aire libre o
tal vez bebe un vaso de agua o come un refrigerio ligero.
Si se siente frustrado, puede que valga la pena dejar su trabajo a un
lado por ese día. Una buena noche de sueño casi siempre hace que
el problema sea más abordable.
Buscando en línea
Es muy probable que alguien más haya tenido el mismo problema
que usted y haya escrito sobre ello en línea. Unas buenas
habilidades de búsqueda y consultas específicas le ayudarán a
encontrar recursos existentes para resolver el problema al que se
enfrenta. Por ejemplo, si tiene dificultades para instalar la última
versión de Python en un nuevo sistema Windows, buscar instalar
Python en Windows y limitar los resultados a los recursos del último
año puede conducirlo a una respuesta clara.
Buscar el mensaje de error exacto también puede resultar muy útil.
Por ejemplo, supongamos que recibe el siguiente error cuando
intenta ejecutar un programa Python desde una terminal en un
nuevo sistema Windows:
> python hello_world.py
Python was not found; run without arguments to install from
the Microsoft
Store...
Desbordamiento de pila
Stack Overflow (https://stackoverflow.com) es uno de los sitios de
preguntas y respuestas más populares para programadores y, a
menudo, aparecerá en la primera página de resultados de
búsquedas relacionadas con Python. Los miembros publican
preguntas cuando están estancados y otros miembros intentan dar
respuestas útiles. Los usuarios pueden votar por las respuestas que
consideren más útiles, por lo que las mejores respuestas suelen ser
las primeras que encontrará.
Muchas preguntas básicas de Python tienen respuestas muy claras
en Stack Overflow, porque la comunidad las ha perfeccionado con el
tiempo. También se anima a los usuarios a publicar actualizaciones,
por lo que las respuestas tienden a permanecer relativamente
actualizadas. Al momento de escribir este artículo, casi dos millones
de preguntas relacionadas con Python han sido respondidas en
Stack Overflow.
Hay una expectativa que debes tener en cuenta antes de publicar en
Stack Overflow. Las preguntas deben ser el ejemplo más breve del
tipo de problema al que se enfrenta. Si publica entre 5 y 20 líneas de
código que generan el error al que se enfrenta y si responde a las
preguntas mencionadas en “Primeros pasos” en la página 477
anteriormente en este apéndice, probablemente alguien le ayudará.
Si comparte un enlace a un proyecto con varios archivos grandes, es
muy poco probable que la gente le ayude. Hay una excelente guía
para redactar una buena pregunta en
https://stackoverflow.com/help/how-to-ask. Las sugerencias de esta
guía son aplicables para obtener ayuda en cualquier comunidad de
programadores.
Publicaciones de blog
Muchos programadores mantienen blogs y comparten publicaciones
sobre las partes del lenguaje con las que trabajan. Debe buscar una
fecha en las publicaciones del blog que encuentre para ver qué tan
aplicable es la información para la versión de Python que está
utilizando.
Discordia
Discord es un entorno de chat en línea con una comunidad de
Python donde puedes pedir ayuda y seguir discusiones relacionadas
con Python.
Para comprobarlo, dirígete a https://pythondiscord.com y haz clic en
el enlace de Discord en la esquina superior derecha. Si ya tiene una
cuenta de Discord, puede iniciar sesión con su cuenta existente. Si
no tiene una cuenta, ingrese un nombre de usuario y siga las
instrucciones para completar su registro en Discord.
Si es la primera vez que visita Python Discord, deberá aceptar las
reglas de la comunidad antes de participar plenamente. Una vez que
hayas hecho eso, podrás unirte a cualquiera de los canales que te
interesen. Si busca ayuda, asegúrese de publicarla en uno de los
canales de ayuda de Python.
Flojo
Slack es otro entorno de chat en línea. A menudo se utiliza para
comunicaciones internas de la empresa, pero también hay muchos
grupos públicos a los que puede unirse. Si desea consultar los
grupos de Python Slack, comience con https://pyslackers.com. Haga
clic en el enlace de Slack en la parte superior de la página, luego
ingrese su dirección de correo electrónico para recibir una invitación.
Una vez que esté en el espacio de trabajo de Python Developers,
verá una lista de canales. Haga clic en Canales y luego elija los
temas que le interesen. Quizás quieras comenzar con los canales
#help y #django.
D
Uso de Git para el control de
versiones
Instalación de Git
Git se ejecuta en todos los sistemas operativos, pero existen
diferentes enfoques para instalarlo en cada sistema. Las siguientes
secciones proporcionan instrucciones específicas para cada sistema
operativo.
Git se incluye en algunos sistemas de forma predeterminada y, a
menudo, se incluye con otros paquetes que quizás ya haya instalado.
Antes de intentar instalar Git, vea si ya está en su sistema. Abra una
nueva ventana de terminal y emita el comando git --version. Si ve
un resultado que enumera un número de versión específico, Git está
instalado en su sistema. Si ve un mensaje que le solicita que instale
o actualice Git, siga las instrucciones en pantalla.
Si no ve ninguna instrucción en pantalla y está usando Windows o
macOS, puede descargar un instalador desde https://git-scm.com. Si
es un usuario de Linux con un sistema compatible con apt, puede
instalar Git con el comando sudo apt install git.
Configurando Git
Git realiza un seguimiento de quién realiza cambios en un proyecto,
incluso cuando solo hay una persona trabajando en el proyecto. Para
hacer esto, Git necesita saber su nombre de usuario y correo
electrónico. Debes proporcionar un nombre de usuario, pero puedes
crear una dirección de correo electrónico falsa:
$ git config --global user.name "username"
$ git config --global user.email "[email protected]"
Si olvida este paso, Git le solicitará esta información cuando realice
su primera confirmación.
También es mejor establecer el nombre predeterminado para la
rama principal de cada proyecto. Un buen nombre para esta sucursal
es main:
Esta configuración significa que cada nuevo proyecto que utilices Git
para administrar comenzará con una única rama de confirmaciones
llamada principal.
Hacer un proyecto
Hagamos un proyecto con el que trabajar. Cree una carpeta en algún
lugar de su sistema llamada git_practice. Dentro de la carpeta, cree
un programa Python simple:
hola_git.py
Ignorar archivos
Los archivos con la extensión .pyc se generan automáticamente a
partir de archivos .py, por lo que no necesitamos que Git realice un
seguimiento de ellos. Estos archivos se almacenan en un directorio
llamado __pycache__. Para decirle a Git que ignore este directorio,
cree un archivo especial llamado .gitignore (con un punto al principio
del nombre del archivo y sin extensión de archivo) y agréguele la
siguiente línea:
.gitignore
__pycache__/
Nota
Inicializando un repositorio
Ahora que tiene un directorio que contiene un archivo Python y un
archivo .gitignore, puede inicializar un repositorio Git. Abra una
terminal, navegue hasta la carpeta git_practice y ejecute el siguiente
comando:
Comprobando el estado
Antes de hacer nada más, veamos el estado del proyecto:
❷ Untracked files:
(use "git add <file>..." to include in what will be
committed)
.gitignore
hello_git.py
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
❸ new file: .gitignore
new file: hello_git.py
git_practice$
Hacer un compromiso
Hagamos el primer compromiso:
Comprobando el registro
Git mantiene un registro de todas las confirmaciones realizadas en el
proyecto. Revisemos el registro:
Started project.
git_practice$
Cada vez que realiza una confirmación, Git genera una identificación
de referencia única de 40 caracteres. Registra quién realizó la
confirmación, cuándo se realizó y el mensaje grabado. No siempre
necesitarás toda esta información, por lo que Git ofrece una opción
para imprimir una versión más simple de las entradas del registro:
El segundo compromiso
Para ver el poder real del control de versiones, debemos realizar un
cambio en el proyecto y confirmar ese cambio. Aquí simplemente
agregaremos otra línea a hello_git.py:
hola_git.py
❷ modified: hello_git.py
Nota
❶ You are in 'detached HEAD' state. You can look around, make
experimental
changes and commit them, and you can discard any commits you
make in this
state without impacting any branches by switching back to a
branch.
git switch -
Turn off this advice by setting config variable
advice.detachedHead to false
Eliminar el repositorio
A veces arruinarás el historial de tu repositorio y no sabrás cómo
recuperarlo. Si esto sucede, primero considere pedir ayuda usando
los enfoques discutidos en el Apéndice C. Si no puede solucionarlo y
está trabajando en un proyecto en solitario, puede continuar
trabajando con los archivos pero deshacerse del historial del
proyecto eliminando el directorio .git. Esto no afectará el estado
actual de ninguno de los archivos, pero eliminará todas las
confirmaciones, por lo que no podrá verificar ningún otro estado del
proyecto.
Para hacer esto, abra un explorador de archivos y elimine el
repositorio .git o elimínelo desde la línea de comando. Luego, deberá
comenzar de nuevo con un repositorio nuevo para comenzar a
rastrear sus cambios nuevamente. Así es como se ve todo este
proceso en una sesión de terminal:
Untracked files:
(use "git add <file>..." to include in what will be
committed)
.gitignore
hello_git.py
❷ [RootNotFoundException]
Project root not found. This can only be run from inside a
project directory.
Nota
Nota
Git bash
Otro enfoque para crear un entorno local que pueda implementar
utiliza Git Bash, un entorno de terminal que es compatible con Bash
pero se ejecuta en Windows. Git Bash se instala junto con Git
cuando usa el instalador de https://git-scm.com. Este enfoque puede
funcionar, pero no es tan sencillo como WSL. En este enfoque,
tendrás que usar una terminal de Windows para algunos pasos y
una terminal de Git Bash para otros.
Primero necesitarás instalar PHP. Puedes hacer esto con XAMPP, un
paquete que incluye PHP con algunas otras herramientas centradas
en desarrolladores. Vaya a https://apachefriends.org y haga clic en
el botón para descargar XAMPP para Windows. Abra el instalador y
ejecútelo; Si ve una advertencia sobre las restricciones del Control
de cuentas de usuario (UAC), haga clic en Aceptar. Acepte todos los
valores predeterminados del instalador.
Cuando el instalador termine de ejecutarse, deberá agregar PHP a la
ruta de su sistema; esto le indicará a Windows dónde buscar cuando
desee ejecutar PHP. En el menú Inicio, ingrese path y haga clic en
Editar las variables de entorno del sistema; haga clic en el botón
denominado Variables de entorno. Deberías ver la variable Path
resaltada; haga clic en Editar debajo de este panel. Haga clic en
Nuevo para agregar una nueva ruta a la lista actual de rutas.
Suponiendo que mantuvo la configuración predeterminada al
ejecutar el instalador XAMPP, agregue C:\xampp\php en el cuadro que
aparece, luego haga clic en Aceptar. Cuando haya terminado, cierre
todos los cuadros de diálogo del sistema que aún estén abiertos.
Una vez cumplidos estos requisitos, puede instalar la CLI de
Platform.sh. Necesitará utilizar una terminal de Windows con
privilegios de administrador; ingrese command en el menú Inicio y, en
la aplicación Símbolo del sistema, haga clic en Ejecutar como
administrador. En la terminal que aparece, ingrese el siguiente
comando:
Símbolos
+ (adición), 26
{} (llaves)
diccionarios, 92
conjuntos, 104
/ (división), 26
== (igualdad), 72, 74
** (exponente), 26
!= (desigualdad), 74
[] (lista), 34
% (formulario), 116
* (multiplicación), 26
\n (nueva línea), 22
- (resta), 26
\t (pestaña), 22
_ (guión bajo)
en nombres de variables, 17
alice.py, 195-197
extraterrestres, 256–274
disparo, 249–250
ajustes, 247
acelerando, 269
clases
Alien, 257
AlienInvasion, 229
Bullet, 247–248
Button, 278–279
GameStats, 271
Scoreboard, 286–287
Settings, 232
Ship, 234–235
niveles
mostrando, 294–296
planificación, 228
desactivando, 282
dibujo, 279–280
puntuación, 286–298
nivel, 294–296
restablecer, 289–290
actualización, 289
barco, 233–244
parque_atracciones.py, 80–82
suavizado, 279
usuarios_prohibidos.py, 76–77
bicicletas.py, 34–35
Valores booleanos, 77
llaves ({})
diccionarios, 92
conjuntos, 104
do
coche.py, 162–178
coches.py, 43–45, 72
ciudades.py, 121
clases
atributos, 159
accediendo, 160
creando, 158–161
importar, 173-179
herencia, 167-172
composición, 170
subclases, 168
superclases, 168
casos, 157
métodos, 159
llamando, 160
encadenamiento, 185
objetos, 157
comentario.py, 29
comentarios, 29–30
usuarios_confirmados.py, 124–125
constantes, 28
valle_muerte_altos_bajos.py, 339–341
valores predeterminados
del declaración
con diccionarios, 96
dice_visual_d6d10.py, 326–327
dice_visual.py, 324–326
diccionarios
definiendo, 92
vacío, 94
KeyError, 98
pares clave-valor, 92
añadiendo, 93–94
eliminando, 96
dando vueltas
claves, 101–102
valores, 103–104
metodos
get(), 97–98
items(), 99-101
keys(), 101–103
values(), 103-104
anidando
valores
modificando, 94–96
morir.py, 320
die_visual.py, 320–321
dimensiones.py, 66–67
Arranque, 434–445
tarjeta, 443
contenedor, 440
documentación, 444
jumbotrón, 440–441
comandos
createsuperuser, 382
flush, 427
migrate, 377
shell, 386
startproject, 376
bases de datos
creando, 376
Postgres, 447
restablecer, 427
SQLite, 377
gunicorn, 447
Plataforma.sh, 445
psycopg2, 447
requisitos.txt, 446
ajustes, 451
documentación
consultas, 388
plantillas, 400
csrf_token, 407
validación, 404–406
widgets, 408
HTML
comentarios, 437
márgenes, 440
acolchado, 440
INSTALLED_APPS, 380
instalación, 375–376
modelos, 379
activación, 380–381
ajustes
ALLOWED_HOSTS, 451
DEBUG, 457–458
LOGIN_REDIRECT_URL, 417–418
LOGIN_URL, 424
LOGOUT_REDIRECT_URL, 420
SECRET_KEY, 451
plantillas
bloquear etiquetas, 393
filtros, 399
herencia, 392–394
linebreaks, 399
bucles, 395–397
escritura, 390–392
UserCreationForm, 421–422
valores de ID de usuario, 426
versiones, 376
perro.py, 158–162
mi
coche_electrico.py, 167–173
módulo, 177–179
eq_explore_data.py, 343–347
eq_world_map.py, 347–352
números_pares.py, 58
par_o_impar.py, 117
ZeroDivisionError, 192-195
exponentes (**), 26
lector_archivo.py, 184–187
archivos
absoluto, 186
pariente, 186
de cuerdas, 198
en Windows, 186
primeros_números.py, 57
accesorios, 221–223
banderas, 120–121
carrozas, 26–28
alimentos.py, 63–64
cuerdas f
nombre_completo.py, 21
funciones, 129-155
argumentos
arbitrario, 146-149
errores, 136
opcional, 138–139
posicional, 131-133
cuerpo, 130
incorporado, 467
definiendo, 130
importando, 149-153
alias, 151-152
módulos, 149–153
parámetros, 131
GRAMO
obteniendo ayuda
Discordia, 480
flojo, 481
sucursales, 486
confirma, 486–488
.gitignore, 484
CABEZA, 490
registro, 487
repositorios, 356
estado, 485–486
GitHub, 356
saludar_usuarios.py, 142
hola_git.py, 484–491
hn_artículo.py, 368–369
hn_envíos.py, 369–371
I
IDE (entorno de desarrollo integrado), 469–470
if declaraciones
expresiones booleanas, 77
comprobando
igualdad (==), 72
desigualdad (!=), 74
elemento en la lista, 76
artículo no en la lista, 76
or palabra clave, 76
sencillo, 78
pautas de estilo, 89
inmutable, 65
método insert(), 38
Archivos JSON
idioma_encuesta.py, 219
archivos, 392
404.html, 459
500.html, 459
administrador.py, 382–383
editar_entrada.html, 413
.gitignore, 452–453
nueva_entrada.html, 410
nuevo_tema.html, 407
.plataforma.aplicación.yaml, 448–450
registrarse.html, 422
requisitos.txt, 446–447
rutas.yaml, 450
servicios.yaml, 450
páginas, 391
registro, 420–423
tema, 397–400
temas, 394–397
linux
Pitón
terminales
listas, 33
comprensiones, 59–60
copiando, 63–64
elementos
acceder, 34
accediendo último, 35
modificando, 36–37
vacío, 37–38
errores
sangría, 53–56
índice, 46
índices, 34–35
índice negativo, 35
anidando
función max(), 59
función min(), 59
función sum(), 59
rebanadas, 61–62
clasificación
método reverse(), 44
método sort(), 43
corchetes, 34
errores lógicos, 54
macos
Pitón
terminales
magos.py, 49–56
número_magico.py, 74
haciendo_pizzas.py, 150–152
magnitudes, 346
Matplotlib
ejes
eliminando, 317
ax objetos, 303
formatear gráficos
sombreado, 337–338
galería, 302
instalación, 302
métodos, 20
motocicletas.py, 36–41
montaña_poll.py, 125–126
mpl_cuadrados.py, 302–306
mi_car.py, 174–175
mis_autos.py, 176–179
mi_coche_electrico.py, 176
norte
nombre_función.py, 211–217
nombre.py, 20
nombres.py, 211–212
números, 26–28
aritmética, 26
constantes, 28
exponentes, 26
carrozas, 26–27
formateo, 291–292
números enteros, 26
orden de operaciones, 26
número_escritor.py, 201
oh
pandas, 320
parámetros, 131
PEP 8, 68–69
persona.py, 139–140
pipa, 210–211
actualización, 210
pi_string.py, 187–189
pizza.py, 146–148
jugadores.py, 61–62
documentación, 368
formatear gráficos
galería, 320
histogramas, 322
instalación, 320
modelos_impresión.py, 143–145
grupos
definiendo, 248–249
vaciado, 268–269
recorriendo, 249–251
imágenes, 234–236
instalación, 228
niveles, 283–285
eventos, 230
superficies, 230
Pitón
>>> mensaje, 4
instalando
en Linux, 465–466
intérprete, 15-16
en Linux, 9
en macOS, 7–8
en Windows, 6
versiones, 4
python_repos.py, 357–362
python_repos_visual.py, 362–367
paseo_aleatorio.py, 312–313
trazando, 313–314
recuerda_me.py, 202–206
método removeprefix(), 24
método removesuffix(), 25
rw_visual.py, 313–318
scatter_squares.py, 306–311
conjuntos, 103-104
sitka_highs_lows.py, 336–338
sitka_highs.py, 330–336
rebanadas, 61–64
método sort(), 43
cuadrados.py, 59–60
cuerdas, 19-25
cambio de caso, 20
metodos
lower(), 20
lstrip(), 22-23
removeprefix(), 23-24
removesuffix(), 25
rstrip(), 22-23
split(), 196–197
splitlines(), 186–187
strip(), 22-23
title(), 20
upper(), 20
multilínea, 115
líneas en blanco, 69
clases, 181
diccionarios, 96–97
funciones, 153
if declaraciones, 89
sangría, 68
longitud de línea, 69
PEP 8, 68
encuesta.py, 218
errores de sintaxis, 24
resaltado de sintaxis, 16
instalación, 210–211
prueba_nombre_función.py, 212–217
test_survey.py, 220–223
Geany, 474
INACTIVO, 474
PyCharm, 475
tuplas, 65–67
definiendo, 65
escribiendo encima, 67
errores tipográficos, 66
Ud.
en números, 28
en nombres de variables, 17
perfil_usuario.py, 148–149
constantes, 28
asignación múltiple, 28
convenciones de nomenclatura, 17
valores, 16
votación.py, 78–80
configurando, 470–473
características, 469–470
instalando
en Linux, 9
en MacOS, 8
en Windows, 6
Extensión de Python, 9
ejecutando archivos, 10
atajos, 473–474
W.
ventanas
Pitón
terminales
word_count.py, 197–199
escribir_mensaje.py, 190–191
ZeroDivisionError, 192-195