Principales características y mejoras en la programación de Python
Habilidades avanzadas de Python
En este tutorial, se le presentarán los últimos avances en el lenguaje de programación Python, que incluyen:
• Mensajes de
error mejorados a través de rastreos más descriptivos
• Aceleración
del rendimiento del código como resultado de importantes contribuciones al
proyecto Faster CPython
• La
integración de grupos de tareas y excepciones para una experiencia optimizada
al manejar código asíncrono
• Capacidades
avanzadas de escritura que aumentan la funcionalidad de escritura estática de
Python
• Soporte nativo para el formato TOML en el trabajo con archivos de configuración.
Para seguir
eficazmente los ejemplos presentados en este tutorial, se recomienda utilizar Python 3.11. La guía de instalación y configuración de Python 3, así como la guía
sobre la instalación de una versión preliminar de Python, proporcionan varias
opciones para agregar la última versión de Python a su sistema.
Además de
obtener información sobre las nuevas características del lenguaje, este
tutorial también ofrece orientación sobre qué tener en cuenta antes de
actualizar a la última versión de Python.
Más seguimientos de errores descriptivos
Python es
reconocido por su simplicidad y versatilidad, con su sintaxis fácil de leer y
estructuras de datos robustas que lo convierten en una opción ideal para
aquellos nuevos en la programación. Un desafío común que enfrentan todos,
particularmente los principiantes, es la interpretación de los rastreos cuando se producen errores en
Python.
En Python
3.10, hubo una mejora significativa en el sistema de mensajería de errores.
Python 3.11 continúa mejorando la experiencia del desarrollador al agregar
anotaciones informativas a los seguimientos. Estas anotaciones proporcionan una
comprensión más clara de los mensajes de error, lo que permite una resolución
más rápida de los problemas.
Para experimentar
la mejora en los seguimientos, simplemente agregue el siguiente código a un
archivo denominado "inverse.py
+
# Definir la función inversa
def inverso
(número):
# Devuelve el
resultado de 1 dividido por el número de entrada
Devolución 1 /
Número
# Llamar a la
función inversa y pasar el argumento 0
imprimir(inversa(0))
La función 'inverse()'
se puede utilizar para calcular el recíproco de un número. Sin embargo, como no
hay recíproco para 0, intentar ejecutar el código dará como resultado un error.
Rastreo
(última llamada más reciente):
Archivo
"/home/realpython/inverse.py", línea 6, en <módulo>
imprimir(inversa(0))
^^^^^^^^^^
Archivo
"/home/realpython/inverse.py", línea 4, a la inversa
Devolución 1 /
Número
~~^~~~~~~~
ZeroDivisionError:
división por cero
```"
El resultado
de ejecutar el script inverse.py da como resultado un seguimiento debido
a un ZeroDivisionError que se produce cuando se intenta una división por
cero. El seguimiento detalla el error, incluido el archivo en el que se produjo
(/home/realpython/inverse.py)), el número de
línea (línea 6) y la llamada que causó el error (print(inverse(0))). El rastreo también
muestra que el error se origina en la operación de división en la función
inverse() (línea 4) con el símbolo ~~^~~~~~~~~ que indica la ubicación
específica del error en el código.
Tenga en
cuenta el uso de los símbolos ^ y ~ dentro del rastreo. Estos símbolos se
utilizan para resaltar el código específico que está causando el error. Como es
la práctica estándar cuando se trabaja con rastreos, se recomienda comenzar
desde abajo y trabajar hacia arriba. En el presente ejemplo, el error es el
resultado de la división 1 / número, causado por la ejecución de inverse(0)
donde 0 no tiene recíproco.
La inclusión
de estas anotaciones en los seguimientos de errores puede mejorar
significativamente la experiencia de depuración, especialmente cuando el código
es complejo. Las anotaciones pueden proporcionar información que antes no era
posible solo con los seguimientos.
Para ilustrar
aún más los beneficios de los rastreos mejorados, considere un escenario en el
que se crea un pequeño analizador para extraer información sobre algunos
programadores. Los datos se almacenan en un archivo denominado programmers.json
y contiene la siguiente información:
# Los datos se
almacenan en una lista de diccionarios, cada uno representando a un programador
# Cada
programador tiene una clave de "nombre" que contiene un diccionario
con "nombre" y opcionalmente "apellido" nombre
# Cada
programador tiene una clave de "nacimiento" que contiene un
diccionario con "año", "mes" y "día" de
nacimiento
# Cada
programador tiene una clave "death" que contiene un diccionario con
"year", "month" y "day" of death o está
configurado en "null"
[
{
"nombre":
{
"primero":
"Tío Barry" }},
{
"nombre":
{
"primero":
"Ada",
"last":
"Lovelace" },
"nacimiento":
{
"año":
1815 },
"muerte":
{
"mes":
11,
"día":
27 }},
{
"nombre":
{
"primero":
"Gracia",
"last":
"Hopper" },
"nacimiento":
{
"año":
1906,
"mes":
12,
"día":
9 },
"muerte":
{
"año":
1992,
"mes":
1,
"día":
1 } },
{
"nombre":
{
"primero":
"Ole-Johan",
"last":
"Dahl" },
"nacimiento":
{
"año":
1931,
"mes":
10,
"día":
12 },
"muerte":
{
"año":
2002,
"mes":
6,
"día":
29
}
},
{
"nombre":
{
"primero":
"Guido",
"último":
"Van Rossum"
},
"nacimiento":
{
"Año":
1956,
"mes":
1,
"día":
31
},
"muerte":
nulo
}
]
La información sobre los
programadores en el archivo JSON es inconsistente e incompleta. Por ejemplo, la
información sobre Grace Hopper y Ole-Johan Dahl está completa, pero faltan el
día y el mes de nacimiento de Ada Lovelace y su año de muerte. Además, solo se
registra la información de nacimiento de Guido Van Rossum, y solo se registra
el nombre del tío Barry.
Para solucionar esto, creará una clase que pueda almacenar y administrar
correctamente esta información. El primer paso es leer la información del archivo
JSON.
#
programmers.py
# Importar
módulos requeridos
Importar JSON
import pathlib
# Cargar datos
del archivo "programmers.json" en la variable "programmers"
programadores
= json.loads(
pathlib.
Path("programmers.json").read_text(encoding="utf-8")
)
El siguiente
código utiliza la biblioteca pathlib para leer el contenido del archivo JSON y
json para analizar el contenido en una lista de diccionarios en Python. Los
datos se encapsulan en una clase de datos para una mejor organización y
facilidad de uso.
#
programmers.py
Desde
DataClasses Importar DataClass
# Definir la
clase de datos Person
@dataclass
Persona de
clase:
# Definir
variables de clase para almacenar el nombre y la vida útil de la persona
Nombre: Str
life_span:
tupla[int, int]
# Definir un
método de clase para convertir la información del diccionario en objeto Person
@classmethod
def
from_dict(cls, info):
# Extraer el
nombre y el life_span del diccionario y crear un objeto Person
return cls(
name=f"{info['name']['first']}
{info['name']['last']}",
life_span=(info["nacimiento"]["año"],
info["muerte"]["año"]),
)
La clase
Person tiene dos atributos: nombre y life_span. La clase también
contiene un método de clase denominado from_dict que sirve como un
constructor conveniente para inicializar una instancia de Person basada en la información encontrada en un diccionario, como la almacenada en el
archivo JSON. Además, hay una función que puede inicializar dos objetos Person
a la vez.
def
convert_pair(primero, segundo):
""Convierte
dos diccionarios en dos objetos Person.
Argumentos:
first: Un
diccionario que representa información sobre la primera persona.
segundo: Un
diccionario que representa información sobre la segunda persona.
Devuelve:
Una tupla de
dos objetos Person, uno para cada diccionario de entrada.
"""
return
Person.from_dict(primero), Person.from_dict(segundo)
La función convert_pair()
toma dos diccionarios que representan un par de programadores en la estructura
JSON y utiliza el archivo .
from_dict() constructor para convertirlos en objetos Person. Para probar y explorar el
código, se recomienda ejecutar el programa con el indicador -i para abrir el
REPL interactivo de Python, permitiendo el acceso a todas las variables, clases
y funciones definidas.
Ejecución del
REPL interactivo de Python con el archivo "programmers.py" cargado
$ python -i
programmers.py
Convertir el
tercer programador de la lista "programadores" en un objeto
"Persona"
Person.from_dict(programadores[2])
Salida: Objeto
de persona con el nombre "Grace Hopper" y vida útil (1906, 1992)
Persona(name='Grace
Hopper', life_span=(1906, 1992))
Convertiste
con éxito la información de Grace Hopper en un objeto Person con su nombre
completo y su vida útil. Para demostrar el efecto del rastreo, ahora
intentaremos convertir al tío Barry.
# Acceso al
primer elemento de la lista de 'programadores'
>>>
programadores[0]
{'name':
{'first': 'Uncle Barry'}}
# Intentando
convertir el primer elemento de la lista 'programadores' en un objeto 'Persona'
>>>
Person.from_dict(programadores[0])
# El rastreo
se produce debido a un KeyError al intentar acceder al campo 'último' dentro
del campo 'nombre'
Rastreo
(última llamada más reciente):
Archivo
"/home/realpython/programmers.py", línea 17, en from_dict
name=f"{info['name']['first']}
{info['name']['last']}",
~~~~~~~~~~~~^^^^^^^^
La ausencia de
la "última" clave dentro del campo "nombre" de los datos
para el tío Barry hace que se genere un KeyError. Esto resalta la importancia
de las anotaciones para aclarar la estructura de los datos.
De manera
similar, la falta de información completa sobre la vida útil de Ada impide la
creación de un objeto Persona para ella. Esto enfatiza la necesidad de una
validación exhaustiva de los datos antes del procesamiento.
>>>
programadores[1]
{
'nombre': {'primero':
'Ada', 'último': 'Lovelace'},
«nacimiento»:
{'año': 1815},
'muerte':
{'mes': 11, 'día': 27}
}
>>>
Person.from_dict(programadores[1])
Rastreo
(última llamada más reciente):
Archivo
"/home/realpython/programmers.py", línea 18, en from_dict
life_span=(info["nacimiento"]["año"],
info["muerte"]["año"]),
~~~~~~~~~~~~~^^^^^^^^
- El código intenta crear un objeto Person a partir
de un diccionario que representa a un programador
- El seguimiento indica un error en el proceso de
creación, específicamente en la línea que define life_span
- El KeyError: 'year' sugiere que el
diccionario no contiene una clave de año para la información de muerte, lo
que resulta en un error al intentar acceder a él.
Recibe otro KeyError, esta vez porque falta el atributo "año"
para la muerte. El seguimiento resalta el atributo faltante dentro de la
información de vida útil, lo que permite una resolución más rápida del
problema.
¿Qué
pasa con Guido? Solo tienes información sobre su nacimiento:
>>>
programadores[4]
{
«nombre»:
{'primero': 'Guido', 'último': 'Van Rossum'},
'nacimiento':
{'año': 1956, 'mes': 1, 'día': 31},
'muerte':
Ninguna
}
# Intento de
convertir a un objeto Person
>>>
Person.from_dict(programadores[4])
# Dando como
resultado un TypeError, ya que el valor de "death" es None y no se
puede subíndice
Rastreo
(última llamada más reciente):
Archivo
"/home/realpython/programmers.py", línea 18, en from_dict
life_span=(info["nacimiento"]["año"],
info["muerte"]["año"]),
~~~~~~~~~~~~~^^^^^^^^
TypeError: el
objeto 'NoneType' no es subscriptableTypeError:
En este
escenario, se produce un TypeError. Tales errores de tipo que involucran
'NoneType' a menudo pueden ser difíciles de depurar debido a la falta de
claridad sobre qué objeto no está definido. Sin embargo, la anotación de
seguimiento proporciona información sobre el problema, lo que indica que
info["death"] está establecido en None.Finalmente, examinaremos el
resultado de las llamadas a funciones anidadas. Recuerde que la función
convert_pair() invoca el método Person.from_dict() dos veces. Intentemos
emparejar a Ada y Ole-Johan.
>>>
convert_pair(programadores[3], programadores[1])
Rastreo
(última llamada más reciente):
Archivo
"/home/realpython/programmers.py", línea 24, en convert_pair
return
Person.from_dict(primero), Person.from_dict(segundo)
# Error
generado al convertir en primera persona
Archivo
"/home/realpython/programmers.py", línea 18, en from_dict
life_span=(info["nacimiento"]["año"],
info["muerte"]["año"]),
# Error
generado al acceder al año de fallecimiento
KeyError:
'año'
Al intentar encapsular Ada, se produce el mismo
KeyError que antes. El rastreo desde dentro de convert_pair() es notable en
este escenario. En versiones anteriores de Python, habría sido difícil
determinar si el error fue causado por el procesamiento de primero o segundo,
ya que la función llama a .from_dict() dos veces. Sin embargo, en la última
versión de Python, es inmediatamente evidente que el problema es con el
segundo.
Estos rastreos mejorados en Python 3.11 hacen que
la depuración sea más fácil que en versiones anteriores. Para obtener más
ejemplos e información sobre la implementación de estos seguimientos, así como
otras herramientas de depuración, puede consultar el tutorial de vista previa
de Python 3.11 "Mensajes de error aún mejores". Además, para una
perspectiva más técnica, puede consultar PEP 657.
Los rastreos anotados en Python 3.11 son un gran
activo para los desarrolladores de Python, ya que aumentan su productividad.
Además, esta versión de Python también es la más rápida hasta ahora.