Czytaj książkę: «Python a fondo»
PYTHON A FONDO
Óscar Ramírez Jiménez
Acceda a www.marcombo.info para descargar gratis contenido adicional complemento imprescindible de este libro
Código: PYTHON6
PYTHON A FONDO
Óscar Ramírez Jiménez
Python a fondo
Primera edición, 2021
© 2021 Óscar Ramírez Jiménez
© 2021 MARCOMBO, S. L.
www.marcombo.com
Diseño de cubierta: ENEDENÚ DISEÑO GRÁFICO
Revisor técnico: Ferran Fábregas
Corrección: Haizea Beitia y Manel Fernández
Maquetación: D. Márquez
Directora de producción: M.a Rosa Castillo
«Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra».
ISBN: 978-84-267-3287-3
Producción del ePub: booqlab
Motivación y agradecimientos
Desde que era un niño siempre me gustó la idea de dejar huella en los demás y aportar mi granito de arena a la sociedad de una forma u otra, de ahí que desde que conocí el desarrollo de software, siempre haya tenido especial predilección por el software libre y el compartir el conocimiento con los demás.
En esta ocasión, el sentimiento de compartir el conocimiento con los demás me ha llevado a escribir lo que he aprendido en todos estos años de trabajo, desarrollando aplicaciones y creciendo como profesional, de la forma en la que me hubiera gustado aprenderlo cuando comencé mi carrera.
Por lo tanto, me gustaría agradecerles a todos los que han hecho posible este sueño, y dado que son muchísimas las personas las que han dejado huella en mí, desde profesores de universidad tanto de Málaga como de Horsens, compañeros de estudios, compañeros de trabajo, amigos, etc., no podría nombrarlas una a una sin olvidar a alguien, por lo que prefiero agradecerles a todas su apoyo. Aunque sí que me gustaría agradecer de forma especial a mi familia, la que me ha apoyado desde siempre con todos mis proyectos, estudios y en mi carrera profesional, y, cómo no, a mi compañera de viaje, la cual ha sufrido más que nadie el robo de horas que compartir juntos con el fin de poder realizar esta obra, gracias Salva.
Me gustaría también agradecer la oportunidad y la confianza depositada en mí por parte de la editorial Marcombo, la cual apostó por mí para este proyecto desde primera hora y siempre ha estado dispuesta cuando se ha necesitado.
ÍNDICE
CAPÍTULO 1 INTRODUCCIÓN AL LENGUAJE PYTHON
1 Introducción
1.1 Evolución de las versiones de Python
1.2 Características principales de los lenguajes de programación
1.2.1 Generaciones y niveles de abstracción de los lenguajes de programación
1.2.2 Paradigmas
1.2.3 Clasificación de lenguajes según su tipado
1.2.4 Características de Python
1.3 Ámbitos de uso de Python
1.3.1 Programación a nivel de sistema operativo
1.3.2 Aplicaciones con interfaz de usuario
1.3.3 Aplicaciones web e interacción con servicios web
1.3.4 Interacción con servicios de Internet
1.3.5 Gestión de contenido
1.3.6 Aplicaciones científicas y manejo de datos
1.3.7 Inteligencia artificial y Python
1.4 Python Enhancement Proposals (PEP)
1.4.1 Proceso de creación de un PEP
1.5 PEP-8: Guía de estilos
1.5.1 Indentación
1.5.2 Longitud de líneas
1.5.3 Espacios, saltos de línea y líneas en blanco
1.5.4 Otros consejos generales
1.5.5 Comentarios y documentación de código
1.5.6 Convención de nombres
1.5.7 Herramientas para cumplir con la PEP-8
1.6 PEP-20: Zen de Python
1.7 Compañías que usan productos creados en Python
1.8 Posición de Python entre los lenguajes de programación
1.9 Python 2 vs Python 3
1.9.1 str, bytes y Unicode
1.9.2 Comparaciones de tipos no ordenables
1.9.3 Operaciones numéricas diferentes
1.9.4 Iteradores por defecto
1.9.5 Función print
1.9.6 Migrar de Python 2 a Python 3
1.10 Instalación de Python en diferentes sistemas operativos
1.10.1 Instalación en Linux
1.10.2 Instalación en Windows
1.10.3 Instalación en Mac OS X
1.11 Distribuciones de Python
1.11.1 Anaconda
1.11.2 WinPython
1.11.3 Enthought Canopy
1.11.4 ActivePython
1.12 Instalación de librerías y módulos en Python
1.13 Manejo de entornos virtuales
1.14 Intérpretes interactivos (REPL)
1.14.1 Python
1.14.2 IPython
1.14.3 bpython
1.14.4 ptpython
1.14.5 Intérpretes online
1.15 El intérprete de Python
1.15.1 Estructura del intérprete de Python
1.16 Implementaciones de Python
1.16.1 CPython
1.16.2 Jython
1.16.3 PyPy
1.16.4 IronPython
1.17 Desarrollar programas en Python (IDE)
1.17.1 Entornos de desarrollo open source
1.17.2 Entornos de desarrollo de código cerrado
1.17.3 Comentario general sobre IDE
1.18 Primeros programas en Python
1.18.1 Ejecutar programas en el REPL de Python
1.18.2 Usar Jupyter Notebook
1.18.3 Primeros programas ejecutados desde ficheros
CAPÍTULO 2 VARIABLES Y TIPOS DE DATOS
1 Introducción a los tipos de datos
2 Literales, variables y datos en Python
2.1 Literales
2.2 Variables e identificadores
2.3 Gestión de memoria en Python
2.4 Mutabilidad de variables
3 Tipos booleanos
3.1 Operaciones con booleanos
3.2 Cortocircuito lógico
4 Comparaciones
5 Tipos numéricos
5.1 Operaciones numéricas básicas
5.2 Enteros
5.3 Operaciones a nivel de bits con enteros
5.4 Números de coma flotante
5.5 Números complejos
5.6 Utilizar distintos tipos numéricos
6 Secuencias
6.1 Listas
6.2 Tuplas
6.3 Rangos
6.4 Selección de subsecuencias basadas en índices (slices)
6.5 Operaciones predefinidas para secuencias
7 Secuencias de caracteres
7.1 Construir cadenas de caracteres
7.2 Convertir caracteres a números y viceversa
7.3 Operaciones de búsqueda de caracteres y conteo
7.4 Operaciones relacionadas con el tamaño de letra
7.5 Operaciones de identificación de cadenas
7.6 Operaciones relacionadas con la codificación
7.7 Traducciones, reemplazos y mapeos
7.8 Funciones de manipulación de cadenas: limpiado, división y unión de cadenas
7.9 Funciones de justificado y alineación de cadenas de caracteres
7.10 Formatear cadenas de caracteres
7.11 Diferentes subtipos de cadenas
7.12 Cadenas f-string en profundidad
7.13 Introducción al minilenguaje de formateado de strings
7.14 Función para imprimir caracteres (print)
8 Secuencias binarias
8.1 Qué son los datos binarios
8.2 Tipos bytes y bytearray
8.3 Operaciones con bytes y bytearray
8.4 Cadenas de literales de bytes (byte literals)
9 Conjuntos (set y frozenset)
9.1 Funciones de actualización de conjuntos
9.2 Funciones para operar conjuntos
9.3 Operaciones condicionales para conjuntos
10 Mapas (diccionarios)
10.1 Explorar valores de diccionarios
10.2 Actualizar valores en diccionarios
10.3 Objetos de tipo vista en diccionarios (view objects)
11 Iterables e iteradores
11.1 Operadores para trabajar con iterables
12 Expresiones generadoras
12.1 Inicializar tipos por comprensión
12.2 Inicializar objetos con expresiones generadoras
CAPÍTULO 3 FUNDAMENTOS DEL LENGUAJE
1 Asignaciones simples y múltiples
1.1 Asignaciones simples
1.2 Asignaciones de múltiples variables
1.3 Asignaciones múltiples
1.4 Asignaciones slicing con *
2 Control de flujo de ejecución
2.1 Control de flujo condicional con las sentencias if, elif y else
2.2 Implementaciones de switch case en Python
2.3 Sentencia if ternaria
3 Flujo de ejecución con bucles
3.1 Analizando bucles while
3.2 Usar bucles con sentencia for
3.3 Control de flujo dentro de bucles: break y continue
4 Operador walrus para asignar
5 Funciones en Python.
5.1 Ejemplo de modularización de código
5.2 Parámetros y argumentos en funciones
5.3 Uso de args y kwargs
5.4 Anotaciones y tipado en las funciones
5.5 Funciones recursivas
5.6 Funciones anónimas: expresiones lambda
5.7 Funciones de orden superior
5.8 Funciones dentro de funciones
5.9 Espacio de nombres y contextos
5.10 Memoización
5.11 Decoradores
5.12 Documentación de funciones
5.13 Funciones generadoras
5.14 Corrutinas
5.15 Funciones asíncronas
6 Excepciones
6.1 Controlar el flujo de ejecución con excepciones
6.2 Utilizar las trazas de error
6.3 Excepciones conocidas
6.4 Elevar excepciones de forma manual
6.5 Definición de excepciones propias
CAPÍTULO 4 PROGRAMACIÓN ORIENTADA A OBJETOS
1 Definición de clase
2 Atributos
2.1 Inicializar clases
2.2 Operar con los atributos
2.3 Atributos de clases
3 Nombres y privacidad en clases
4 Construcción de clases personalizadas
5 Propiedades en clases
6 Métodos
6.1 Métodos de instancia
6.2 Métodos de clase
6.3 Métodos estáticos
7 Métodos mágicos
7.1 Métodos para usar operaciones matemáticas
7.2 Emular contenedores
7.3 Personalizar el acceso a los atributos
7.4 Información sobre funciones definidas por el usuario
8 Controlar el espacio de atributos con slots
9 Duck typing o polimorfismo
10 namedtuple
11 dataclasses
12 Herencia
12.1 Herencia simple y el uso de super
12.2 Herencia múltiple
12.3 Clases Mixin
13 Metaclases y type
13.1 Creación de metaclases propias
14 Entonces, ¿cuándo se deben usar clases?
CAPÍTULO 5 ESTRUCTURA DE CÓDIGO EN PYTHON
1 Diferentes componentes de un módulo
2 Estructura básica de los paquetes
2.1 Importación de código Python
2.2 Potenciales problemas de usar import *
2.3 Evitar problemas al importar con importaciones cíclicas
2.4 Importar contenido en __init__.py
3 Repositorios de paquetes
3.1 Estructura de un paquete de Python
4 Paquetes disponibles en la librería estándar
CAPÍTULO 6 PERSISTENCIA DE DATOS EN FICHEROS
1 Ficheros de texto plano
1.1 Trabajar con ficheros sin estructura
1.2 Trabajar con ficheros de anchura definida
1.3 Trabajar con ficheros en formato CSV y TSV
1.4 XML
1.5 HTML
1.6 JSON
1.7 YAML
1.8 Librerías con todo incluido - tablib y pandas
2 Ficheros binarios
2.1 Serialización de objetos Python - pickle
2.2 Persistiendo diccionarios - shelve
3 Compresión y archivación de ficheros
3.1 Comprimir ficheros en archivos ZIP - zipfile
3.2 Archivación y compresión de ficheros - tarfile
CAPÍTULO 7 PERSISTENCIA EN BASES DE DATOS
1 Interfaz para trabajar con bases de datos DB-API
1.1 Funciones básicas para todos los conectores
1.2 Capa de abstracción de bases de datos en Python – pydal
1.3 Librería para consultas SQL en crudo – records
2 Bases de datos relacionales o SQL
2.1 Conceptos básicos de las bases de datos relacionales
2.2 Sentencias básicas en SQL
2.3 SQLite y Python
2.4 Diferentes bases de datos profesionales
3 Mapeo de objetos relacionales – ORM
3.1 SQLAlchemy ORM
3.2 Peewee ORM
3.3 Pony ORM
3.4 Django models
3.5 ORM asíncrono GINO
4 Bases de datos NoSQL
4.1 Bases de datos clave-valor
4.2 Bases de datos orientadas a documentos
4.3 Bases de datos en tiempo real
4.4 Bases de datos para series temporales
4.5 Bases de datos orientadas a grafos de datos
4.6 Bases de datos columnares
4.7 Bases de datos para búsquedas de texto - Full-text databases
5 Sistemas de clave-valor y cachés en Python - dbm, Memcached y Redis
5.1 Usar dbm en Python
5.2 Memcached en Python
5.3 Usar Redis como base de datos y caché en Python
6 Bases de datos en la nube
6.1 Servicios de bases de datos en la nube
CAPÍTULO 8 PARALELISMO Y CONCURRENCIA
1 Procesos en Python
1.1 Lanzar procesos externos - subprocess
1.2 Múltiples procesos en Python - multiprocessing
2 Hilos en Python
2.1 GIL en CPython
2.2 Hilos en Python - threading
3 Ejecuciones de hilos y de procesos - concurrent futures
4 Asincronía de entrada/salida – Asynchronous I/O
4.1 AsyncIO en Python
CAPÍTULO 9 INTERFACES DE USUARIO
1 Interfaz con consola de comandos en Python – CUI
1.1 input y print – E/S estándar
1.2 CLI estándar - argsparse
1.3 Interfaces de usuario basadas en texto
2 Interfaces gráficas de usuario - GUI
3 Tkinter a fondo
3.1 Componentes principales de una aplicación tkinter
3.2 Componentes disponibles
3.3 Disposición y propiedades de elementos en tkinter
3.4 Manejo de eventos en tkinter
3.5 Organización de la aplicación
4 Ejemplos de aplicaciones creadas con tkinter
4.1 Calculadora de porcentajes creada con tkinter
4.2 Analizador de ficheros de texto creado con tkinter
CAPÍTULO 10 SERVICIOS DE RED Y APLICACIONES WEB
1 Protocolos de Internet y Python
1.1 Transferencia de hipertexto - HTTP y HTTPS
1.2 Transmisión de ficheros – FTP
1.3 Conexiones entre servidores – Telnet
1.4 Conexiones seguras entre servidores – SSH y SFTP
1.5 Correo electrónico – SMTP
2 Desarrollo web
2.1 Participantes en la web
2.2 Trabajando con protocolo HTTP (request-response)
2.3 Estructura de las aplicaciones web en Python
2.4 Servidor simple utilizando http.server
2.5 Frameworks web en Python
3 Desarrollando un blog con Django
3.1 Planteamiento de la aplicación a realizar
3.2 Primeros pasos con Django
3.3 Primera aplicación con Django
3.4 Modelos con Django
3.5 Panel de administración de Django
3.6 Añadiendo contenido por defecto
3.7 Desarrollando la lógica de la aplicación
3.8 Renderizado basado en plantillas
ANEXO A TESTEO Y COBERTURA DE APLICACIONES EN PYTHON
Capítulo 1 INTRODUCCIÓN AL LENGUAJE PYTHON
Hoy día, la tecnología se ha vuelto un elemento fundamental para todos los seres humanos. Existen dispositivos que ayudan a realizar casi todas las actividades cotidianas, desde teléfonos inteligentes que resuelven cualquier duda en cuestión de segundos a miniordenadores que pueden ser integrados en pacientes como una prótesis, pasando por cohetes espaciales capaces de enviar robots fuera de nuestro planeta y recopilar información constantemente durante años. Muchos de estos componentes tecnológicos son operados por un software encargado de velar por su correcto funcionamiento y por su ejecución óptima. Por tanto, el desarrollo de este software es un pilar muy importante en la evolución de la tecnología y de la humanidad en su conjunto, además de ser un ámbito en auge que, se prevé, seguirá creciendo y necesitando de más expertos en los próximos años.
El software se compone de algoritmos. Estos no son más que la sucesión de comandos, programados en un lenguaje entendible de alguna forma por los humanos, que son transformados a un lenguaje que las máquinas pueden procesar y ejecutar con rapidez. El desarrollo del software se hace utilizando lenguajes de programación que se definen con una gramática específica (similar a un lenguaje hablado por los seres humanos) que define los comandos y la lógica que debe aplicar el sistema en cuestión bajo unos parámetros específicos. Existen multitud de lenguajes de programación, que difieren en sus propósitos y características. Encontraremos desde lenguajes de alto nivel (muy parecidos a los lenguajes que utilizamos para comunicarnos entre humanos) hasta lenguajes muy próximos al código máquina, pasando por lenguajes con un propósito puramente educacional o creados por simple diversión para los desarrolladores.
En este libro se darán las bases y los conceptos principales de uno de los mejores lenguajes de programación que existe hasta el momento, además de ser uno de los que se encuentra en mayor auge: el lenguaje de programación Python.
1 INTRODUCCIÓN
Corría el año 1989 cuando un joven holandés de 24 años comenzó sus primeras implementaciones del lenguaje de programación que hoy conocemos como Python. Este joven era Guido van Rossum y trabajaba para la empresa Centrum Wiskunde & Informatica (CWI). En su tiempo libre, no paraba de pensar en cómo mejorar la interfaz de usuario que utilizaban para trabajar con el sistema operativo Amoeba. Con esta idea en mente, nació el lenguaje de programación Python, que en principio iba a ser un proyecto pequeño de un lenguaje de programación que sucediera al lenguaje ABC que desarrollaban en CWI y que tuviera manejo de excepciones y ayudara a interactuar mejor con el sistema operativo. El nombre del lenguaje proviene de la afición que tenía Van Rossum a la serie de televisión Monty Python's Flying Circus y no de algo relacionado con el mundo de los reptiles.
La primera versión de Python fue lanzada en febrero de 1991 con el número de versión 0.9.0. En esta versión ya tenía componentes como clases con herencia, gestores de excepciones, funciones y tipos de datos como listas y diccionarios o cadenas de caracteres, con lo que desde sus inicios se veía su potencial frente a los demás lenguajes del momento (C++, Common Lisp o Perl).
Van Rossum ha sido el principal autor del lenguaje y quien ha dirigido el rumbo del mismo desde su inicio hasta que, en julio de 2018, decidió abandonar el cargo de benevolent dictator for life (BDL, dictador benevolente de por vida) para formar parte de una comisión directiva formada por cinco miembros.
1.1 EVOLUCIÓN DE LAS VERSIONES DE PYTHON
Durante los años que lleva en desarrollo, Python ha sufrido numerosos cambios y, hoy día, sigue recibiéndolos continuamente por medio de las PEP (siglas en inglés de Python Enhancement Proposals), que son propuestas que se crean en la comunidad de Python y que, de aprobarse, se aplican al código del lenguaje, a la documentación o a la parte específica que trate la PEP (se profundizará en esta cuestión más adelante).
A continuación, se nombran las principales versiones de Python con los cambios más destacados y sus fechas de lanzamiento:
• Versión 0.9 (febrero de 1991): primera versión de Python publicada por Van Rossum. Ya tenía componentes actuales como listas, diccionarios, clases y herencia, cadenas de caracteres y otras muchas características.
• Versión 1.0 (enero de 1994): se introdujo por primera vez la programación funcional y funciones como lambda, map o filter, entre otras.
• Versión 1.4 (octubre de 1996): añade los parámetros por clave-valor y los números complejos.
• Versión 1.6 (septiembre del 2000): se arreglan algunos errores y se añade una licencia compatible con GPL (GNU General Public License).
• Versión 2.0 (octubre del 2000): se añaden las listas por comprensión y el recolector de basura.
• Versión 2.1 (diciembre del 2001): se hacen cambios en el código para soportar ámbitos anidados y ámbitos estáticos. También cambia la licencia de nombre a Python Software Fundation License (PSFL). La Python Software Fundation se crea en ese mismo año como organización sin ánimo de lucro, organizadora y dueña, desde entonces, del código, documentación y especificaciones del lenguaje.
• Versión 2.2 (septiembre del 2006): se unifican los tipos de Python, escritos en C, y se añaden los conceptos de generadores.
• Versión 2.5 (septiembre del 2008): se introduce la cláusula with en el lenguaje, lo que permite encapsular bloques de código dentro de un administrador de contexto como se explicará más adelante en el libro.
• Versión 2.7 (julio del 2010): se añade OrderedDict a collections. Es la última versión de la rama 2.X y se incluyen algunas mejoras de la ya empezada a desarrollar versión 3.X. En noviembre de 2014 se anuncia que la versión 2.7 será la última de las versiones de la rama 2.X y que dejará de tener soporte a principios de 2020, invitando a todos los usuarios a migrar activamente a la versión 3.
• Versión 3.0 (diciembre del 2008): tras el lanzamiento de la versión 2.6, se realiza también el de la versión 3 (también llamada Python 3000), en la que, entre otras cosas, se hacen cambios en la parte principal del lenguaje, quitando redundancia de código e introduciendo grandes incompatibilidades con la versión 2.
• Versión 3.5 (septiembre del 2014): se añaden corrutinas con la sin-taxis de async y await. Además, se añaden una forma adicional de hacer las asignaciones en iteradores y la nueva librería para definir el tipado de variables: typing.
• Versión 3.6 (diciembre del 2016): aparece el concepto de f-string para ayudar al formateado de cadenas de caracteres, se añaden las anotaciones de variables, se permite la generación de generadores asíncronos y se mejora notablemente la implementación de diccionarios.
• Versión 3.8 (octubre del 2019): introducción del operador walrus, se añaden los parámetros solo-posicionales usando /en las funciones y el soporte de = para los f-strings, para autodocumentar expresiones y ayudar a depurar.
• Versión 3.9 (octubre del 2020): se añade el paquete zoneinfo para facilitar el uso de zonas horarias en fechas, se añade el operador de unión (|) para diccionarios, se permite el uso de expresiones en decoradores, se añaden los métodos removesuffix y removeprefix para cadenas de caracteres, se permite usar tipos del builting para definir hints sin necesidad de importar la librería y se añade Annotated a typing para mejorar la integración de ambas, entre otros muchos cambios. Cabe destacar que en esta versión se han borrado muchas funciones que estaban presentes por retrocompatibilidad con la versión 2, y que en las siguientes versiones se borrarán más.
Para más información sobre cada una de las versiones y los cambios entre una y otra es recomendable revisar con frecuencia la web oficial de lenguaje de programación Python: https://www.python.org/doc/versions/.
1.2 CARACTERÍSTICAS PRINCIPALES DE LOS LENGUAJES DE PROGRAMACIÓN
Los humanos nos comunicamos por medio de un lenguaje (mayoritariamente verbal). De forma similar, para comunicarnos con las máquinas hemos diseñado diferentes formas de comunicación denominadas lenguajes de programación. Al igual que los lenguajes utilizados entre humanos, los lenguajes de programación tienen diferentes características; están orientados a satisfacer las necesidades por las que han sido creados y los gustos de sus creadores y desarrolladores principales. Las principales características por las que un lenguaje de programación se puede caracterizar son la generación a la que pertenece, el nivel de abstracción del lenguaje, el tipo de tipado de variables, los paradigmas de programación que soporta y el propósito que tiene el lenguaje, como se verá en los próximos apartados.
1.2.1 Generaciones y niveles de abstracción de los lenguajes de programación
Cada lenguaje de programación se puede clasificar como de bajo o de alto nivel, y en una de las tres generaciones principales de lenguajes. Sin embargo, algunas clasificaciones extienden las generaciones hasta cinco. A continuación, se explican cómo son y sus características.
• Lenguajes de primera generación: utilizan el código máquina (basado en el sistema binario) para su programación. Este tipo de sistema es muy dependiente del hardware donde se esté ejecutando el programa y es el lenguaje que presenta menor abstracción de programación. Cabe destacar que todos los lenguajes de más alto nivel acaban ejecutando código máquina, pero, dependiendo de su nivel de abstracción sobre el hardware, pertenecen a una categoría en concreto.
• Lenguajes de segunda generación: se denominan de bajo nivel, pero son la primera abstracción disponible a nivel de programación. Aunque se denominen de bajo nivel no quiere decir que sean menos potentes o que tengan menos capacidades que los de alto nivel, sino que requieren tener un mayor control al programar y conocer bien el hardware en el que se va a ejecutar el código. En esta categoría están los lenguajes específicos para programar los microcontroladores y el código Ensamblador.
• Lenguajes de tercera generación: añaden una capa de abstracción superior frente a la generación previa y pueden añadir estructuras de datos y variables complejas dentro del repertorio de funcionalidades del lenguaje en cuestión. A los lenguajes de programación de tercera generación y sucesivos se les denomina lenguajes de alto nivel, aunque algunos sigan necesitando realizar operaciones de bajo nivel como el manejo de memoria. A esta categoría pertenecen muchos de los lenguajes comúnmente utilizados y forman parte de muchos proyectos de software utilizados en la actualidad. Algunos ejemplos son: C, Fortran, C++, Java y JavaScript, entre otros.
• Lenguajes de cuarta generación: son los lenguajes que tienen un gran parecido al lenguaje humano. A esta categoría pertenecen los lenguajes usados comúnmente para programar bases de datos, como SQL, y los lenguajes de sintaxis amena y de alto nivel, como Perl, Ruby, PHP y Python, entre otros.
• Lenguajes de quinta generación: son lenguajes que disponen de herramientas visuales para su desarrollo o lenguajes de inteligencia artificial. En esta generación encontramos lenguajes como Prolog o Mercury.
1.2.2 Paradigmas
Un paradigma es una teoría o conjunto de teorías cuyo núcleo es aceptado por todos los integrantes que usan ese paradigma, y que sirve como modelo para resolver problemas utilizándolo. En el mundo del desarrollo de software un paradigma de programación indica el método y la forma en la que se deben estructurar y organizar las tareas que debe realizar el programa que se está desarrollando.
Python implementa múltiples paradigmas, lo que permite que se puedan resolver problemas utilizando enfoques diferentes y que el desarrollador pueda elegir el que más se ajuste al problema a resolver o a sus gustos personales. A continuación, se muestran los paradigmas disponibles en Python y cómo se define cada uno de ellos:
• Paradigma imperativo: se caracteriza por enfocarse en cómo se pretenden realizar los cálculos por medio de instrucciones secuenciales para formar algoritmos que resuelvan una tarea específica. El paradigma imperativo es el más utilizado en los lenguajes de programación, de forma única o en conjunción con otros paradigmas. Ejemplos cotidianos de este paradigma se pueden encontrar en las recetas de cocina o en cualquier guía paso a paso. Python soporta este paradigma por completo, al igual que lo hacen otros lenguajes, como C, BASIC, Pascal o Golang.
• Paradigma procedural: este paradigma es un derivado del paradigma imperativo, pero añade funcionalidades extra como la creación de procedimientos o funciones que pueden ser llamadas en las secuencias de código como si fueran instrucciones simples. De esta forma, los programas están organizados y modularizados en funciones específicas que no tienen por qué residir en el mismo fichero. Esto favorece la modularización y organización del código. En Python el uso de este paradigma ayuda mucho a la legibilidad del código, ya que hace que pueda leerse incluso sin saber programar (se asemeja en gran medida al lenguaje usado por los humanos de manera natural y permite obviar la complejidad subyacente de cada función).
• Paradigma orientado a objetos: este paradigma es uno de los más populares junto con el imperativo y se basa en encapsular las entidades principales del programa en objetos que pueden contener tanto datos como comportamiento (métodos para interactuar con otros datos o con otros objetos). Este paradigma es especialmente útil, puesto que favorece mucho la modularidad, el encapsulamiento, la reusabilidad y la escalabilidad de cualquier programa. Muchas implementaciones de este paradigma cuentan con la herencia de objetos. Otros lenguajes de programación conocidos que implementan este paradigma son Java, C++, Rust o JavaScript.
• Paradigma funcional: se basa en funciones matemáticas que se centran en los cambios de estado del programa por medio de la mutación de variables. Tiene su origen en el cálculo lambda (lambda calculus) y presenta características muy potentes, como la recursividad, el uso de funciones de orden superior o el uso de las funciones puras que definen que la misma función, con los mismos argumentos, siempre debe devolver el mismo resultado (evitando cualquier efecto colateral). Python no es considerado un lenguaje puramente funcional, ya que no dispone de todas las características necesarias, pero sí que implementa muchas de ellas y son de gran utilidad. Ejemplos de lenguajes puramente funcionales son Haskell o Miranda y otros híbridos son Scala, Lisp o OCaml.
1.2.3 Clasificación de lenguajes según su tipado
El tipado de las variables de un lenguaje es una de las piezas angulares en las que el lenguaje se desarrolla, ya que define la forma en la que las variables son accedidas, guardadas y modificadas. Dependiendo del tipado definido en el lenguaje, el compilador o intérprete que configure la ejecución del código en un sistema puede conocer cuánta memoria necesita reservar para cada variable y dónde colocarla incluso antes de ejecutar el programa.
Python presenta un tipado dinámico, lo que quiere decir que el tipo de las variables se asigna en tiempo de ejecución del programa y no cuando se está compilando. Esta característica ayuda a que se puedan desarrollar programas utilizando Python de forma muy rápida, aunque puede generar algunos problemas si no se utiliza con cuidado.
Por otro lado, Python es un lenguaje fuertemente tipado, lo que quiere decir que, una vez definido el tipo de una variable, esta siempre actuará conforme a su tipo. Así, puede que la realización de operaciones no sea compatible con otras variables de tipos distintos, dado que no se hace un cambio de tipo en las variables de forma implícita. Otros lenguajes de programación, al sumar el carácter 1 y el número 1, devolverían como resultado el número 2, mientras que Python elevaría una excepción porque la función de suma de enteros no soporta la suma de enteros con cadenas de caracteres.
1.2.4 Características de Python
Resumiendo lo visto en los apartados anteriores, Python es un lenguaje de alto nivel, de propósito general, muliparadigma, principalmente imperativo, orientado a objetos y funcional, de tipado dinámico y fuertemente tipado a nivel de lenguaje de programación, pero en este apartado entraremos en profundidad en otros aspectos importantes del lenguaje.
La gramática de Python es una de las características más representativas, dado que hace que el lenguaje posea una sintaxis sencilla, simple y clara que permite al programador desarrollar programas de forma intuitiva. Esta característica hace que leer un programa escrito en Python sea muy parecido a leer un texto anglosajón y que sea un lenguaje muy bueno para aprender, entender y recordar. Por lo general, los programadores que utilizan Python rara vez necesitan revisar el manual de cada librería, dado que el lenguaje está diseñado para ser autodescriptivo en muchos aspectos.
Algunos ejemplos del espíritu innovador de Python que motivan su claridad y su simplicidad son los siguientes:
• Los bloques lógicos se definen utilizando indentación en vez de utilizar caracteres de apertura y cierre de bloque como "{}" (usado en lenguajes como Java, JavaScript o C).
• Las expresiones simples no necesitan el uso de paréntesis como pasa en lenguajes como JavaScript.
• Para la separación de instrucciones se utiliza el salto de línea en vez del comúnmente utilizado carácter ";", aunque también permite utilizarlo.
• Posee un sistema de recolección de basura que permite que el desarrollador se despreocupe de la gestión de memoria y que el lenguaje se pueda centrar en otros aspectos de alto nivel.
Cuando un código escrito en Python hace uso de buenas prácticas de programación y aprovecha las mejores partes del lenguaje y sus fortalezas, se denomina código pythónico y suele ser una señal de excelencia, dado que se percibe el dominio que el desarrollador tiene del lenguaje y marca la diferencia respecto a otros lenguajes de programación. Todos los desarrolladores que usan Python deberían enfocar sus esfuerzos en escribir código pythónico.
Python es un lenguaje interpretado, lo que significa que no es necesario compilar los programas cada vez que se hace un cambio en el código, por pequeño que este sea. Esto presenta una gran ventaja frente a los lenguajes compilados (como C o C++, por ejemplo) y aumenta muchísimo la velocidad de desarrollo de aplicaciones. Por otro lado, el ser un lenguaje interpretado permite que el código no sea dependiente del hardware en el que se ejecuta, y ayuda a que el lenguaje sea multiplataforma gracias al uso de su máquina virtual.
En multitud de definiciones se habla de Python como un lenguaje con "las baterías incluidas" (the bateries-included). Esto hace referencia a que posee multitud de herramientas en la librería estándar que ayudan a realizar un sinfín de aplicaciones sin necesidad de utilizar otros lenguajes de programación. Y es que Python cuenta con una gran y extensa librería estándar que es complementada por multitud de librerías de terceros o por aplicaciones que los contribuidores desarrollan día tras día usando Python en todas las áreas de conocimiento.
Sin embargo, Python también permite la integración con otros lenguajes de programación, ya sea importando código Python dentro de otros lenguajes o permitiendo ejecutar código de otros lenguajes en Python, lo que permite aumentar el ámbito de uso del lenguaje. Así, podemos tener código Python ejecutando código C, C++, .Net o Java, y viceversa. Usando diferentes técnicas, el código Python se puede transcompilar en otro lenguaje (como JavaScript) u otros lenguajes pueden ejecutar código Python haciendo uso de subprocesos u otras técnicas.
Al igual que cualquier otro lenguaje de programación, Python también tiene puntos débiles. Uno de los más importantes suele ser que se considera "lento" en comparación con los lenguajes compilados, principalmente por ser un lenguaje interpretado y no poseer por defecto un compilador JIT (del inglés Just-in-Time), lo que haría que se compilase el programa escrito en Python y optimizasen más los tipos de datos. Aun así, en Python 3 se han hecho muchas mejoras de rendimiento de los tipos de datos y se ha mejorado notablemente este aspecto. También existen librerías, como Numba, que permiten fácilmente marcar porciones de código para ser compiladas en tiempo de ejecución (usando un JIT), o CPython, que permite escribir código C compatible con Python e integrarlo de forma natural para mejorar mucho la velocidad de procesamiento.
En otras situaciones, para obtener mejores rendimientos, se ha optado por crear una librería escrita en un lenguaje compilado para aprovechar su velocidad y hacer la interfaz, que se usará después desde un código Python estándar. Este es, por ejemplo, el caso de Pandas, que permite el uso de operaciones numéricas y científicas sobre grandes volúmenes de datos de forma muy eficiente. Está escrita, en su mayoría, en código C, pero, al mimso tiempo, es totalmente transparente para el programador de Python, ya que tiene la misma sintaxis que cualquier otro módulo Python estándar.
Otra opción sería hacer tipos de datos altamente eficientes, como los que presenta la librería collections, que permite utilizar tipos de datos optimizados para ser ejecutados y usados con código Python estándar.
A más alto nivel cabe destacar que todo el código de Python es código libre y gratuito, con una comunidad que le da soporte y mejora continuamente. Es un lenguaje multiplataforma, lo que permite que se pueda ejecutar y programar en multitud de plataformas, desde los sistemas operativos más tradicionales de ordenadores personales, como Windows, Linux o Mac OS X, hasta dispositivos electrónicos más exóticos como teléfonos o relojes inteligentes, pasando por videoconsolas.
1.3 ÁMBITOS DE USO DE PYTHON
Python es un lenguaje de propósito general, por lo que se utiliza en multitud de ámbitos de la informática. Puesto que está en desarrollo constante, se va adaptando a los nuevos retos y va evolucionando para proporcionar nuevas soluciones.
A continuación, se hará una introducción a los ámbitos fundamentales que cubren el uso de Python, enumerando las librerías más utilizadas y ejemplos de las aplicaciones que se realizan con ellas. Cada ámbito que se presenta es muy extenso y se invita al lector a que se informe sobre cada tema específico con libros que estudien cada parte en profundidad.
1.3.1 Programación a nivel de sistema operativo
Python permite interactuar fácilmente con el sistema operativo, facilitando así la creación de scripts (pequeños programas con un propósito específico), programas completos o utilidades para resolver tareas sobre el sistema operativo, como lanzamiento de comandos del sistema, ejecución de otros programas, manejo de ficheros, interacción con sockets del sistema, etc. Gracias a la portabilidad del lenguaje, estos programas se desarrollan solo una vez, pero pueden ser ejecutados en diferentes sistemas operativos sin necesidad de cambiarlos.
1.3.2 Aplicaciones con interfaz de usuario
Gracias a la librería estándar de Python se pueden crear aplicaciones con interacción de usuario de diferentes formas, tanto usando la línea de comandos como utilizando interfaces gráficas. Hay varias librerías destinadas a facilitar la tarea de hacer CLI (command line interface; interfaz de línea de comandos) y crear programas con menú para que el usuario pueda realizar diferentes acciones. Dos de las opciones más utilizadas son: argparse (https://docs.python.org/3/library/argparse.html) y click (https://click.palletsprojects.com/).
Por otro lado, con el fin de hacer aplicaciones más amenas para el usuario y mejorar la experiencia de uso, Python permite crear aplicaciones multiplataforma y de escritorio que se integran perfectamente con la apariencia del sistema operativo donde se ejecutan. La librería básica para este cometido es Tkinter (https://wiki.python.org/moin/TkInter), la cual permite crear aplicaciones de escritorio de primer nivel y es el estándar de facto en este ámbito, aunque también existen librerías de más alto nivel para poder desarrollar aplicaciones más avanzadas integradas con GTK, como PyGTK (https://python-gtk-3-tutorial.readthedocs.io/), o con Qt, como PyQT (https://wiki.python.org/moin/PyQt), entre otras, como se estudiará más adelante.
Una librería muy utilizada para la creación de interfaces de usuario para la línea de comandos es ncurses (https://invisible-island.net/ncurses/ncurses.faq.html), la cual está escrita en C pero es accesible desde la librería de Python curses (https://docs.python.org/3/howto/curses.html). Otra, puramente en Python, es urwid (http://urwid.org/). Ambas permiten crear interfaces de usuario para la consola de comandos algo más amenas que los simples CLI.
1.3.3 Aplicaciones web e interacción con servicios web
Uno de los pilares más importantes dentro de las aplicaciones que se pueden desarrollar utilizando Python son las aplicaciones web en todo su amplio abanico, desde las aplicaciones más simples, como páginas web estáticas generadas en Python usando Pelican (https://blog.getpelican.com/), hasta juegos generados en JavaScript utilizando la librería de Python Pyjamas (https://wiki.python.org/moin/Pyjamas), que convierte código Python en código JavaScript.
Dentro de las aplicaciones web existen múltiples frameworks para el desarrollo de aplicaciones, orientados a crear aplicaciones tanto de propósito general como específico. Así, el desarrollador puede elegir la herramienta que más se ajuste a sus necesidades y al proyecto que se pretenda desarrollar.
Existen frameworks de propósito general, como Django (https://www.djangoproject.com/), que es uno de los líderes en el ámbito del desarrollo web gracias a sus múltiples funcionalidades: su facilidad de uso con bases de datos relacionales gracias a su propio ORM (object relational mapping), el cual permite hacer un uso extremadamente sencillo para el manejo de tablas e información guardados en diferentes bases de datos soportadas; su sistema de migraciones de datos; el sistema de autenticación; el enrutador propio de URL; soporte de múltiples motores de renderizado, y un largo etcétera, que hacen que este framework pueda competir con cualquier framework de propósito general de cualquier otro lenguaje, por ejemplo, Java, aparte de poder ser extendido por medio del uso de multitud de aplicaciones de terceros fácilmente integrables y que se amplían día a día.
Por otro lado, existen los microframeworks de carácter general, que están orientados a ofrecer al desarrollador una funcionalidad básica para favorecer la posibilidad de escalar las aplicaciones y ser extendidas con plugins o aplicaciones de terceros fácilmente. En este segmento destacan frameworks como Flask (https://palletsprojects.com/p/flask/) o Pyramid (https://trypyramid.com/).
Por último, destacan los frameworks diseñados para un uso específico, los cuales se han optimizado para mejorar en un aspecto concreto. Por ejemplo, existe el framework Falcon (https://falcon.readthedocs.io/en/stable/), que está diseñado para poder gestionar muchísimas peticiones por segundo. Da unos resultados muy superiores (en cuanto al manejo de peticiones por segundo) comparado con cualquiera de los frameworks mencionados anteriormente, dado que se diseñó para ser excepcional en ese aspecto.
Otro ejemplo de framework orientado a un propósito específico es Starlette (https://www.starlette.io/), un framework orientado a aplicaciones en tiempo real que se caracteriza por la cantidad de peticiones concurrentes que soporta. Puede gestionar hasta decenas de miles de peticiones por segundo.
1.3.4 Interacción con servicios de Internet
Aunque el desarrollo web sea un pilar muy importante, Python no solamente se centra en poder ayudar en el desarrollo de las mismas, sino que también se puede integrar fácilmente con servicios de terceros de múltiples formas, sobre todo mediante el uso de API (application programming interface; interfaz de aplicaciones programables). Estas sirven como interfaz entre aplicaciones web y permiten, desde un código escrito en Python, intercambiar información con servicios de los principales proveedores.
Un claro ejemplo serían las API de servicios de Google, que permiten conectarse con los usuarios de sus productos (Google Maps, YouTube, Google Ads…) de forma fácil e intuitiva por medio del uso de sus API desde Python o Telegram. Así, se pueden crear clientes de este sistema de mensajería en cuestión de minutos. Existen otros muchos servicios con API compatibles con Python que proveen hasta programas de ejemplo en Python para simplemente descargar y usar.
Otra forma de interactuar con servicios de Internet es la transferencia de archivos usando FTP (file transfer protocol; protocolo de transferencia de archivos), con el que se pueden compartir archivos a través de Internet. Python tiene la librería ftplib (https://docs.python.org/3/library/ftplib.html), la cual permite el uso de este servicio de forma intuitiva y profesional.
Por otro lado, Python soporta de forma nativa el envío de emails de manera fácil y sencilla, haciendo uso de las librerías smtplib y email, con las que el envío de emails se hace simplemente con algunas líneas de código, como se puede ver aquí: https://docs.python.org/3/library/email.examples.html.
1.3.5 Gestión de contenido
A más alto nivel se encuentran las aplicaciones para la gestión de contenidos, tanto web como de escritorio.
En la parte de particulares podemos destacar los CMS (content management system; sistemas de gestión de contenido), que ayudan a gestionar contenido como páginas web, blogs, subscripciones, emails, etc. En esta categoría destaca el framework de software libre Django-CMS (https://github.com/divio/django-cms), que no solo es una aplicación para ayudar a hacer la tarea de generar contenido web intuitiva y fácil, sino que está construida encima del framework Django, por lo que permite extender cualquier aplicación de forma sencilla.
Un caso práctico sería el de la creación de un blog con alguna página estática que después de un tiempo se quiere extender añadiendo funcionalidades propias, que requieren un desarrollo personalizado. Con Django-CMS se podría tener la aplicación completa utilizando el mismo framework. Cabe destacar que Django-CMS es usado por empresas de primer nivel, como NASA, Canonical o National Geographic, entre otras, para la gestión de su contenido.
En la parte de empresas destacan los ERP (enterprise resources planning; sistema de planificación de recursos empresariales), que son aplicaciones que ayudan a la gestión de empresas en sus diferentes áreas, como contabilidad, facturación, gestión de inventarios, gestión de pedidos, gestión de relación con los clientes, etc.
Uno de los más populares ERP de software libre escritos en Python es Odoo (https://github.com/odoo/odoo), que cuenta con varios módulos diferentes, como CRM para la gestión de relaciones con los clientes, un creador de sitios web, un creador de comercios en línea, un creador de infoproductos o de cursos online y otras muchas características que, al unirlas, forman un ERP muy potente capaz de competir con otros tan conocidos en este segmento como SAGE.
1.3.6 Aplicaciones científicas y manejo de datos
Python ya es un referente en la comunidad científica y en el manejo de datos a gran escala dada su simpleza y su claridad a la hora de programarlo. Esto hace que su barrera de entrada sea muy baja y cada vez más científicos den el salto a usarlo.
En el ámbito científico se pueden definir diferentes áreas en las que encontrar soluciones implementadas en Python para categorías como manejo de datos, realización de cálculos y visualización de los datos.
Para el manejo de datos se dispone de dos librerías principales, NumPy y Pandas, las cuales permiten manipular grandes cantidades de datos y hacer operaciones masivas sobre los mismos, como aplicar funciones a vectores de datos o hacer operaciones con matrices de forma simple y altamente eficiente.
NumPy (https://numpy.org/) es un paquete fundamental que contiene herramientas para interactuar con vectores de N dimensiones y realizar operaciones matemáticas sobre ellos. Además, tiene integración con código C++ o Fortran, y otras muchas utilidades relacionadas con el manejo de datos científicos, como operaciones de álgebra lineal, transformada de Fourier, etc. NumPy está orientado a operar de forma altamente eficiente pero enfocada a operaciones con vectores N dimensionales.
Pandas (https://pandas.pydata.org/) es una librería especializada en realizar operaciones altamente eficientes sobre conjuntos de datos grandes de varias dimensiones, organizados en datasets y permitiendo manipularlos de forma simple.
Tanto Pandas como NumPy forman parte del proyecto SciPy (https://www.scipy.org/), el cual es un ecosistema basado en Python para el uso científico.
Aunque se ha visto cómo se puede interactuar con los datos, es importante disponer de una herramienta potente y fácil de usar para realizar pruebas y cálculos. En este segmento destaca la herramienta Jupyter Notebook (https://jupyter.org/), la cual permite interactuar continuamente con los datos que se están analizando, además de poder exportar los resultados, compartirlos, evaluar solamente parte de los cálculos y un largo etcétera de funcionalidades que ayudan enormemente al desarrollo de ideas científicas. Además, dispone de versión online, y muchos servicios online soportan el formato para mejorar la visualización de documentos. Este es el caso de GitHub (https://github.com/), si se aloja el código en sus repositorios y detecta que el fichero que se quiere mostrar tiene la extensión de Jupyter Notebook, la interfaz automáticamente se actualiza para amoldarse a la vista natural de un notebook.
Una vez visto cómo se pueden manipular los datos y trabajar continuamente con ellos hasta conseguir los resultados del estudio científico, llega la parte de la visualización de datos. En este caso, Python permite realizar visualizaciones de datos avanzadas, e incluso interactivas, con librerías propias.
Una de las librerías fundamentales para la visualización de datos es Matplotlib (https://matplotlib.org/). Aunque es una de las librerías más básicas, es también la más utilizada en proyectos que necesitan realizar visualizaciones más avanzadas, dado que es altamente eficiente y contiene multitud de tipos de gráficos soportados, que van desde los gráficos de líneas hasta los gráficos de puntos o mapas de calor, pasando por gráficos de barras.
Para la representación estadística se hace uso de la librería Seaborn (https://seaborn.pydata.org/), la cual usa Matplotlib, pero añade nuevos tipos de visualizaciones más orientados a la estadística, como gráficos que soportan rangos de errores y una interfaz de alto nivel para hacer el uso intuitivo.
Por último, cabe destacar la librería Bokeh (https://bokeh.org/), la cual no solo permite mostrar gráficos tanto en dos dimensiones como en tres dimensiones, sino hacer interacciones con ellos, permitiendo a los usuarios ver de forma atractiva e interactiva los datos representados.
Cabe mencionar que existen distribuciones de paquetes científicos que unen muchas de las herramientas que se han expuesto en este apartado en un solo paquete de software. Así, la instalación será una sola, en vez de instalar cada herramienta por separado.
Un ejemplo de distribución científica es Anaconda (https://www.anaconda.com/), que, gracias a sus herramientas añadidas, comprende desde el manejo simple de datos con Jupyter Notebook hasta el visualizado de datos con Bokeh o Matplotlib, pasando por herramientas de inteligencia artificial como Scikit-learn o TensorFlow. Utiliza librerías como NumPy y Pandas y gestiona las dependencias de paquetes con Conda (https://docs.conda.io/en/latest/). Anaconda es una distribución completa que contiene todas las herramientas necesarias para la mayoría de ámbitos de la ciencia, la investigación y la ingeniería de datos, y es utilizada para instalar todos los componentes de una vez y bajo el mismo entorno.
1.3.7 Inteligencia artificial y Python
La inteligencia artificial ha sido un tema de interés muy popular desde que se empezó a investigar a finales de los años 30 hasta que se consiguió crear la primera red neuronal artificial en 1951, la famosa SNARC (Stochastic Neural Analog Reinforcement Calculator). Desde entonces, este campo de la ingeniería informática ha evolucionado de forma trepidante, aunque se relajó un poco cuando paró la financiación para este tipo de proyectos entre los años 80 y el 2011. Hoy día es una parte fundamental en el desarrollo de aplicaciones y está totalmente de moda gracias a las tres partes fundamentales en las que se desarrolla en la actualidad: big data, inteligencia artificial general y deep learning.
El término big data hace referencia a la manipulación de grandes cantidades de datos. En Python se realiza haciendo uso de las herramientas comentadas en el apartado 3.6, cuando se habla del manejo de datos utilizando herramientas que permitan la obtención de los mismos y el guardado eficiente, como Apache Spark (https://spark.apache.org/) y Hadoop (https://hadoop.apache.org/), o Dask (https://dask.org/), que permite la ejecución paralela del mismo código en diferentes máquinas y, en caso de ser necesario, escalar las mismas, lo que ayuda a agilizar la ejecución de los cálculos científicos.
La rama de las ciencias de la computación especializada en inteligencia artificial general intenta entender la inteligencia humana y desarrollar algoritmos para que las computadoras puedan tener comportamientos similares a los humanos. En este aspecto, los ejemplos más clásicos podrían ser: la categorización de imágenes, el reconocimiento de patrones de habla, el procesamiento de lenguaje natural y la robótica, entre otros. En Python existe una gran variedad de herramientas que ayudan a desarrollar aplicaciones en cada aspecto, pero cabe destacar la librería Scikit-learn (https://scikit-learn.org/), la cual permite hacer clasificación de objetos en categorías, predicciones basadas en eventos pasados, reprocesamiento de datos y otras muchas herramientas muy útiles en este campo.
El aprendizaje profundo (o deep learning) se centra en el desarrollo de algoritmos de aprendizaje automático para analizar datos expresados de forma matricial o tensorial mediante capas de redes neuronales. Son especialmente útiles porque no necesitan, en la mayoría de los casos, la intervención humana para su aprendizaje. El software más destacado en este campo es TensorFlow (https://www.tensorflow.org/), que es una librería desarrollada por Google como parte de sus herramientas principales y abierta como código abierto para el público general. Aunque TensorFlow es la herramienta principalmente utilizada, existen otras como CNTK (https://docs.microsoft.com/es-es/cognitive-toolkit/), de Microsoft, o Theano (https://pypi.org/project/Theano/). Estos tres frameworks pueden ser utilizados fácilmente por la librería de Python Keras (https://keras.io/), la cual permite la integración y utilización de cualquiera de las tres herramientas de forma fácil.
1.4 PYTHON ENHANCEMENT PROPOSALS (PEP)
El desarrollo del núcleo de Python se lleva a cabo gracias a los core-developers ("desarrolladores principales" del lenguaje) del código, los cuales forman parte de una comunidad diversa y abierta que permite que cualquiera pueda aportar ideas y ayude a hacer evolucionar el lenguaje.
Por este motivo, el desarrollo de Python se hace por medio de propuestas de cambio que se denominan Python Enhancement Proposals ("propuestas de mejora de Python" o PEP) y se pueden encontrar en el repositorio de propuestas https://github.com/python/peps. Así quedan públicamente marcados los cambios realizados en cada propuesta y la evolución de la misma. Existen tres tipos de propuestas:
• Propuesta estándar: describe una nueva funcionalidad o implementación para la librería estándar de Python.
• Propuesta informacional: describe un problema de diseño, una guía general o información general para la comunidad de Python, pero no propone ninguna nueva funcionalidad. Este tipo de propuestas no necesitan de consenso o recomendaciones.
• Propuestas de proceso: describen el proceso alrededor de Python. Son parecidas a las propuestas estándar, pero están enfocadas a áreas diferentes del lenguaje en sí y, por lo general, necesitan de consenso para llevarse a cabo.
Python evoluciona siguiendo las propuestas, y estas son una parte fundamental en el desarrollo del lenguaje. Cada día se crean propuestas nuevas, y su número va aumentando. Las propuestas disponen de un número entero que las identifica y, como no podía ser de otra forma, la documentación sobre cómo hacer una propuesta y la información sobre las propuestas se encuentra en la primera propuesta creada o PEP-1: https://www.python.org/dev/peps/pep-0001/.
1.4.1 Proceso de creación de una PEP
En el proceso de creación y desarrollo de una propuesta hay diferentes actores implicados, con distintos papeles y poderes que se describen en la PEP-13 (https://www.python.org/dev/peps/pep-0013/):
• Consejo directivo de Python: es un consejo elegido por la comunidad y su papel es ser la autoridad final a la hora de tomar una decisión sobre cualquier propuesta. El consejo puede aceptarla o rechazarla.
• Core Developers: son los desarrolladores que contribuyen activamente al desarrollo del núcleo de Python y la librería estándar.
• BDFL-Delegated: BDFL son las siglas en inglés de benevolent dictator for life (dictador benevolente de por vida). Guido Van Rosum ha sido esa figura hasta que renunció al título en 2018 para entrar en el consejo directivo. Esta figura es quien finalmente tiene la decisión de aceptar o rechazar una propuesta, aunque desde la constitución del consejo directivo suele ser el consejo quien determina este aspecto, y no una sola persona.
• Editores de la propuesta: son los editores que comienzan y dan forma a la propuesta.
El proceso general de una propuesta, de forma resumida, sería:
1. Se comienza con una idea, a poder ser lo más resumida y concreta posible, y se envía a los canales de la comunidad para ver si se rechazaría directamente por algún motivo (por ejemplo, que solamente afecta a una parte de los pythonistas o que ya está repetida). Los canales usuales son las listas de emails en python-list@python.org o en python-ideas@python.org. Si la recepción es buena, se puede comenzar con el borrador de la PEP y continuar con el proceso.
2. Cuando una propuesta quiere ser enviada, debe tener un patrocinador (sponsor), que idealmente debería ser un desarrollador del núcleo de Python, aunque no es un requisito indispensable. Se añaden todos los campos necesarios siguiendo la plantilla que se provee en la PEP-1 y se envía al repositorio de GitHub donde están todas las demás PEP con el número 9999. Este número indica que es una nueva PEP en proceso de creación.
3. Tras el envío de la propuesta se hacen varias revisiones hasta que se decide la resolución final. El nombre de la figura de BDFL-Delegated queda reflejado en la propuesta y, normalmente, se asigna un desarrollador que será el que desarrolle la propuesta durante el proceso, ya sea de forma voluntaria o por designación del consejo.
4. Los mantenimientos o futuras revisiones de las propuestas estándar no suelen realizarse, puesto que quedan claramente definidas una vez terminadas (se especifica también en qué punto han terminado). Las propuestas informacionales o de proceso, por el contrario, sí que pueden contener cambios durante el tiempo que se abordan, según la naturaleza de la propuesta.
1.5 PEP-8: GUÍA DE ESTILOS
El código de cualquier aplicación en cualquier lenguaje se lee muchas más veces de las que se escribe, por tanto, es importante tener claro cómo escribirlo, y será aún mejor si se pueden seguir unas reglas y una guía que todos los desarrolladores acepten, entiendan y compartan. Python tiene una propuesta específica donde se define la guía de estilo para los desarrolladores del lenguaje y por la que habría que guiarse si se pretende escribir código pythónico.
Esta guía es fácil de seguir y se encuentra totalmente documentada en la PEP-8 https://www.python.org/dev/peps/pep-0008/, por lo que se recomienda su lectura en profundidad, aunque en esta sección se verán muchos de los puntos más relevantes a tener en cuenta cuando se programa en Python.
1.5.1 Indentación
La indentación en Python es uno de los pilares principales del lenguaje, dado que define los bloques lógicos de código y la lógica entre los mismos; un simple cambio de indentación podría cambiar drásticamente la lógica de un programa, por lo que hay que tenerlos muy en cuenta y seguir las normas que se establecen para desarrollar correctamente.
En la guía oficial se establece la preferencia de indentación: utilizar espacios es mejor que utilizar tabulaciones, especialmente 4 espacios, aunque pueden prevalecer las tabulaciones en proyectos que ya las usen como indentación principal. Cuando se utilizan tabulaciones, el tamaño que toma en la pantalla cada indentación puede variar considerablemente dependiendo del editor o del tipo de letra, mientras que si se usan espacios, las diferencias entre fuentes suelen ser más sutiles. Cabe destacar que en Python 2 se podían tener diferentes caracteres de indentación en el código de espacios y de tabuladores en el mismo fichero, pero en Python 3 esto eleva un error de sintaxis, por lo que se recomienda usar siempre el mismo tipo (a poder ser, 4 espacios).
1.5.2 Longitud de líneas
La longitud de línea máxima es de 79 caracteres para el código y de 72 caracteres para los comentarios y la documentación. Esto favorece que los editores de texto con límite de ancho de línea de 80 caracteres muestren el código correctamente y sin necesidad de utilizar saltos de línea, ayudando también a poder abrir varios archivos en la misma ventana con facilidad.
Para equipos de desarrollo que realmente quieran utilizar una longitud mayor de línea y estén de acuerdo con aumentar el número de caracteres por línea, se permite llegar hasta un límite de 99 caracteres para el código, quedando el límite para comentarios y textos de documentación en el mismo que antes: 72 caracteres.
Cuando la longitud de una línea sea mayor que la cantidad máxima predefinida, se utilizará el carácter "\" para hacer un salto de línea y hacer que el texto continúe en la siguiente.
1.5.3 Espacios, saltos de línea y líneas en blanco
En Python se intenta tener solo el número mínimo necesario de espacios y líneas en blanco para permitir que el código se lea correctamente y de manera natural. Por tanto, es mejor evitar cualquier tipo de espacio añadido que parezca exagerado, como en los siguientes ejemplos.

Los saltos de línea se añaden antes que los operadores lógicos, dado que mejoran la legibilidad del código, y siempre deben estar alineados al mismo nivel, como en el siguiente ejemplo:

A la hora de añadir las líneas en blanco existen diferentes reglas:
• Las funciones generales o las clases deberán estar rodeadas de dos líneas en blanco.
• Los métodos definidos dentro de clases se rodean de solo una línea en blanco.
• Se pueden usar líneas en blanco de forma moderada entre bloques de funciones o entre secciones lógicas.
• Algunos editores de texto reconocen el control-L (^L) como separador de página, por tanto, puede ser usado para separar las partes de código.
1.5.4 Otros consejos generales
• Para las comparaciones de si un objeto es verdadero o falso, comprobando si existe el primer elemento de una lista o la longitud de una cadena, se recomienda utilizar las herramientas del lenguaje.

• La forma correcta de comprobar si una variable tiene el valor de True, False o None, es con el operador is o is not, intentando evitar el uso de negaciones de expresiones afirmativas.

• La importación de módulos se hará siempre al comienzo del fichero.
• El orden de importación de los módulos es:
− Módulos de la librería estándar.
− Módulos de terceros (otras librerías).
− Módulos propios.
− Nota: dentro de cada sección se mantendrá el orden alfabético.
1.5.5 Comentarios y documentación de código
PEP-8 es bastante explícito a la hora de definir las reglas de cómo deberían ser los comentarios del código:
• Es mucho mejor no tener comentarios que tener comentarios que contradigan el código.
• Los comentarios deben de estar compuestos de una o varias frases completas, terminar con un punto al final y comenzar con mayúsculas, a no ser que comiencen con un identificador que esté en minúscula, en ese caso, se respetará siempre la forma en que esté escrito el identificador.
• Para programadores de habla distinta a la inglesa, se ruega que escriban los comentarios en inglés a no ser que estén 120 % seguros de que cualquier otro desarrollador que lea el código sabrá su idioma.
• Los bloques de comentarios afectan al código que está indentado al mismo nivel que el bloque de comentarios, y entre párrafo y párrafo habrá que añadir dos líneas en blanco.
• Los comentarios de una sola línea deberán usarse de forma moderada, los añadidos tras la línea de código se harán añadiendo dos espacios, los caracteres "# " detrás de la línea en la que se desea añadir el comentario y solo se añadirán si el objetivo de la línea de código no es obvio en sí mismo.
• Para la documentación de código (docstrings) se utilizarán siempre los tres caracteres seguidos """ para abrir y cerrar el bloque de comentarios.
• Los módulos públicos, funciones, clases y métodos deben tener un docstring (documentación de código) y solo los métodos privados pueden no tenerlo, aunque deben tener un comentario justo después de la definición de la cabecera del mismo.
1.5.6 Convención de nombres
La convención de nombres establece cómo deben de nombrarse los identificadores de cada parte del lenguaje (variables, funciones, nombres de fichero, clases, etc.). PEP-8 define también las reglas para nombrar el código Python.
Antes de pasar a las reglas se exponen las convenciones más usadas:
• CamelCase: se construye uniendo palabras clave una detrás de otra, pero utilizando la primera letra de cada palabra en mayúscula. Su nombre hace referencia a que el resultado final se asemeja a la joroba de los camellos.
• snake_case: se construye uniendo las palabras con una barra baja (_) entre ellas.
• SCREAMING_SNAKE_CASE: se construye de forma similar a la snake_case, pero usa todos los caracteres en su forma mayúscula.
• Mayúsculas o minúsculas: son palabras en las que todas las letras son de uno u otro formato.
A continuación, se muestra cómo se usan estas convenciones de nombres en Python:
• Los nombres deberían estar definidos por el uso que se vaya a hacer de ellos y no por la implementación interna.
• Si un proyecto ya tiene una convención de nombres establecida, se mantendrá, dado que no hay que romper la consistencia del proyecto por seguir la guía de estilos.
• Para definir que un objeto está protegido se añade un carácter "_" como prefijo del nombre, dado que al hacer una importación general (utilizando el carácter *) se omiten los nombres escritos de esta forma.
• Los nombres que terminan con el carácter "_" se utilizan para evitar conflictos con palabras reservadas como class, def, if, etc.
• Cuando un nombre comienza por doble "_" (por ejemplo: __foo) dentro de cualquier clase (por ejemplo, Bar), se trata de manera diferente, dado que el compilador lo cambiará internamente por _<nombre_clase>__<nombre> (_Bar__foo en el ejemplo), lo que se conoce como Python's mangling rules y evita colisiones de nombres. Este método se asemeja a hacer un objeto privado, aunque realmente se puede acceder a él si se conocen las reglas, las clases y el nombre de la función. En cualquier caso, se recomienda encarecidamente no acceder a estos métodos utilizando este tipo de nombres.
• Si el nombre de la función comienza y termina con doble "_", se considera un método mágico y se recomienda que no se inventen nuevos nombres, sino que se usen los disponibles en la documentación.
• Para nombrar paquetes o módulos se utilizarán nombres en minúsculas o snake_case, intentando que sean lo más cortos posible.
• Para los nombres de las clases y los tipos de variables se usa CamelCase.
• El primer parámetro para los métodos de instancias y para los métodos de clase será self y cls, respectivamente.
• Para los nombres de las funciones se utilizará snake_case. Prefijo con un solo "_" si la función es local al módulo y prefijado con 2 caracteres "_" solo para evitar colisiones con la herencia al hacer uso de las Python's mangling rules.
• Para las constantes se hará uso de las mayúsculas o de SCREAMING_SNAKE_CASE.
1.5.7 Herramientas para cumplir con la PEP-8
Las recomendaciones son simples y fáciles de seguir, pero siempre es mejor poder disponer de un software que compruebe este tipo de estilos y alerte al usuario en caso de que alguna regla haya sido infringida y le sugiera cómo se podría arreglar.
Para este propósito, en Python existen analizadores de código que comprueban si se ha incumplido alguna de las reglas establecidas por la PEP-8 en un código. Suelen marcar qué regla ha sido infringida, la línea exacta y, en muchas ocasiones, ofrecen alternativas de código para que este cumpla con las reglas. El programa más utilizado es Pylint (https://www.pylint.org/), que no solamente comprueba si la sintaxis cumple con las reglas de PEP-8, sino que también alerta de posibles problemas en el código, ayuda a hacer refactorizaciones, se integra fácilmente con editores y es capaz de hacer diagramas UML que describen la estructura del código de una forma estándar.
Cuando se pretende comprobar si un módulo cumple las reglas de estilos, se puede utilizar el paquete pycodestyle (https://github.com/PyCQA/pycodestyle), el cual se puede ejecutar por consola para obtener un reporte detallado del estado del módulo analizado.
Cabe destacar que la mayoría de editores modernos tienen soporte para Pylint o disponen de su propio validador, que en la mayoría de ocasiones viene activado por defecto y, en algunos casos, puede aplicar los cambios sugeridos automáticamente, aunque es recomendable revisar ese tipo de cambios para no tener cambios de código no deseados.
1.6 PEP-20: ZEN DE PYTHON
Intentando crear una guía para resolver los conflictos de cómo se debería escribir código Python, en el año 1999, Patrick Phalen pidió a Tim Peters y a Guido van Rossum en la lista general de Python (python-list@python.org) que hicieran una lista de unas 20 reglas que todo pythonista debería seguir para discernir la forma en la que habría que proceder ante un conflicto a la hora de escribir código pythónico.
El pythonista que tomó la iniciativa y propuso las primeras 19 fue Tim Peters. Dejó espacio para que Van Rossum añadiese una más y completase hasta 20 las reglas, pero hasta la fecha esto no ha ocurrido (ni se espera que ocurra).
La PEP que recoge esta lista de reglas fue bautizada como PEP-20 y es accesible tanto por el método habitual (en la página web oficial, https://www.python.org/dev/peps/pep-0020/) como haciendo uso del huevo de pascua incluido en todas las versiones de Python, simplemente ejecutando el siguiente comando en la consola interactiva:

En castellano sería algo como:
• Bonito es mejor que feo.
• Explícito es mejor que implícito.
• Simple es mejor que complejo.
• Complejo es mejor que complicado.
• Plano es mejor que anidado.
• Esparcido es mejor que denso.
• La legibilidad cuenta.
• Los casos especiales no son lo suficientemente especiales como para romper reglas.
• Pero la practicidad vence a la pureza.
• Los errores nunca deberían pasar silenciosamente.
• A no ser que haya un silencio explícito.
• En caso de ambigüedad, evita la tentación de adivinar.
• Debe haber una —y preferiblemente solo una— forma obvia de hacerlo.
• Aunque esa forma no parezca obvia desde el primer momento a no ser que seas holandés [hace referencia a que Guido es holandés].
• Ahora es mejor que nunca.
• Pero, muchas veces, nunca es mejor que ahora mismo.
• Si la implementación es complicada de explicar, es una mala idea.
• Si la implementación es fácil de explicar, es una buena idea.
• Los espacios de nombres son una buena idea –, ¡Hagamos más de esos!
Utilizando este Zen de Python, cualquier pythonista debería poder elegir la opción que se considera más "pythónica" para resolver cualquier conflicto al desarrollar una aplicación en este lenguaje.
1.7 COMPAÑÍAS QUE USAN PRODUCTOS CREADOS EN PYTHON
Muchísimas empresas conocidas usan Python para proveer los servicios en el lado del servidor, realizar scripts de mantenimiento, hacer aplicaciones internas para el manejo de determinadas tareas y un largo etcétera. A continuación se muestran algunos ejemplos de empresas y productos que usan Python:
• Google (Alphabet Inc.): es una de las compañías más grandes del mundo y tiene muchísimos productos, desde herramientas para gestión, como calendario, email, chats, videoconferencias, etc., hasta redes sociales donde compartir vídeos o imágenes. En muchos de estos productos, Python tiene un papel principal en el desarrollo. Google también contrata a muchos core-developers de Python, por lo que se puede decir que es una compañía muy integrada en la comunidad de Python.
• YouTube: es una red social para publicar vídeos que posee casi 2000 millones de usuarios y, según las publicaciones del equipo de ingenieros, gran parte del núcleo de la aplicación está escrito en Python, aunque esto no quiere decir que sea todo, dado que es un gran proyecto y tiene partes en Golang, Java, C y otros muchos lenguajes, como cualquier proyecto de esa magnitud.
• Instagram: es un producto donde se comparten más de 95 millones de fotos y vídeos diarios, con más de 1000 millones de usuarios registrados. Django es uno de los framework web que más utilizan sus ingenieros para desarrollar la plataforma. Más información en https://instagram-engineering.com/tagged/python.
• Spotify: es un producto para escuchar música en streaming con más de 207 millones de usuarios en todo el mundo. Su servicio se basa en el paso de mensajes entre los servidores y el cliente usando Python en gran parte de su sistema. Para más información https://labs.spotify.com/2013/03/20/how-we-use-python-at-spotify/.
Por otro lado, se puede ver cómo compañías tan grandes como las anteriormente comentadas también contribuyen activamente al desarrollo de librerías en Python y publican muchas de ellas como software libre:
• Google https://github.com/google?utf8=%E2%9C%93&q=&type=&language=python
• YouTube https://github.com/youtube?utf8=%E2%9C%93&q=&type=&language=python
• Instagram https://github.com/instagram?utf8=%E2%9C%93&q=&type=&language=python
• Facebook https://github.com/facebook?utf8=%E2%9C%93&q=&type=&language=python
• Spotify https://github.com/spotify?utf8=%E2%9C%93&q=&type=&language=python
Los ejemplos mencionados son solamente algunos de los cientos de compañías que utilizan Python para cualquier parte de sus sistemas. Asimismo, la cantidad de compañías haciendo uso y contribuyendo al lenguaje va en aumento cada año. Algunos ejemplos más podrían ser: Netflix, Uber, Drop-box, Eventbrite, Pinterest, Reddit y otras muchas más.
1.8 POSICIÓN DE PYTHON ENTRE LOS LENGUAJES DE PROGRAMACIÓN
Cada año se realizan encuestas a miles de programadores para saber sus gustos u opiniones sobre la tecnología que usan y cada vez es más evidente que Python se va consolidando, ganando posiciones o incluso lidera las estadísticas.
Uno de los índices más conocidos en el mundo es el que realiza la compañía TIOBE, que recopila información sobre el número de desarrolladores profesionales, cursos y búsquedas en 25 motores de búsqueda para componer el índice TIOBE Programming Community Index (https://tiobe.com/tiobe-index/). A continuación, se puede ver la evolución de los principales lenguajes de programación desde 2002:

Figura 1.1 E volución de lenguajes según el índice TIOBE.
Como se puede ver en la Figura 1.1, Python ha aumentado su ratio poco a poco en el sector profesional, y desde 2018 ha experimentado un fuerte incremento, lo que permite pronosticar que su uso seguirá creciendo en los próximos años, compitiendo con los lenguajes utilizados en el sector más arraigados, como pueden ser Java o C++.
En 2019, los resultados de la encuesta anual de StackOverflow (https://insights.stackoverflow.com/survey/2019/#most-loved-dreaded-and-wanted) sobre lenguajes y tecnologías mostraron que Python sigue liderando muchas de las secciones y mejorando posiciones en otras. Especialmente en la sección de amados, temidos y queridos, en la que se puede ver que Python está en segunda posición en los amados, con un 73,1 %, lo que significa que desarrolladores que han usado Python siguen queriendo usarlo tras aprenderlo. Además, está en primera posición entre los queridos, con un 25,7 %, lo que significa que quien no conoce el lenguaje, quiere conocerlo antes que los demás lenguajes.
En el ámbito del software libre se pueden evaluar las contribuciones que se han hecho para este lenguaje en GitHub. GitHut (https://githut.info/) es un proyecto que se encarga de analizar los repositorios públicos que hay en GitHub y mostrar la popularidad de cada lenguaje de programación basándose en los pull-requests (contribuciones) que se envían a cada repositorio y en el lenguaje en el que están escritos.
En la Figura 1.2 se puede ver la evolución de las contribuciones de los lenguajes más populares y cómo Python se ha mantenido desde 2013 entre el 17-18 %, mientras que otros como JavaScript han comenzado a decaer desde mediados de 2016.

Figura 1.2 Evolución de contribuciones a lenguajes en GitHub.
Por otro lado, se puede analizar para qué lenguajes se suelen buscar tutoriales o cursos, y cuál es la evolución de esas búsquedas. Para esta métrica se utiliza el índice PYPL (https://pypl.github.io/PYPL.html), que estudia la información sobre este aspecto usando Google Trends. En la Figura 1.3 se puede ver que Python está ganando cada vez más interés y se hacen más búsquedas relacionadas con cursos o tutoriales para aprender sobre él.

Figura 1.3 Evolución de búsquedas de cursos o tutoriales sobre lenguajes de programación.
1.9 PYTHON 2 VS PYTHON 3
Como se ha visto anteriormente, Python sigue en constante evolución y actualmente se utiliza la versión 3 del lenguaje, aunque aún quedan proyectos usando la versión 2 para mantener la compatibilidad con aplicaciones escritas en esa versión.
En enero de 2020 la versión 2 quedó sin soporte de seguridad, y la última versión, la 2.7.18, salió en abril de 2020, por tanto, todo el desarrollo que se hace hoy día debe hacerse en la versión 3. A continuación se exponen algunas diferencias importantes existentes entre la versión 3 y la versión 2.
1.9.1 str, bytes y Unicode
Una de las diferencias principales entre las versiones de Python 2 y Python 3 es cómo se categorizan las cadenas de caracteres binarias y caracteres Unicode.
En Python 3 existen dos representaciones diferentes: bytes y str. Los objetos tipo bytes representan cadenas binarias de 8 bits y los tipo str contienen caracteres Unicode.
Sin embargo, en Python 2 la categorización era diferente: existían str y Unicode como tipos de cadenas de caracteres, y str incluía las cadenas de caracteres ASCII y binarias de 8 bits. Unicode tenia los caracteres de Unicode.
La verdadera diferencia entre ambas versiones se puede resumir en que en Python 3 se han unificado Unicode y las cadenas de caracteres ASCII bajo el tipo str, mientras que en Python 2, unicode y cadenas (tanto binarias como ASCII) estaban separadas en dos tipos.

Por tanto, hay que prestar especial atención cuando se migra código a Python 3 que hace operaciones con las cadenas de caracteres, porque puede que la aplicación no funcione correctamente tras la migración y eleve excepciones en tiempo de ejecución.
1.9.2 Comparaciones de tipos no ordenables
En Python 2 era posible hacer comparaciones en las que realmente no quedaba claro cuál sería el resultado, como comparar si una tupla o un número era más grande que una cadena de caracteres, o si una tupla era más pequeña que una lista. En Python 3, sin embargo, cuando se pretende comparar datos distintos, el intérprete eleva una excepción tipo TypeError en vez de hacer la comparación con un resultado inesperado.

1.9.3 Operaciones numéricas diferentes
En Python 3 el manejo de números cambia un poco:
• Unifica el tipo long y el tipo entero bajo el tipo int.
• Hace que las divisiones por defecto puedan devolver números de coma flotante (float) en vez de enteros.
• Cambia la forma de redondear; hace que el medio (.5) se redondee hacia el número par más próximo, ayudando así a que las operaciones de agregaciones (sumas, medias, etc.) de números redondeados tengan menos margen de error, dado que no siempre se redondean al alza.

1.9.4 Iteradores por defecto
En Python 3 se utilizan los iteradores por defecto en muchas funciones y métodos de objetos de la librería estándar que en Python 2 se usaban como listas, con la finalidad de optimizar la cantidad de memoria utilizada. Esto es algo a tener en cuenta y se verá en profundidad más adelante en este libro.
Por tanto, la función para generar una secuencia ordenada de números, range, en Python 2 devuelve una lista, y en Python 3 devuelve un iterador. Esto no solo afecta a esta función, sino que nos encontramos con casos similares en map, filter, zip o en las funciones sobre diccionarios, como keys, values o items.

1.9.5 Función print
Como era de esperar ante un cambio de versión, en Python 3 se han modificado muchas funciones, pero aquí se expondrán algunas de las más utilizadas.
La función print se utiliza para imprimir por la salida estándar (normalmente por consola) una cadena de caracteres. Mientras que en Python 2 era una palabra reservada, en Python 3 es una función como cualquier otra, por tanto, es necesario utilizarla haciendo uso de los paréntesis que realizan la llamada a la función y pasando como primer argumento la cadena de caracteres. Adicionalmente, se han añadido algunas funcionalidades como escribir directamente a fichero la cadena de caracteres, o determinar el carácter final de la cadena o el separador si se utilizan varios parámetros en lugar de uno solo.

1.9.6 Migrar de Python 2 a Python 3
Para ayudar a la transición de la versión 2 a la versión 3, existe un programa que permite hacerlo de forma automática. Se llama 2to3: https://docs.python.org/2/library/2to3.html.
El uso de este programa es simple, pero cabe destacar que no siempre da el resultado esperado, dado que habrá partes del código que tengan problemas de incompatibilidad o que no se hayan convertido de forma automática. Habrá que realizar un análisis más exhaustivo manualmente, por lo que se recomienda realizar test sobre el código antiguo para que sean ejecutados tras la migración y poder asegurar que la conversión no ha causado daños en la lógica de la aplicación que se esté migrando.
A continuación, se muestra un ejemplo de migración utilizando este programa:


A continuación, se puede ver el contenido de los ficheros de código usados como fuente (using_2to3_source.py) y el fichero resultante (using_2to3_target.py).

Por otro lado, puede que haya librerías que hayan cambiado la nomenclatura o la forma de operar entre las versiones, por lo que se recomienda que se revise la documentación de cada librería externa que se use en el proyecto para evitar problemas en tiempo de ejecución.
La página https://caniusepython3.com/ permite comprobar si una librería es compatible con Python 3, aunque, hoy en día, todas las librerías que se creen deberían hacerse directamente en Python 3 o soportarlo sin mayor problema.
1.10 INSTALACIÓN DE PYTHON EN DIFERENTES SISTEMAS OPERATIVOS
Python se encuentra preinstalado en muchos sistemas operativos, especialmente si se utiliza Linux o Mac OS X, pero es recomendable utilizar la última versión, dado que continuamente se van añadiendo mejoras y nuevas funcionalidades. En esta sección se verá como instalar Python 3 en Linux, Windows y Mac OS X.
Por otro lado, una vez instalado el intérprete, se recomienda encarecidamente instalar el gestor de paquetes pip y la herramienta virtualenv para poder, por un lado, instalar y gestionar dependencias de forma fácil e intuitiva y, por otro lado, crear entornos de trabajo totalmente aislados de las dependencias del sistema y así no tener conflictos al trabajar en diferentes proyectos Python con diferentes necesidades.
1.10.1 Instalación en Linux
Para comprobar la versión de Python (si la hay) que tiene instalada en un sistema operativo, simplemente hay que ejecutar el siguiente comando en una terminal de comandos:

Si el texto mostrado en la consola es ese, significa que está instalada la versión 3, que es la utilizada en este libro, en caso contrario es necesario seguir los siguientes pasos.
Para Linux hay dos formas habituales de instalar Python, por un lado, mediante el gestor de paquetes del sistema y, por otro lado, utilizando el código fuente.
La forma más sencilla es utilizando el gestor de paquetes que puede ser apt (si la distribución es Debian o derivados, como Ubuntu o Linux Mint), yum (si la versión utiliza CentOS) o cualquier otro gestor de paquetes de la distribución de Linux utilizada.
A continuación, se muestran los comandos utilizados para instalar Python 3 utilizando el gestor de paquetes apt:

Si el repositorio de paquetes se encuentra demasiado desactualizado, será necesario agregar esta nueva fuente para que encuentre Python 3 y comenzar de nuevo el proceso:

Si la distribución Linux contiene un gestor de paquetes en forma gráfica, como por ejemplo Synaptic (http://www.nongnu.org/synaptic/), se puede buscar la palabra clave "Python" e instalar una de las versiones de Python disponibles.
Una vez completado el proceso de instalación, se puede comprobar que el intérprete interactivo por defecto está instalado en el sistema operativo ejecutando el siguiente comando. Al ejecutar python o python3 en una consola de comandos se debe obtener un resultado parecido al siguiente:

Esta salida en la consola quiere decir que todo está funcionando como se espera y que el intérprete interactivo está operativo y listo para ser utilizado. En las distribuciones de Linux derivadas de Debian, Python se instala en la carpeta /usr/bin/python.
Otra forma de instalar Python en Linux es haciendo uso del código fuente que se puede descargar del repositorio oficial (https://www.python.org/ftp/python/). Hay que especificar la versión que se desea instalar, igual que en el siguiente ejemplo:

Se recomienda usar altinstall en vez de install con el comando make para que no se reemplace el intérprete actual de Python instalado en el sistema (si lo hubiera), dado que puede causar graves incompatibilidades con las aplicaciones que lo estén usando actualmente.
1.10.2 Instalación en Windows
Es altamente improbable que en un sistema Windows ya se encuentre instalada una versión de Python por defecto, pero aún más que sea la última versión o la versión que se desee utilizar. Por tanto, en esta sección se expone cómo instalar Python en Windows.
La forma más rápida y efectiva es descargar el instalador de la página oficial de Python: https://www.python.org/downloads/. Es importante tener en cuenta que la versión del sistema operativo coincida con la del instalador, 32 o 64 bits.

Figura 1.4 Instalar Python en Windows.
Como se puede ver en la Figura 1.4, el instalador de Python es igual que cualquier instalador estándar de Windows, con la peculiaridad de que permite seleccionar dónde instalar la versión de Python. Es importante que se seleccione la opción de añadir Python al PATH (Add Python 3.9 to PATH en la imagen), dado que, así, en cualquier terminal de Windows se podrá ejecutar cualquier programa Python llamando al intérprete como se muestra a continuación:

__________
Si se utiliza Windows 10, se puede instalar el subsistema de Windows para Linux (WSL), el cual permite ejecutar una instalación de Ubuntu Linux en el sistema para disponer de herramientas Unix (https://docs.microsoft.com/es-es/windows/wsl/). Otra alternativa interesante es el uso de Cygwin (https://www.cygwin.com/) si WSL no está disponible en su sistema.
En la instalación de Python se incluyen IDLE y pip. IDLE es un editor para desarrollar programas escritos en Python que se verá en profundidad más adelante, y pip es un gestor de paquetes de Python que también se verá en detalle más adelante en este libro.
1.10.3 Instalación en Mac OS X
En las versiones actuales de Mac OS X ya viene instalada por defecto la versión 3 de Python, aunque puede que no sea la última versión, dado que normalmente las versiones preinstaladas en los sistemas operativos no suelen estar tan actualizadas como la versión oficial. A continuación se muestra cómo se puede instalar la versión que se desee de diferentes formas.
Cabe destacar que no hay problema en tener varias versiones distintas de Python instaladas en el mismo sistema operativo siempre que las instalaciones sean de forma aislada y no compartan las dependencias y librerías entre diferentes versiones.
Hasta la fecha, Mac OS X no tiene un gestor de paquetes integrado en el sistema, pero desde hace algunos años existe una alternativa muy intuitiva y eficaz llamada Homebrew (https://brew.sh/). Es un gestor de paquetes excelente para MacOS y también tiene soporte para Linux.
La forma de instalarlo es muy sencilla, solo hay que lanzar este comando en una terminal de Mac OS X:

Y se encarga de instalar el gestor de paquetes. Para instalar la última versión de Python disponible en Homebrew, se pueden seguir los siguientes comandos:

En Mac OS X las versiones de Python se alojan en /usr/local/Cellar/ python/ una vez han sido instaladas desde Homebrew.
Otro método para instalar la versión que se desee en Mac OS X es descargar el paquete de instalación de Mac OS X (.dmg) correspondiente de la página oficial de Python: https://www.python.org/downloads/. Con este método sí que se puede elegir exactamente la versión que se desea instalar, no solo la última disponible.

Figura 1.5 Instalador gráfico de Python para Mac OS X.
Una vez terminado el proceso de instalación, se puede ver que en las aplicaciones instaladas hay dos nuevos programas, IDLE y Python Launcher. IDLE es un editor de desarrollo de código Python e intérprete de desarrollo que se estudiará en profundidad más adelante en este libro. Por otro lado, Python Launcher es un lanzador de aplicaciones de Python. Es la aplicación que se puede configurar para abrir por defecto cualquier archivo con extensión de python o, si simplemente se arrastra un fichero python hacia el icono del launcher, este lo lanzará en el destino que esté configurado, por defecto, en una consola.
1.11 DISTRIBUCIONES DE PYTHON
Existen distribuciones de Python que pretenden unir diferentes paquetes de librerías comúnmente utilizados en un ámbito específico con la finalidad de facilitar la instalación de todos ellos a la vez, para así comenzar a utilizar las herramientas lo antes posible sin dedicar tiempo a instalar cada componente por separado.
Normalmente, estas distribuciones tienen más herramientas de las que un principiante, o incluso un experto, necesitaría, pero así se intenta cubrir el máximo número de casos de uso, aunque suponga tener que hacer una distribución de mayor tamaño. A continuación, se muestran algunas de las distribuciones de Python más conocidas.
1.11.1 Anaconda
Anaconda (https://www.anaconda.com/distribution/) es una distribución creada y mantenida por Anaconda Inc. Su finalidad es dar soporte a un amplio abanico de profesionales de la industria del software y, en particular, al sector científico y matemático que usa Python.
El modelo de negocio de Anaconda Inc. es el de dar soporte a empresas que usen sus productos, principalmente Anaconda Enterprise, por lo que provee servicios de gran calidad, incluso en la versión gratuita llamada Anaconda Distribution.
Los paquetes más destacables que tiene Anaconda son Pandas, NumPy, Scikit-learn, Numba, Matplotlib, Bokeh, soporte de IPython y Jupyter Notebook, entre otros. Gracias a su gestor de paquetes llamado Conda (https://conda.io/), se pueden instalar más de 1500 paquetes científicos de Python y R. Conda viene integrado por defecto en Anaconda, pero no es dependiente de esta, así que es posible usar el gestor de paquetes para diferentes lenguajes, como Ruby, R, Lua, Java, JavaScript y C++, entre otros, sin necesidad de instalar la distribución Anaconda.

Figura 1.6 Anaconda Navigator de la distribución de Python Anaconda.
Anaconda es la distribución de Python más utilizada en la actualidad, y cuenta con una interfaz gráfica intuitiva llamada Anaconda Navigator (ver Figura 1.6) que permite gestionar los paquetes instalados y las aplicaciones que se instalen y se utilicen desde la aplicación.
1.11.2 WinPython
WinPython (https://winpython.github.io/) es un proyecto de distribución de Python de software libre. Tiene el objetivo de dar soporte directo a la instalación de Python en un sistema operativo Windows con un programa portable y autocontenido, sin necesidad de instalar por separado el intérprete ni ningún paquete fuera del ecosistema de WinPython.
Esta distribución resulta muy interesante cuando en el sistema Windows no se pueden instalar aplicaciones a nivel del sistema, cuando se quiere mantener todo dentro de la distribución y no se permite que cualquier usuario instale otro tipo de programas o paquetes fuera de la misma o cuando, simplemente, se quiere utilizar una versión preconfigurada de Python.

Figura 1.7 Panel de control de WinPython.
Aparte del intérprete de Python, dispone del editor Spyder (que se verá en profundidad más adelante) y Pyzo (un editor minimalista e interactivo para editar código Python), herramientas para desarrollo de aplicaciones con Qt, una consola interactiva llamada IPython Qt Console, IDLE y muchas librerías preinstaladas, como PyQtGraph para gráficos científicos, SciPy, Pandas y Jupyter, entre otras.
Utiliza su propio gestor de paquetes llamado WPPM (WinPython Package Manager), que permite instalar paquetes de forma fácil, intuitiva y gráfica. Se puede acceder a WPPM utilizando el programa WinPython Control Panel que viene incluido en la distribución (ver Figura 1.7).
1.11.3 Enthought Canopy
Enthought Inc. (https://www.enthought.com/) es una compañía que mantiene una distribución de Python llamada Enthought Canopy. Es una distribución orientada al ámbito científico, aunque también permite programar aplicaciones de escritorio. Una de las principales características es que cuenta con un gestor de paquetes propio llamado EDM (Enthought Deployment Manager) que dispone de un índice propio de paquetes que ellos mismos han testeado y que mantienen para que sean compatibles con su gestor de paquetes, el cual, si se desea, se puede usar de manera independiente a Canopy en cualquier sistema operativo.
La distribución se puede descargar de https://assets.enthought.com/downloads/ para cualquier sistema operativo. La distribución presenta una interfaz gráfica a modo de editor, pero la están eliminando y recomiendan usar otro editor, como por ejemplo Visual Studio Code, que dispone de integración con EDM. Actualmente, los creadores de EDM prefieren enfocar sus esfuerzos en la integración con otros editores antes que en mantener un editor propio.

Figura 1.8 Usar Canopy para desarrollar en Python.
Esta distribución cuenta con paquetes científicos como Pandas, NumPy, SciPy, Matplotlib y IPython, además de contar con un depurador en el editor gráfico y herramientas como autocompletado, importación de datos, visualización de variables, etc. Para más información se puede consultar la página web de la documentación: https://assets.enthought.com/documentation/.
1.11.4 ActivePython
La empresa ActiveState ofrece múltiples soluciones para diferentes lenguajes de programación, entre ellas una distribución llamada ActivePython (https://www.activestate.com/products/python/). Esta distribución pretende dar soporte a empresas que quieran tener una distribución de Python en la que los paquetes que se instalen hayan sido revisados por ellos para verificar que son seguros y que cumplen con sus estándares.
A diferencia de Anaconda o Canopy, ActivePython intenta ser más trasversal y no enfocarse solo en el desarrollo matemático, estadístico o de inteligencia artificial (a los que también les da soporte), sino también en otros sectores, como el desarrollo de aplicaciones web, la seguridad o el testeo de aplicaciones.
ActivePython cuenta con un repositorio de más de 300 paquetes analizados, precompilados y validados. Una de las características a destacar es que tiene soporte para Intel Math Kernel Library (MKL - https://software.intel.com/en-us/mkl), la cual ayuda a acelerar el procesamiento de las operaciones matemáticas.
1.12 INSTALACIÓN DE LIBRERÍAS Y MÓDULOS EN PYTHON
Una vez visto cómo instalar el intérprete de Python, en esta sección se verá cómo se pueden gestionar los paquetes y librerías que se quieran instalar en el sistema utilizando las herramientas estándar.
Python es un lenguaje de código abierto y la comunidad que lo compone comparte esa misma filosofía publicando código para ayudar a los demás, e ir apoyándose los unos a los otros. Por tanto, existe una gran cantidad de paquetes y librerías disponibles para ser utilizadas en Python.
Casi todas las librerías que se utilizan se encuentran en el repositorio de paquetes oficial de Python, llamado PyPI (https://pypi.org/). PyPI son las siglas en inglés de "The Python Package Index". En este repositorio se pueden encontrar cientos de miles de proyectos, más de dos millones de ficheros y varios centenares de miles de usuarios, dado que es el repositorio oficial de los paquetes de Python.
Se pueden buscar manualmente y por categorías, pero usualmente se instalan usando el gestor de paquetes preferido en Python, pip (https://pypi.org/project/pip/). Este gestor de paquetes permite buscar e instalar de forma sencilla cualquier paquete de software dentro del catálogo PyPI, aunque también se puede utilizar para instalar paquetes que estén listados en un repositorio externo, cuyos paquetes tengan el mismo formato que se utiliza en pip.
Pip viene instalado por defecto en las versiones de Python superiores a la 3.4, pero, si por alguna razón se quiere instalar de forma independiente, se puede hacer mediante los siguientes pasos:

No obstante, cualquier librería o paquete se puede instalar de forma manual desde el código fuente. Normalmente, esto requiere compilarla siguiendo las instrucciones que indique el desarrollador e integrarla en el entorno de Python que se quiera utilizar.
Con pip se pueden listar o buscar paquetes en PyPI que contengan una determinada cadena de caracteres, instalar o desinstalar paquetes, actualizar un paquete a su última versión, mostrar información sobre ellos, ver los paquetes instalados en un entorno de Python, comprobar las dependencias de los paquetes instalados, instalar paquetes de otro repositorio diferente a PyPI, etc. Se utilizan los siguientes comandos:


Cabe destacar que, cuando se instalan paquetes en Python, es recomendable usar el argumento --user, dado que, así, solamente se instalará el paquete para el usuario actual y se evitarán conflictos con paquetes que tengan instalados otros usuarios del sistema, aunque es recomendable el uso de entornos virtuales para evitar este tipo de conflictos (como se verá en el siguiente apartado).
Como alternativa al uso de pip, se pueden instalar paquetes Python a nivel de sistema, instalándolos desde el gestor de aplicaciones de su sistema (apt, aptitute, homebrew, yum, etc.), construyéndolos desde el código fuente o utilizando binarios precompilados. De todas formas, se recomienda el uso de pip salvo en casos específicos en los que se desee instalar una versión en desarrollo o haya restricciones en el sistema que obliguen a utilizar una instalación alternativa a la que se hace utilizando pip.
1.13 MANEJO DE ENTORNOS VIRTUALES
Cuando se utiliza Python no solamente se pueden tener múltiples versiones del intérprete funcionando en el mismo sistema operativo, sino que se pueden tener múltiples versiones de los paquetes o librerías instalados siempre y cuando cada uno esté asociado a un intérprete independiente.
Por tanto, cuando se trabaja en varios proyectos y en el mismo sistema operativo, es común que se instalen versiones diferentes de los mismos paquetes de forma directa o indirecta (dado que hay paquetes que dependen de otros y a veces solo funcionan en versiones específicas). Por este motivo, la comunidad de Python ideó una forma de mantener los entornos de Python aislados unos de los otros y poder tener tantos como se desee. La solución se llamó entornos virtuales de Python (virtual environments).
Un entorno virtual se compone del intérprete en la versión que se haya elegido y todas las librerías o paquetes que se quieran usar, pero asegurándose de que el cambio o instalación de cualquier paquete o librería no afectará al sistema operativo o a ningún otro entorno virtual que se tenga instalado en la máquina, sino solamente al que se ha utilizado como destino.
Pip provee herramientas muy útiles a la hora de gestionar dependencias, dado que se pueden listar todas las dependencias instaladas en un entorno de Python con el comando freeze, para después guardar la salida en un fichero y poder instalar las mismas dependencias de forma automática, utilizando la opción –r y –U, para instalarlas recursivamente desde un fichero y actualizarlas a la versión especificada en el mismo:

De esta manera, se puede replicar fácilmente un entorno de Python con las mismas dependencias y requisitos que otro, utilizando simplemente el fichero de dependencias.
Virtualenv o venv
La herramienta más conocida para la creación de entornos virtuales en Python es venv. Esta permite crear entornos en la misma carpeta del proyecto, y se puede añadir un nombre específico al entorno virtual.
Venv viene preinstalado en Python 3, pero si se necesita usar en Python 2, existe la posibilidad de utilizar virtualenv, el cual se puede instalar utilizando pip (como cualquier otro paquete de Python).
Para su activación se utiliza el comando source sobre el fichero activate que se encuentra en la carpeta bin, como se ve a continuación:

Para desactivar el entorno virtual se puede ejecutar el comando deactivate.
Una vez activado el entorno virtual, cualquier paquete que se instale utilizando pip se instalará dentro del entorno virtual (específicamente en esa carpeta creada), y no en el sistema en general, evitando así las colisiones con otros paquetes o librerías.
Pipenv
Recientemente, la comunidad de Python ha desarrollado una herramienta que une virtualenv y pip para que trabajen juntos. Se llama pipenv (https://pipenv.kennethreitz.org/).
Usando pipenv se pueden crear entornos virtuales; instalar, actualizar o desinstalar dependencias; ver un gráfico de dependencias instaladas; abrir un intérprete de Python con el intérprete activado; comprobar vulnerabilidades de seguridad en las dependencias, y otras acciones de forma muy sencilla.
El paquete pipenv se puede instalar utilizando pip.
Pyenv
Para gestionar la instalación de varios intérpretes de Python con versiones diferentes, existe una herramienta llamada Pyenv (https://github.com/pyenv/pyenv), la cual permite hacer esta tarea de forma sencilla.
1.14 INTÉRPRETES INTERACTIVOS (REPL)
Una de las características principales de Python es que es interpretado y, por tanto, permite el desarrollo de intérpretes ligeros e interactivos, denominados REPL (read eval print loop o bucle de lectura, evaluación e impresión), que permiten el prototipado de aplicaciones o testeo de las mismas de una forma rápida y eficaz.
En esta sección se muestran los intérpretes interactivos más conocidos de Python.
1.14.1 Python
Es el intérprete por defecto. Se puede llamar ejecutando el comando python en cualquier terminal de comandos:

Este intérprete permite ejecutar comandos directamente en la terminal de manera interactiva. Se puede escribir código, importar librerías, testear la propia instalación de Python, etc. Es una herramienta muy útil cuando se está desarrollando cualquier aplicación, dado que permite testear en segundos el código que finalmente quedará en la aplicación final.
1.14.2 IPython
IPython (https://ipython.org/) es un intérprete interactivo que soporta autocompletado, por lo que permite escribir código Python de manera mucho más cómoda. Realmente es una aplicación muy potente capaz de soportar computación paralela, y por ello forma parte del núcleo de Jupyter Notebook.

Figura 1.9 Usando IPython en la consola de comandos.
1.14.3 bpython
Otro intérprete interactivo es bpython (https://bpython-interpreter.org/). Al igual que IPython, presenta autocompletado avanzado, pero lo hace en forma de cuadros de diálogo en los que se puede navegar. Esto mejora aún más la experiencia de prototipar código. Además, da soporte para guardar el código que se ha introducido en un fichero o en la web pastebin, soporta volver a ejecutar los comandos lanzados previamente guardados en el historial, etc.

Figura 1.10 Usando bpython en la consola de comandos.
1.14.4 ptpython
ptpython (https://github.com/prompt-toolkit/ptpython) se podría considerar una evolución del intérprete interactivo que, aparte de soportar el autocompletado en diálogo convencional, presenta algunas funcionalidades extra, como la edición de multilíneas utilizando las flechas, soporte de múltiples esquemas de colores, soporte de ratón, soporte de mapeado de teclas al estilo Vi o Emacs y otras muchas características.

Figura 1.11 Usando ptpython en la consola de comandos.
1.14.5 Intérpretes online
Existen varios intérpretes accesibles online que permiten ejecutar código Python desde cualquier dispositivo como si se estuviera ejecutando en una máquina local. Esto es así gracias a que el código se ejecuta a través de Internet en un servidor destinado para ello.
Este es el caso de PythonAnywhere (https://www.pythonanywhere.com/), una web que permite a sus subscriptores, o a cualquier persona que quiera probar Python, tener un intérprete siempre disponible. Ofrece diferentes planes según las necesidades de sus clientes.
PythonAnywhere da soporte a la propia web oficial de Python, www.python.org, en la que se puede encontrar una consola interactiva lista para ser usada en cualquier momento.

Figura 1.12 Consola interactiva de Python en python.org.
1.15 EL INTÉRPRETE DE PYTHON
Python es un lenguaje de programación interpretado, lo que significa que el código fuente no necesita ser compilado al código máquina específico del hardware donde se ejecuta, sino que se ejecuta directamente en cualquier sistema que tenga instalada la máquina virtual de Python.
Cuando se instala Python en una máquina, este tiene, como mínimo, dos compontes: el intérprete y la librería estándar (módulos, funciones, constantes, tipos, tipos de datos, excepciones, etc.). Dependiendo de la implementación de Python, el intérprete puede estar escrito en C, Java, .Net, etc. (como se verá más adelante), y ser un ejecutable o un conjunto de librerías enlazadas a otros programas.
Se podría definir el intérprete como un programa que se encarga de ejecutar otros programas. A continuación se ahondará en ello.
1.15.1 Estructura del intérprete de Python
Por un lado, está el código fuente, que se compone de ficheros de texto plano que tienen una gramática específica (que se denomina lenguaje Python), con una extensión concreta (.py) y estructurados de una forma precisa.
Por otro lado, se encuentran los ficheros de byte code, que son el resultado de una compilación rápida que se efectúa justo antes de comenzar la ejecución. El código escrito en byte code está listo para ser ejecutado en cualquier máquina virtual de Python.
Por último, se encuentra la máquina virtual de Python (PVM – Python Virtual Machine), que es la encargada de ejecutar los ficheros que tienen el byte code en la máquina. Por lo tanto, la parte que sí es dependiente del hardware utilizado es la máquina virtual, y es la que normalmente debe ser compilada cuando se instala Python por primera vez.
Lo que se denomina intérprete de Python es el programa completo que analiza el código fuente, genera los ficheros compilados y ejecuta el código usando la máquina virtual.

Figura 1.13 Estructura de ejecución de un programa en Python.
Cabe destacar algunas peculiaridades del byte code:
• Los ficheros que contienen el byte code tienen una extensión. pyc (Python compiled).
• Los ficheros no son necesarios para la ejecución del programa, dado que, si no se pueden generar por algún motivo (por falta de espacio o de permisos de escritura), el byte code será generado e insertado en memoria directamente, sin crearse en ficheros guardados en el sistema operativo.
• Un programa en Python que tenga los ficheros . pyc generados no necesita tener el código fuente, por lo que se puede ahorrar espacio de disco borrando los códigos fuente y solo ejecutando los .pyc. Esta práctica solo se recomienda en sistemas con grandes restricciones de espacio, dado que desde los ficheros byte code no se puede generar el código original.
• El intérprete de Python es inteligente a la hora de generar los .pyc, y si ya se han generado los ficheros pyc y no ha habido cambios en el fichero fuente, no realiza ninguna compilación, simplemente usa los ficheros ya compilados, agilizando así el proceso de iniciar la aplicación.
Como en cualquier otro proceso de compilación, cuando se lanza un programa usando el intérprete de Python, se pueden configurar los parámetros de forma que:
• Eviten que se generen los .pyc (opción –B).
• Se puedan ejecutar módulos directamente (opción –m).
• Se pueda pasar el programa como una cadena de caracteres (opción –c).
• Se pueda ajustar la cantidad de warnings emitidos por el intérprete (opción –W).
• Se pueda utilizar la optimización del código eliminando los asserts y los docstrings (opción –O y –OO).
Si, por ejemplo, se quisiera crear ficheros compilados (utilizando el módulo compileall, https://docs.python.org/3/library/compileall.html) y optimizados, se podría ejecutar el siguiente comando:

1.16 IMPLEMENTACIONES DE PYTHON
Cuando se habla de la implementación de Python, normalmente se hace referencia a la implementación usando C, denominada CPython. No obstante, el intérprete puede ser implementado también en otros lenguajes. Las diferencias entre implementaciones están, principalmente, en la habilidad de usar librerías (y otros programas) escritas en algún lenguaje específico. Por ejemplo, Pandas o NumPy están escritas en C y, por tanto, de forma nativa no pueden ser ejecutadas en intérpretes que no sean CPython. De todas formas, otras implementaciones de Python pueden permitir el uso de librerías que no están disponibles para CPython utilizando otras técnicas. A continuación, se detallan las implementaciones más conocidas del lenguaje.
1.16.1 CPython
La implementación original del lenguaje y la que se menciona por defecto es CPython. Es una implementación del lenguaje hecha en ANSI C y es la más utilizada. El intérprete genera byte codes desde los ficheros fuente para ejecutarlos en la máquina virtual de Python, y esta los ejecuta.
Si un sistema operativo tiene una versión de Python preinstalada, lo más seguro es que la implementación sea CPython. Es el software que se puede descargar de la web oficial del lenguaje (http://www.python.org).
Si se pretende crear código que sea lo más compatible posible o software libre, y no se tiene una restricción fuerte o una dependencia con algún otro lenguaje (como Java o C#), lo mejor es optar por CPython, dado que es el estándar, siempre se mantiene más actualizada, soporta la interoperabilidad con librerías escritas en C y, normalmente, es muy rápida en tiempo de ejecución si se compara con la mayoría de las demás implementaciones.
1.16.2 Jython
Existe una implementación de Python hecha sobre la máquina virtual de Java (JVM) denominada Jython (https://www.jython.org/). También se conoce como JPython, y su principal ventaja es que puede ejecutarse junto a código Java.
La principal diferencia es que el código fuente Python es transformado de forma transparente a byte codes de Java y ejecutado como cualquier otro programa escrito nativamente en Java, lo que permite la interoperabilidad completa y ejecutar código Python como si fuera código nativo en la misma máquina virtual. Esto permite importar librerías de Java sin problema como si fueran librerías de Python y viceversa.
1.16.3 PyPy
La implementación de CPython es a veces criticada por ser lenta, y una solución podría ser implementar un JIT ("compilador en tiempo de ejecución" o just-in-time compiler) que permita compilar el código en tiempo de ejecución, manteniendo así el carácter interpretado e interactivo, pero con el potencial de compilar el código mejorando su velocidad de ejecución. Para mejorar ese aspecto se creó PyPy (https://pypy.org/).
PyPy es una implementación de Python en C que cuenta con un JIT, lo que permite que su ejecución sea desde 0.23 hasta 4.4 veces más rápida, según afirman es su web de comparación de tiempos de ejecución https://speed.pypy.org/.
Otra característica de PyPy es que posee lo que se conoce como stackless mode, que permite desacoplar el código a la hora de ejecutarse. Esto permite generar minihilos de ejecución para conseguir una concurrencia de código masiva, hace que la ejecución de diferentes partes del código sea casi paralela en tiempo de ejecución y mejora muchísimo la velocidad de ejecución de los programas.
Un aspecto para tener en cuenta de PyPy es la compatibilidad con librerías en general, dado que implementa una versión reducida de Python, RPython (reduced Python). Por este motivo, no es compatible con todos los programas escritos en Python, pero se han encargado de dar soporte a librerías y frameworks grandes como Django o Twisted, entre otros.
Actualmente soporta Python 2 y Python 3, por lo que puede ser una gran herramienta a tener en cuenta y usar cuando el proyecto lo permita para mejorar mucho la velocidad de ejecución o aprovechar la capacidad del modo stackless.
1.16.4 IronPython
Existe una implementación en C# de Python que está integrada perfectamente en el framework .NET. Se llama IronPython (https://ironpython.net/) y permite la integración transparente con C#.
Por tanto, es una gran herramienta para desarrolladores .NET que quieran utilizar un lenguaje de scripting y todos los beneficios de Python para crear proyectos nuevos.
1.17 DESARROLLAR PROGRAMAS EN PYTHON (IDE)
Para poder desarrollar aplicaciones Python, solo se necesitan un editor de texto y el intérprete de Python, pero como en todos los lenguajes de programación, existen programas especializados para hacer que esta tarea sea más fácil e intuitiva. En muchas ocasiones estos programas ahorran tiempo de desarrollo gracias a sus herramientas integradas. Este tipo de programas se denominan IDE (integrated development environment, entorno de desarrollo integrado).
Los IDE ayudan en muchas partes del desarrollo, dado que pueden pro-veer la documentación estándar o librerías externas de forma intuitiva y corregir errores gramaticales o código que potencialmente no se vaya a ejecutar correctamente. También pueden proveer herramientas prácticas como conexiones a servidores remotos, integración con sistemas de control de versiones o herramientas de integración continua, así como facilitar el despliegue o publicación de aplicaciones y un largo etcétera. Además, en la mayoría de los casos permiten personalizar el editor para que se adapte a los gustos del desarrollador.
1.17.1 Entornos de desarrollo open source
En esta categoría se verán los entornos de desarrollo open source más utilizados para desarrollar Python. Se mencionarán algunos trucos o recomendaciones para mejorar la experiencia de su uso y las características principales de cada uno. Se han ordenado según su especialización para desarrollar programas en Python, de menor especialización (más generales) a más específicos.
Vim
Vim (Vi Improved, https://www.vim.org/) es el IDE utilizado por muchos desarrolladores para distintos lenguajes de programación. Cuenta con plugins y soporte para Python, lo que lo convierte en un potente editor. Algunas características de este editor son:
• Editor de texto orientado a consola, así que prácticamente todo el desarrollo se puede hacer sin utilizar el ratón.
• Utiliza combinaciones de teclas para poder moverse o editar rápidamente, y dispone de varios modos de operación, no solo del modo "edición" presente en la mayoría de editores.
• Es un editor que requiere mucho tiempo de aprendizaje y configuración, por lo que es algo avanzado para programadores novatos.
• Es un editor altamente configurable y modulable.
• Toda la configuración se puede hacer en un único fichero de configuración, lo que permite que sea muy portable y tener la misma configuración en diferentes entornos rápidamente.
• Se organiza en buffers (a modo de ventanas en consola). Puede dividir cada buffer vertical y horizontalmente, por lo que permite tener múltiples ficheros abiertos en la misma pantalla y moverse con facilidad entre ellos.
• Su principal beneficio es que permite editar texto extremadamente rápido. Tiene funcionalidades por defecto como macros donde se pueden guardar una serie de modificaciones y repetirlas N veces sin esfuerzo, o edición vertical de múltiples líneas de forma nativa.
• Aunque esté concebido para ser usado en consola, también existen implementaciones con interfaz de ventana, como gVim o MacVim.
• Uno de los beneficios importantes es que se puede integrar con programas de consola como tmux (https://github.com/tmux/tmux/wiki), en el que se puede tener el editor de código y su ejecución en la misma consola, sin tener que salir de ella, lo que permite ahorrar tiempo de desarrollo.

Figura 1.14 MacVim configurado para programar Python.
Vim suele venir instalado por defecto en la mayoría de sistemas operativos Unix, pero en Mac o en Windows hay que instalarlo aparte.
Si no está instalado en su sistema Unix, se puede encontrar fácilmente en el repositorio de paquetes de su distribución buscando el paquete vim. En Windows será necesario descargar el ejecutable compilado del programa de la web oficial, https://www.vim.org/download.php. En Mac OS se puede instalar usando homebrew o usando el proyecto alternativo MacVim (https://macvim-dev.github.io/macvim/).
Es importante que este editor tenga el soporte para Python activado. Se puede comprobar lanzando el siguiente comando por consola y buscando por +python:

El archivo de configuración suele estar en la carpeta de usuario como archivo oculto con el nombre .vimrc, y la configuración básica para poder desarrollar de manera eficiente en Python sería:

Con esta mínima configuración se tiene la indentación correcta y las herramientas más básicas. Existen varios proyectos que añaden más funciones, como los validadores de PEP-8 integrados, el sistema de navegación de archivos, la búsqueda de información, etc. Uno de esos proyectos es Python-mode https://github.com/python-mode/python-mode. Usándolo se pueden utilizar configuraciones avanzadas de programadores expertos para conseguir "el editor perfecto". El objetivo es dar con el editor en el que el desarrollador se encuentre más cómodo y sea más productivo.
Emacs
Otra alternativa de editor que usa la consola de comandos es Emacs. Emacs está desarrollado en Lisp y es otro de los editores mundialmente utilizados para desarrollar diferentes lenguajes de programación. Como Vim, puede ser altamente personalizado para satisfacer los gustos de cada programador.
A diferencia de Vim, Emacs está por defecto en modo edición y solo sabiendo los comandos correctos se puede hacer uso de funciones avanzadas.
Emacs cuenta con plugins que permiten extender ampliamente las funcionalidades del editor y convertirlo en una excelente herramienta para desarrollar Python. Sin embargo, a diferencia de Vim, las instalaciones o configuraciones hay que hacerlas escribiendo código en Lisp, en vez de tener un fichero de configuración con sintaxis propia del editor. Por suerte, no son difíciles de añadir, y cada plugin cuenta con la documentación necesaria para que su uso sea fácil.

Figura 1.15 Emacs usando Elpy para programar en Python.
Uno de los plugins más utilizados para convertir Emacs en un editor de código Python es Elpy (https://github.com/jorgenschaefer/elpy). Con él se añaden funcionalidades como autocompletado y navegación de código, validación de sintaxis, modo depuración, análisis de rendimiento, etc. Para instalar este plugin en Emacs solo hay que cambiar el fichero ~/.emacs añadiendo el siguiente comando o instalándolo desde el repositorio de paquetes de Emacs (Melpa).

Atom
Atom es un editor de texto moderno, open source, altamente configurable y multiplataforma. Fue creado por los desarrolladores de GitHub y, como dice su eslogan, pretende ser un editor de textos hackeable del siglo XXI (A hackable text editor for the 21st Century).
Una de las características fundamentales de este editor es que es intuitivo y fácil de usar, por lo que cualquiera puede comenzar a programar en cualquier lenguaje en cuestión de minutos y de forma gratuita. Tiene soporte para Python; detecta y colorea de forma correcta el código Python sin necesidad de utilizar un plugin adicional.

Figura 1.16 Atom para programar Python usando el plugin de autocomplete-python.
Uno de los pilares fundamentales es que tiene un sistema de paquetes que permite instalar nuevos plugins, es multiplataforma, tiene un navegador de archivos, capacidad de utilizar múltiples paneles para separar tanto horizontal como verticalmente la pantalla y una integración por defecto con el sistema de control de versiones Git y con el repositorio de código GitHub.
Para la integración con Python se pueden encontrar paquetes como:
• Autocomplete-Python: permite autocompletar código Python, buscar la definición de cada parte del código, buscar uso de objetos, etc. Usa populares paquetes como jedi o Kite para hacer los autocompletados, por lo que es altamente recomendable. https://atom.io/packages/autocomplete-python
• Linter-pylint: plugin que permite integrar pylint para comprobar que se sigue la PEP-8, detectar errores, etc. https://atom.io/packages/linter-pylint
• Script: herramienta para realizar ejecuciones de código de lenguajes, entre ellos, Python. https://atom.io/packages/script
• Python-debugger: herramienta para poder depurar código Python de forma intuitiva. https://atom.io/packages/python-debugger
• IDE-Python: es un paquete que se compone de otros paquetes útiles como jedi, rope, pyflakes, pylint, flake8 y YAPF. https://atom.io/packages/ide-python
IDLE
IDLE (integrated development and learning environment) es un IDE escrito puramente en Python utilizando las librerías de Tkinter. Permite realizar operaciones como la edición de ficheros, depuración de código con puntos de ruptura persistentes, tener varias ventanas de edición, búsqueda en cualquier ventana de edición, coloreado de gramática Python, autocompletado y otras muchas posibilidades.

Figura 1.17 Usando IDLE en macOS para programar en Python.
Se instala por defecto con la mayoría de distribuciones de Python y también cuando se instala Python como paquete externo al gestor de ficheros del sistema operativo. Por esto, en Windows o en macOS es fácil que se tenga ya instalado al haber instalado el intérprete de Python, mientras que, cuando Python ya está preinstalado en el sistema, normalmente hay que instalarlo aparte.
PyDev
Eclipse (https://www.eclipse.org/eclipseide/) es un IDE de software libre muy popular entre los desarrolladores de Java, JavaScript, PHP, Rust y otros lenguajes, pero también se puede utilizar para desarrollar Python instalando PyDev (https://www.pydev.org/index.html).

Figura 1.18 Usando PyDev para programar en Python.
PyDev tiene autocompletado de código, ayudas para acceder a la definición de los objetos, depurador tanto en la máquina local como en remoto, integración de pylint y muchas otras características importantes.
Es un editor de primer nivel, dado que, al basarse en Eclipse, que ha sido utilizado durante años por muchos desarrolladores de diferentes lenguajes, se han ido añadiendo múltiples características que se pueden aprovechar ahora usando PyDev.
Una de las principales ventajas que presenta este editor es que, una vez se instala PyDev dentro de Eclipse, todas las herramientas necesarias para programar en Python ya están instaladas y no hay que instalarlas una a una por separado. No obstante, esto también puede ser un inconveniente, dado que no presenta los mismos beneficios de poder configurarlo a gusto del desarrollador ni los editores comentados anteriormente.
Otra gran ventaja de usar este IDE es que, por defecto, ofrece poder lanzar el código Python usando Jython, que soporta fácilmente la integración de Python y Java.
Spyder
Spyder (https://github.com/spyder-ide/spyder) es un IDE moderno orientado al desarrollo científico utilizando Python. Está presente en el paquete de Anaconda (paquete comentado en secciones anteriores) por defecto y permite utilizar muchas herramientas de desarrollo científico, como Num-Py, Pandas para el manejo de datos, una consola interactiva donde se puede ejecutar código directamente y Matplotlib para la representación de imágenes, aparte de poder mostrar las variables utilizadas, incluso las complejas como dataframes, de forma totalmente transparente e intuitiva.

Figura 1.19 Usando Spyder para programar en Python.
Este IDE se puede utilizar por separado sin necesidad de instalarlo con el gestor de paquetes de Anaconda (Conda), y ciertas características como el analizador de código, el depurador de código, el divisor de pantalla para ver múltiples ficheros a la vez y algunos plugins que se muestran a continuación, hacen que sea un IDE muy completo y útil también para desarrollos no científicos.
Existen algunos plugins interesantes para este IDE:
• Spyder Notebook: integra Jupyter Notebook dentro del IDE y se puede usar tanto en el editor como en la ventana principal. Esto permite gestionar los ficheros utilizados recientemente y autoguardar los modificados de forma sencilla.
• Spyder Terminal: permite integrar una terminal en el mismo espacio de trabajo del editor, lo que aporta mucha más potencia al desarrollo y ejecución de Python, incluso en Windows.
• Spyder UniTest: permite integrar frameworks de test directamente en el editor.
• Spyder Reports: permite generar reportes utilizando markdown en una ventana separada del editor.
Visual Studio Code
Visual Studio Code (https://code.visualstudio.com/) es un IDE de código libre, ligero, rápido, estable y multiplataforma, creado por Microsoft para dar soporte a programadores de muchísimos lenguajes, entre ellos C#, C++, Java, PHP o Python.

Figura 1.20 Usando Visual Studio Code para programar en Python.
Al instalar el plugin para Python (https://marketplace.visualstudio.com/items?itemName=ms-python.python) el IDE se adapta para ser utilizado por cualquier desarrollador Python. Este IDE presenta características muy atractivas, por ejemplo: el autocompletado de sintaxis desarrollado por Microsoft llamado IntelliSense; la comprobación de sintaxis; herramientas de depuración, visualización de variables, navegación y formateado de código; ejecución por consola dentro del mismo entorno de programación; integración con Git y GitHub desde el propio IDE; soporte para Jupyter Notebook; herramientas para refactorizar, y muchas otras características.
Al ser un IDE con un gran soporte y utilizado por muchísimos desarrolladores, la integración con otros lenguajes y las herramientas desarrolladas para otros lenguajes se comparten con todos y, por tanto, el IDE se enriquece de manera orgánica a la vez que soporta más lenguajes de programación.
1.17.2 Entornos de desarrollo de código cerrado
En esta sección se exponen los IDE de código cerrado. Este tipo de IDE son desarrollados por compañías cuyo principal producto es el IDE en cuestión, y normalmente tienen una o varias versiones con un modelo de negocio basado en el pago recurrente por licencias de uso. Esto hace que sean profesionales y tengan soporte técnico rápido para cualquier tipo de fallo. Que haya diferentes empresas orientadas a este tipo de negocio hace que estas compitan entre sí para ofrecer las mejores prestaciones.
Sublime Text
Sublime Text es un editor de texto moderno orientado a múltiples lenguajes que presenta muchas características interesantes para el desarrollo de aplicaciones en general. Está desarrollado por la compañía Australiana Sublime HQ Pty Ltd y su modelo de negocio se basa en permitir evaluar el editor por tiempo. No obstante, te recuerdan que se necesita una licencia para utilizarlo continuamente, por tanto, aunque no fuerzan a comprar la licencia desde un primer momento, sí que es la finalidad al permitir evaluarlo de forma gratuita.
Posee características de primer nivel, como la selección múltiple, la navegación por la documentación o por los objetos del código y la posibilidad de personalizar combinaciones de teclas para realizar tareas cotidianas. Además, tiene un sistema de paquetes para expandir las funcionalidades, es multiplataforma, tiene un modo de edición que emula los comandos de Vi, permite dividir el área de edición tanto vertical como horizontalmente y otras muchas funcionalidades.
Este IDE tiene soporte para Python en el paquete llamado Anaconda (https://packagecontrol.io/packages/Anaconda), lo que permite tener la documentación de Python, pylint, autocompletado, búsquedas de uso y otras funcionalidades específicas para trabajar con Python integradas en Sublime Text.

Figura 1.21 Usando Sublime Text para programar en Python.
PyCharm
Uno de los mejores IDE integrados para el desarrollo de Python es PyCharm (https://www.jetbrains.com/pycharm/). Está desarrollado por JetBrains, una compañía que está especializada en hacer IDE para diferentes lenguajes de programación, muy conocida por la calidad de los mismos y el buen soporte técnico con el que cuentan.
Uno de los IDE más conocidos es IntelliJ IDEA (https://www.jetbrains.com/idea/), que fue el primer IDE que sacaron al mercado para dar soporte a Java. La acogida fue tan buena que cuando Google se dispuso a lanzar su entorno de programación para aplicaciones de teléfonos inteligentes Android, Android Studio (https://developer.android.com/studio), utilizó IntelliJ IDEA como base. Un caso similar es el de PyCharm, que se podría considerar la versión de IntelliJ IDEA orientada al desarrollo de aplicaciones Python. Se trata de un IDE profesional y totalmente integrado con Python, pero distribuido directamente por JetBrains.

Figura 1.22 Usando PyCharm para desarrollar Python.
PyCharm está disponible en dos versiones, la Community Edition y la Professional. La Community Edition es muy potente y dispone de múltiples funcionalidades, como documentación navegable, autocompletado avanzado, comprobación de sintaxis, herramientas para corregir errores de PEP-8 automáticamente, auto-formateado, edición de texto en diferentes modos (soporta el vertical y el selectivo), consola interactiva, terminal del sistema, sistema de control de versiones (soporta diferentes tipos), comprobación de problemas de tiempo de ejecución, depurador de código profesional y un sinfín de funcionalidades que lo convierten en uno de los líderes indiscutibles en el desarrollo de cualquier tipo de aplicación de Python.
La versión Professional dispone de algunas funcionalidades que no están presentes en la edición Community, como el analizador de memoria y de tiempo de una aplicación, herramientas científicas, soporte para desarrollo web (diferentes frameworks, soportes avanzados de JavaScript, soporte de plantillas de renderizado, renderizado en diferentes navegadores, etc.), capacidad para desarrollar en entornos remotos y un fabuloso soporte para bases de datos, que puede integrar muchos tipos de bases de datos, lo que hace que no haya que salir del IDE a casi ninguna aplicación externa y mejore muchísimo la productividad.
PyCharm cuenta con soporte para otros lenguajes y soporta casi cualquier tecnología profesional, dado que tiene muchos plugins, por ejemplo: Gulp, Docker, Bash, Markdown, Jupyter Notebook, scss, etc.
1.17.3 Comentario general sobre IDE
Existen muchos IDE que soportan la programación en Python y cada uno de ellos presenta unas características distintas. Existen IDE de código libre y empresas especializadas que crean IDE de código cerrado con la finalidad de ofrecer el mejor servicio para sus clientes. Cada desarrollador debería elegir el IDE que más se ajuste a sus necesidades. Un listado, ordenado desde los más comunes hasta los más específicos para Python, sería: Vim, Emacs, Sublime Text, Atom, PyDev, Visual Studio, Spyder, ActivePython, PyCharm.
A nivel personal, he utilizado Vim durante algunos años y creo que es un maravilloso editor de texto, aunque hay que reconocer que se necesita mucha paciencia para aprender a utilizarlo correctamente y sacarle el máximo partido. Actualmente utilizo PyCharm en su versión profesional, dado que me ayuda de manera excepcional en todas las tareas que realizo. Aun así, no descarto utilizar otros en un futuro, por ejemplo, Visual Studio Code, Spyder o Atom, en ese orden, dado que a mi juicio ofrecen las mejores características e integraciones con Python con el mínimo esfuerzo de configuración o personalización.
1.18 PRIMEROS PROGRAMAS EN PYTHON
Existe una práctica comúnmente conocida a modo de iniciación en la programación: cuando se aprende un nuevo lenguaje, el primer programa que se crea es uno muy simple en el que se le da la bienvenida al mundo con la frase 'Hola Mundo' en el lenguaje de programación elegido.
En Python esta práctica es muy fácil de realizar y consistiría en escribir lo siguiente en un REPL de Python:

1.18.1 Ejecutar programas en el REPL de Python
Puesto que Python es un lenguaje interpretado y dispone de múltiples REPL (read eval print loop) en los que se puede ejecutar código Python simple, es uno de los instrumentos más utilizados cuando se está desarrollando una aplicación más compleja, se pretende hacer cualquier cálculo simple o se quiere probar alguna funcionalidad de forma aislada.
La forma más sencilla de obtener un REPL es llamar al intérprete de Python sin ningún parámetro desde una consola de comandos, como se muestra a continuación:

Se pueden utilizar otros REPL, como ipython, bpython o ptpython, que presentan autocompletado y copiado de lo escrito a ficheros que después se pueden utilizar para proyectos más grandes.
1.18.2 Usar Jupyter Notebook
Jupyter Notebook es una herramienta a medio camino entre un REPL y un IDE completo. Cuenta con funcionalidades comunes como la exportación de contenido a diferentes formatos, soporte para markdown, soporte para crear imágenes usando librerías de imágenes como Matplotlib, manejo interactivo de datos usando pandas y un sinfín de herramientas que se pueden ampliar gracias a plugins. Esto lo convierte en una herramienta ideal para muchos científicos y amantes de Python que quieren realizar cálculos y expresar sus resultados de la mejor forma. Además, los resultados pueden ser exportados tanto usando el código fuente como en formatos como PDF, HTML o Latex, entre otros.
Una forma de instalar Jupyter es usando pip, aunque también viene incluido por defecto en el paquete de software Anaconda:

Jupyter Notebook dispone de un servidor que gestiona la comunicación y la ejecución de código, por lo que desde la consola de comandos se puede invocar con el siguiente comando:


Figura 1.23 Vista inicial de Jupyter y del sistema de ficheros.
Este comando arranca el servidor y automáticamente abre el navegador que esté instalado en el sistema operativo con la página de Jupyter Notebook mostrando el sistema de archivos. Con Jupyter Notebook se puede explorar el sistema de archivos, crear archivos Notebook, exportar el código, lanzar código y un largo etcétera, pero en este apartado se expondrá solo el manejo para explorar algunos datos simples y algunas visualizaciones básicas usando Matplotlib. En cualquier caso, se recomienda acudir a documentación especializada sobre cómo usar Jupyter Notebook para sacarle el máximo partido.
Por defecto, el punto del sistema de archivos que se muestra es el mismo en el que se ha lanzado el comando para arrancar el servidor. Como se puede ver en la imagen anterior, la extensión de los notebooks .ipynb y la apariencia de la aplicación son muy sencillos y tienen un gran potencial.
Como se puede ver en la Figura 1.24, Jupyter Notebook no solo permite hacer cálculos sencillos, sino definir funciones, editar las mismas funciones varias veces y crear imágenes utilizando librerías de creación como Matplotlib de forma fácil e intuitiva.
Una vez terminada la edición, se puede exportar como un archivo Python normal o como uno de Jupyter, lo que permite compartirlos libremente y que otra persona pueda ver los resultados, ejecutarlos o incluso modificarlos de forma simple. Esto hace que esta herramienta sea ideal para el prototipado y la exposición de soluciones de carácter científico o general.

Figura 1.24 Ejemplo d e uso de Jupyter Notebook.
1.18.3 Primeros programas ejecutados desde ficheros
La forma más común de ejecutar aplicaciones Python es por medio del lanzamiento de un fichero específico de la aplicación que inicializa y ejecuta la lógica principal. Este tipo de lanzamiento se utiliza también en otros lenguajes de programación, como C o Java, en los que se especifica un punto de entrada o inicio a modo de "main point" y a partir de ahí se ejecuta el programa completo.
En Python todos los ficheros pueden tener ese punto de entrada y se definen utilizando la siguiente sentencia en cualquier fichero:

A continuación, se muestra un ejemplo de una aplicación muy simple que imprime la cadena Hola Mundo y algunos cálculos simples que definen funciones y hacen llamadas a las mismas:

Para ejecutar el archivo, simplemente se necesita llamar al intérprete de Python con el que se desea ejecutar el archivo en el lugar en el que se encuentra el punto de entrada. Si se guarda el código anterior en un archivo llamado "ejemplo_simple.py", se puede ejecutar de la siguiente forma:


Este tipo de ejecución se puede hacer tan compleja como se necesite. Se puede añadir la interacción con usuario mediante la utilización de parámetros para el script haciendo uso de librerías como argparse (https://docs.python.org/3/library/argparse.html) o click (https://click.palletsprojects.com/).
Aunque estos programas se pueden ejecutar desde consola, lo más recomendable es utilizar el propio IDE, abrir el fichero y ejecutarlo, dado que no solamente se puede ver la ejecución en el mismo programa, sino que muchos de los IDE permiten realizar una depuración a conciencia del programa y añadir puntos de ruptura en los que analizar cada variable. Asimismo, cuentan con muchas herramientas que ayudan a desarrollar de forma rápida y efectiva.
Capítulo 2 VARIABLES Y TIPOS DE DATOS
En esta sección se verán conceptos fundamentales del lenguaje Python en detalle. Se describirán los conceptos de literales, variables, memoria de un programa y las características de los tipos de datos presentes en el núcleo de Python, dado que son piezas fundamentales para el desarrollo de cualquier aplicación escrita en este lenguaje. Muchos de los conceptos, además, son aplicables a otros lenguajes.
1 INTRODUCCIÓN A LOS TIPOS DE DATOS
En la vida cotidiana estamos rodeados de datos de diferentes tipos. Existen datos numéricos, como las estadísticas de compras y ventas de los productos, datos en forma de cadenas de caracteres, que pueden ser tan pequeños como siglas o tan grandes como un libro completo, datos como la demografía de los países o datos en forma de fechas que ayudan a organizar los calendarios.
En Python, los tipos de datos se pueden construir y hacer a medida con el objetivo de modelar cualquier objeto de la vida real en su versión digital. En esta sección, sin embargo, se verán los tipos de datos presentes en el núcleo de Python.
A continuación, se muestra una tabla que condensa todos los tipos de datos presentes en el núcleo de Python (built-in), ejemplos de cómo inicializar cada tipo de dato y si son mutables o no (concepto que se explica en profundidad en la sección 2.4):

Como se puede observar en la tabla anterior, el núcleo de Python ya provee un amplio abanico de tipos de datos fácilmente instanciables e intuitivos, que permiten que cualquier desarrollador pueda enfocarse en qué quiere construir y no en cómo lo quiere construir.
Sin embargo, Python es lo suficientemente versátil para permitir que cualquier programador pueda definir sus propios tipos de datos según su necesidad y extender así las funcionalidades básicas que estos tipos de datos ofrecen.
Haciendo uso de la función type se puede saber el tipo de cualquier objeto en Python, como se muestra a continuación:

Y haciendo uso de la función dir se pueden ver todas las operaciones disponibles para ese tipo de dato en concreto:

Como se puede observar, para cada tipo existen multitud de operaciones que se pueden hacer por defecto y que se verán en detalle a lo largo de los siguientes capítulos.
2 LITERALES, VARIABLES Y DATOS EN PYTHON
Cuando los valores o tipos de datos se intentan trasladar al ámbito de la computación y de los lenguajes de programación, se hace uso de la memoria de un sistema, que es la herramienta que permite almacenarlos y manejarlos de forma eficiente (el tipo de memoria más conocido en las computadoras es la memoria RAM).
La memoria se divide en pequeñas porciones, semejantes a cajas, que se unen para formar almacenes completos. La porción más pequeña de almacenaje es un bit, y solo puede contener información binaria (representación de un 0 o un 1). A medida que se unen cadenas de bits se forman bytes (B u 8 bits), kilobytes (KB o 1024 bytes), megabytes (MB o 1024 kilobytes), gigabytes (GB o 1024 megabytes), terabytes (TB o 1024 gigabytes), petabytes (PB o 1024 terabytes) y exabytes (EB o 1024 petabytes).
Los ordenadores modernos tienen varias decenas de gigabytes de memoria RAM, la cual permite almacenar y ejecutar programas de un volumen importante. Con las técnicas de ejecución actuales, se pueden incluso usar porciones del disco duro como memoria (SWAP) para no depender únicamente de la capacidad de la memoria RAM, aunque se sacrifica la velocidad de lectura y escritura, dado que, por lo general, estas operaciones en disco duro son más lentas que en memorias especializadas para ellas como es la RAM.
En Python todos los tipos de datos son objetos, y es el propio intérprete el que se encarga de saber dónde se ubican en la memoria y cómo acceder a ellos. Por este motivo, a la hora de desarrollar aplicaciones no es necesario dedicarle mucho esfuerzo a la gestión de memoria.
Conceptualmente, los objetos en Python se pueden ver como dos componentes. Por un lado, un puntero (referencia al objeto) que guarda la dirección de la memoria en la que está alojado ese dato y, por otro lado, la "caja" (continente) que representa el almacenamiento del objeto guardado en sí, el cual contiene información relevante, como el tipo de dato, la información que alberga, cómo acceder a cada sección de los datos almacenados y otras características de más bajo nivel que se escapan del ámbito de este libro.
En Python existen dos formas de especificar el valor de los datos, mediante literales y mediante variables.
2.1 LITERALES
Los literales son el resultado de las expresiones o las propias formas primitivas de cada dato que ocupan un espacio en memoria y pueden ser accedidas por el intérprete.
Un ejemplo de literal simple podrían ser los números (1, 24, 54, etc.), las cadenas de caracteres, que son texto rodeado de comillas simples, dobles o tres comillas (ejemplos: 'cadena de prueba', 'Python', etc.) o cualquier tipo de dato que se defina desde su forma literal.
Todos los objetos en Python tienen un identificador que define dónde se encuentra en memoria. Esta relación de identificador con posición o posiciones específicas de memoria es interna al intérprete, por lo que, a pesar de tener un identificador, no se puede saber con facilidad en qué zona de la memoria está alojado el valor que representa.
Si dos valores tienen el mismo identificador, ambos valores son exactamente el mismo, lo que permite ahorrar espacio en memoria. No obstante, esto conlleva un inconveniente: si se cambia el valor de referencia del identificador, el cambio afectará a todos los objetos que lo usen.
Para saber el identificador de un objeto se puede usar la función id, y para acceder a la información que contiene el objeto con ese identificador, se puede hacer uso de la librería ctypes, como se ve a continuación. Se muestra el identificador que el intérprete le ha dado al número 42 o el literal tipo carácter 'b':

Nota: el uso de estas operaciones tiene solo carácter educacional y los identificadores numéricos son diferentes en cada intérprete. Este código puede generar errores al intentar hacer operaciones con tipos de datos más complejos, como cadenas de varios caracteres.
2.2 VARIABLES E IDENTIFICADORES
Uno de los componentes clave para todos los lenguajes de programación son las variables. Las variables son instrumentos que permiten nombrar valores guardados en memoria, lo que permite al desarrollador hacer referencia a ellos no solo cuando son creados, sino más adelante en el programa. Las variables también permiten manipular los valores.
Un ejemplo simple sería la asignación de un nombre a un literal, como por ejemplo los siguientes:


En el ejemplo anterior, se asigna a los literales 23 y 'Nueva York' un nombre de variable que tiene un significado en el contexto en el que están:
23 es la puntuación obtenida y 'Nueva York' el nombre de una ciudad. Por tanto, estos objetos se pueden tratar de forma natural con nombres especificados por los desarrolladores.
Para nombrar variables en Python se utiliza la sintaxis de los identificadores y la convención de usar snake_case vista anteriormente, la cual define que los nombres deben estar en su forma minúscula o mayúscula (dependiendo de si son variables locales o constantes, respectivamente) y unidos por '_'. Pueden contener números, pero siempre empezarán por una letra.
La definición léxica de los identificadores es la siguiente:

Las categorías Unicode anteriormente mencionadas hacen referencia a:
- Lu - letras mayúsculas.
- Ll - letras minúsculas.
- Lt - letras en formato título.
- Lm - letras modificadoras.
- Lo - otras letras.
- Nl - letras numéricas.
- Mn - marcadores no espaciadores.
- Mc - marcadores combinando espacios.
- Nd - números decimales.
- Pc - conectores de puntuación.
- Other_ID_Start - lista explícita de caracteres en PropList.txt para soportar la retrocompatibilidad.
- Other_ID_Continue - demás caracteres.
Hay que tener en cuenta que Python hace distinción entre mayúsculas y minúsculas, por lo que nombres de variables como 'Casa', 'casa' o 'CASA' son completamente distintos. A continuación se muestran algunos ejemplos del uso de identificadores para nombrar variables válidas e inválidas:

Por otro lado, existen cadenas reservadas del lenguaje que no pueden ser usadas como nombres de variables, aunque sean identificadores válidos. Si se intenta asignar cualquier valor a una palabra reservada, se elevará una excepción tipo SyntaxError.
Estas cadenas reservadas son las siguientes:

Para poder inicializar el valor de una variable se utiliza el concepto de asignación, que se basa en utilizar el carácter '='. Esto permite asignar el contenido que hay en la parte derecha de la ecuación a la parte izquierda de la misma.

De este modo, el literal (o valor) se coloca en la parte derecha, y el nombre de la variable en la parte izquierda. Se puede hacer tan complejo o simple como sea necesario:

Es importante que las variables estén inicializadas con un valor antes de poder ser utilizadas, de lo contrario, al intentar utilizar la variable se elevará una excepción del tipo NameError reportando que el nombre de la variable no está definido.
Conceptualmente, en Python las variables se pueden entender como "etiquetas" de una "caja". La etiqueta sería el nombre de la variable y la caja, el valor de la variable. A diferencia de otros lenguajes de programación, en Python el valor no se guarda en la variable, sino que simplemente apunta hacia donde se encuentra almacenada, como se muestra en la siguiente imagen:

Figura 2.1 Imagen conceptual de una variable en Python.
En Python se puede hacer uso de las asignaciones múltiples, lo que permite tener varios nombres para un mismo valor e incluso asignar valores diferentes a variables diferentes. De todos modos, se recomienda no hacer un uso excesivo de ellas, dado que esto puede mermar la legibilidad del código (su facilidad de lectura):

2.3 GESTIÓN DE MEMORIA EN PYTHON
La gestión de memoria es clave en todos los lenguajes de programación, en especial en los de alto nivel, en los que no se fuerza al usuario a que mantenga la memoria lo más contenida posible. Hay que tener en cuenta este concepto para no crear programas que generen variables sin eliminarlas, dado que podría llenarse la memoria disponible. Cuando ocurre este fenómeno de manera descontrolada, se denomina fuga de memoria (memory leaks en inglés) y el programa que se está ejecutando se interrumpe repentinamente. También podría ocurrir que se ralentizaran los demás procesos de la máquina o incluso que se congelara el sistema completo.
Para evitar el uso excesivo de memoria y, principalmente, el llenado de memoria por variables u objetos que ya no se están utilizando en la ejecución actual de un programa, Python hace uso de una técnica denominada conteo de referencias.
El conteo de referencias se basa en que cada objeto del sistema tiene un contador de las referencias que apuntan al mismo. Dicho de un modo más simple: el contador cuenta cuántas variables apuntan a ese valor en concreto, ya sea directa o indirectamente.
El contador se aumenta mediante referencia directa (cuando se inicializa una variable o se pasa como parámetro a una función) o indirecta (cuando un objeto hace referencia a otro). Por el contrario, cuando una referencia desaparece (porque se elimina una variable o se cambia de contexto), se decrementa. Cuando el número de referencias es 0, ese espacio de memoria es liberado y deja espacio para cualquier otra variable que lo necesite.
Para borrar una variable en Python de forma intencionada, se utiliza la sentencia del, que elimina las referencias que esa variable tiene sobre el objeto. No obstante, no elimina todas las referencias, dado que puede haber otros objetos o variables que aún estén haciendo referencia al mismo.

Para ver el número de referencias que tiene un objeto, se puede utilizar getrefcount del paquete sys, aunque cabe destacar que el número de referencias aumenta cuando se pasa la variable como parámetro a una función y, por tanto, el número de referencias siempre es, como mínimo, 2:


Si el contador de referencias de un objeto llega a 0, se dice que se elimina la variable automáticamente, pero, realmente, el programa encargado de hacer esa tarea se encuentra implementado en la máquina virtual de Python y se denomina recolector de basura. El recolector de basura se encarga de liberar ese espacio en la memoria automáticamente, pero también se puede forzar su ejecución desde el código y manipularlo haciendo uso del módulo gc de la librería estándar (https://docs.python.org/3/library/gc.html).
Gracias a este sistema, los desarrolladores que utilizan Python no suelen preocuparse por la gestión de memoria como pasa en otros lenguajes, por ejemplo, C o C++. Como es lógico, esto facilita mucho el desarrollo.
Existe una herramienta que permite ver de forma rápida la relación entre los objetos, el número de objetos más usados y las potenciales fugas de memoria, entre otras cosas. Esta herramienta se llama objgraph (https://mg.pov.lt/objgraph/).
2.4 MUTABILIDAD DE VARIABLES
La mutabilidad define si un dato puede ser mutado (cambiado) tras ser inicializado o si siempre mantiene el mismo valor, tanto para la variable original como para las demás referencias que tenga el valor.
Este aspecto es muy importante a la hora de trabajar con objetos e intentar cambiar sus valores en cualquier parte del programa para evitar al máximo la aparición de efectos colaterales al actualizar una variable que se espera que no se actualice.
Este problema se hace patente principalmente cuando se intentan copiar objetos o pasar objetos como parámetros de funciones, dado que los objetos no mutables no permitirán cambios y, en algunas ocasiones, generarán objetos nuevos. Los mutables, sin embargo, cambiarán sus valores para todas las referencias que haya hacia ellos.


3 TIPOS BOOLEANOS
En Python, los conceptos de verdadero y falso están presentes y modelados como booleanos con dos valores constantes: True y False (con la primera letra en mayúscula).
El constructor de objetos tipo booleano es bool y permite convertir cualquier objeto en su valor:

En términos de veracidad de una variable u objeto, se puede decir que todos los objetos en Python se consideran verdaderos por defecto, salvo si el objeto define el método mágico __bool__ devolviendo False o el método __len__ devolviendo 0.
Algunos ejemplos de objetos evaluados como falsos son:
• Las constantes None y False
• Los valores numéricos interpretados por cero: 0, 0.0, 0j
• Objetos vacíos: '', "", (), dict(), set(), range(0), []
Existe una particularidad: los números booleanos están construidos como números enteros y pueden actuar como tales, operando con otros enteros. En cualquier caso, es recomendable no hacer operaciones con ellos, dado que puede desencadenar errores conceptuales en el programa. El valor verdadero (True) tiene el valor entero de 1, y el valor de falso (False) tiene el valor de 0.

3.1 OPERACIONES CON BOOLEANOS
Las operaciones lógicas con booleanos son tres: or, and y not, como se muestra en la siguiente tabla:

Cabe destacar que las operaciones and y or siempre devuelven uno de sus operandos, no simplemente True o False. La operación and devuelve el último de sus elementos, y la operación or, el primero que cumpla el cortocircuito lógico:

3.2 CORTOCIRCUITO LÓGICO
El cortocircuito lógico es una propiedad que implementa Python para la evaluación de expresiones y que, además, ayuda a hacerlas más eficientes, puesto que no necesita evaluar las expresiones completas, sino que, si durante la evaluación de una expresión and se encuentra un elemento que es falso, detiene la ejecución (cortocircuita la ejecución) y devuelve ese valor. Hace lo mismo con las expresiones or, pero con elementos que devuelvan verdadero.

En los dos ejemplos anteriores, al no estar definida la variable x, si Python no tuviera la lógica del cortocircuito lógico, las expresiones elevarían una excepción del tipo NameError. Sin embargo, se ejecutan con normalidad.
4 COMPARACIONES
En Python, los objetos se pueden comparar entre sí con los ocho tipos de operadores básicos:

Los operadores se pueden concatenar con múltiples variables en vez de tener que ser usados siempre por pares.

La prioridad de estas operaciones es la misma para todas y es superior a la de los operadores booleanos.
El operador == está siempre definido por defecto en el núcleo de Python, y en algunas ocasiones es similar al operador is, mientras que los operadores <, <=, > y >= solo están definidos cuando tienen sentido. Si no está definida la comparación entre dos objetos, se lanzará una excepción TypeError.
Para los tipos de datos iterables, existen los operadores in y not in, muy útiles para saber si un elemento se encuentra en el tipo de dato, como por ejemplo en listas o diccionarios.

Para poder hacer uso de la ordenación por defecto, el tipo de dato tiene que definir alguno de los métodos mágicos de comparación __lt__, __le__, __gt__ o __ge__, y para poder comparar correctamente dos objetos del mismo tipo, el método mágico necesario es __eq__. Por tanto, cuando se crean tipos de datos propios, solo es necesario implementar __lt__ y __eq__ para que el tipo tenga el soporte natural de todas las operaciones usando los operadores de comparación. Los métodos mágicos y la creación de objetos personalizados se verá en profundidad más adelante.
5 TIPOS NUMÉRICOS
En el núcleo de Python existen tres tipos numéricos definidos que permiten expresar los números enteros los números de coma flotante (números reales) y los números complejos de forma sencilla, así como operar con ellos.
5.1 OPERACIONES NUMÉRICAS BÁSICAS
Existen operaciones numéricas compartidas por todos los tipos numéricos en Python. Además, se pueden combinar objetos de tipos numéricos distintos, en cuyo caso prevalecerá el tipo más general.
Las operaciones numéricas básicas son las siguientes:

Como se puede ver en la tabla, se puede utilizar la forma reflexiva cuando se pretende aplicar la operación sobre la misma variable. Así, la operación queda simplificada visualmente.
5.2 ENTEROS
Los enteros son los valores numéricos más simples e intuitivos. Son del tipo int y la función constructora comparte el mismo nombre y representa números tanto negativos como positivos más el 0.

En Python 3 los enteros pueden ser tan grandes como sea necesario, dado que no tienen un número máximo fijo asignado. No obstante, hay que tener en cuenta que todos los números se guardan en memoria y que la manipulación continuada de números grandes sin hacer una buena gestión de memoria puede hacer que esta se llene, provocando un bloqueo del sistema.
Para saber el tamaño de palabra que soporta el sistema, se puede consultar la constante sys.maxsize:

Para conocer las operaciones disponibles de cualquier objeto en Python, se utiliza el comando dir, el cual muestra todas las disponibles. Se pueden separar en dos tipos: los métodos mágicos y los métodos propios. Los métodos mágicos son fácilmente identificables, dado que están rodeados por dobles barras bajas, '__'.

A continuación, se detallan las operaciones disponibles para los enteros y se especifica para qué sirven y cómo se usan:
• int.as_integer_ratio(): devuelve un par de números enteros, los cuales tienen exactamente el mismo ratio que el entero original y un denominador positivo. En el caso de enteros, el numerador es siempre el número entero al que se le aplica la función, y el denominador es 1. Disponible desde Python 3.8.
• int.bit_length(): devuelve el número de bits necesarios para representar el entero asociado en binario, excluyendo el signo y los ceros del comienzo.
• int.from_bytes(bytes, byteorder, *, signed=False): devuelve la representación de enteros para el array de bytes pasado como parámetro bytes. Los parámetros son los siguientes:
- bytes: debe ser un objeto bytes o un iterable que produzca objetos tipo bytes.
- byteorder: determina el orden de bytes usado para representar el entero; puede ser de dos tipos: 'big' o 'little'. Si se utiliza 'big', los bytes más significativos se sitúan al inicio del parámetro bytes; si se utiliza 'little', los bytes más significativos se sitúan al final del parámetro bytes. Se puede obtener el orden usado en el sistema que lanza el código utilizando sys.byteorder.
- signed: indica si se usa el complemento o no para representar el entero.
• int.to_bytes(length, byteorder, *, signed=False): devuelve una cadena de bytes que representa al entero. Está disponible desde Python 3.2. Los parámetros disponibles para esta función son:
- length: define la longitud de los arrays devueltos por la función para representar el entero asociado.
- byteorder: determina el orden de bytes usado para representar el entero; puede ser de dos tipos: 'big' o 'little'. Si se utiliza 'big', los bytes más significativos se sitúan al inicio del parámetro bytes; si se utiliza 'little', los bytes más significativos se sitúan al final del parámetro bytes. Se puede obtener el orden usado en el sistema que lanza el código utilizando sys.byteorder.
- signed: indica si se usa el complemento o no para representar el entero. Si el argumento para este parámetro es False, pero el número asociado es un numero negativo, se elevará una excepción del tipo OverflowError.
• int.conjugate(): devuelve el número complejo conjugado del número. En el caso de enteros, el conjugado siempre es el propio número entero.
• int.numerator: devuelve el numerador del número. En el caso de enteros, el numerador siempre es el propio número entero.
• int.denominator: devuelve el denominador del número. En el caso de enteros, siempre se devuelve 1.
• int.imag: devuelve la parte imaginaria del número. En el caso de enteros, la parte imaginaria siempre es 0.
• int.real: devuelve la parte real del número. En el caso de enteros, el numerador siempre es el propio número entero.
Las funciones as_integer_ratio, conjugate, denominator, imag, real y numerator están presentes en todos los números enteros por la propia implementación que se ha hecho de los tipos numéricos en Python, pero son realmente útiles en otros tipos numéricos cuyos valores no son tan predecibles.
A continuación, se muestran algunos ejemplos del uso de estas funciones sobre números enteros:

Para ayudar a la representación de enteros, en recientes versiones de Python se ha añadido la posibilidad de utilizar el carácter '_' para separar bloques numéricos en literales enteros. El carácter será omitido internamente, pero es muy útil para mejorar la legibilidad del código.

El constructor int permite crear objetos tipo entero desde cadenas de caracteres que representen números de cualquier base en enteros decimales (enteros de base 10) de forma fácil y sencilla. Simplemente se ha de especificar la base original en la que se encuentra el número de la cadena de caracteres.

Adicionalmente, existen las funciones bin, oct y hex, que permiten hacer la conversión inversa, pasar de un entero en base 10 a su representación en cada una de las bases.

Cabe destacar que, para expresar números en binario, octal o hexadecimal, se utilizan, respectivamente, los prefijos 0b, 0o y 0x en minúsculas o mayúsculas.
5.3 OPERACIONES A NIVEL DE BITS CON ENTEROS
Python permite realizar operaciones a nivel de bits. Son operaciones resultantes de la manipulación de los números en su representación binaria, moviendo los bits que los componen de diferente forma.

A continuación, se verán ejemplos de los operadores con valores específicos. Se han usado los números 47 y 86 en base 10 con su representación binaria, 0b101111 y 0b1010110 respectivamente.

5.4 NÚMEROS DE COMA FLOTANTE
Los números de coma flotante (o números reales) forman parte del conjunto de tipos numéricos implementado en Python y permiten realizar operaciones de forma fácil y sencilla gracias a las operaciones que hay disponibles en el núcleo de Python.
El constructor y el tipo de esta clase de números es float, que permite no solo la representación de números con decimales, sino la representación en notación científica de forma fácil.

En Python, los números de coma flotante tienen una precisión fija. Se puede consultar en la variable del sistema sys.float_info, que muestra esta información y otras como los números máximos y mínimos permitidos, los máximos y mínimos exponentes soportados, los dígitos decimales usados por defecto y demás variables de interés.

Como de costumbre, si se utiliza la función dir sobre un objeto tipo float, se obtiene la lista de operaciones implementadas para este tipo de datos. A continuación, se verá cada una de las operaciones en detalle:


• float.as_integer_ratio(): devuelve un par de enteros, los cuales tienen exactamente el mismo ratio que el float original con denominador positivo. Si se usa sobre infinito, elevará una excepción del tipo OverflowError, y si se usa en NaN, se elevará una excepción ValueError.
• float.is_integer(): devuelve True si el número de coma flotante al que se le aplica la operación es finito y entero, de lo contrario, devuelve False.
• float.fromhex(hex_string): devuelve un objeto tipo float representado por la cadena de caracteres pasada como parámetro a la función. La cadena puede contener espacios al principio o al final. Las cadenas hexadecimales soportadas deben tener el formato:

- sign: puede ser – o +.
- integer y fraction: son cadenas de caracteres de dígitos hexadecimales.
- exponent: es un número decimal y puede comenzar por un signo de forma opcional. Este número representa la potencia de 2 por la que se multiplica el coeficiente.
• float.hex(): devuelve la representación de un número de coma flotante como cadena de caracteres. Para números de coma flotante finitos, la representación incluye '0x' y, como sufijo, el carácter 'p' y el número usado como exponente.
• float.real: devuelve la parte real de un número. En el caso de float siempre devuelve el número de coma flotante al que se le aplica la operación, dado que todo el número representa la parte real del mismo.
• float.imag: devuelve la parte imaginaria de un número. En el caso de float siempre devuelve 0.0, puesto que no tiene parte imaginaria.
• float.conjugate(): devuelve el número complejo conjugado del número. En el caso de float, el conjugado siempre es el propio número de coma flotante.
A continuación, se muestran algunos ejemplos del uso de las operaciones disponibles para objetos tipo float en Python.

Una gran ayuda a la hora de trabajar con números flotantes es la posibilidad de utilizar la notación científica. En Python, el tipo float lo permite de forma nativa. Técnicamente esta notación se puede definir como sigue:

• integer y float: son los números base del número de coma flotante
• e: es una constante necesaria que representa este tipo de notación científica.
• sign: es un término opcional si se pretende crear un número positivo, pero es obligatorio si se quiere representar un número negativo. Los valores permitidos son – o +.
• exp_integer: es un número entero que representa el valor del exponente representado.
A continuación, se muestran algunos ejemplos de esta útil anotación:

Existen dos constantes especiales para float: Inf, que representan el infinito, y NaN, que representa que un número no es numérico.
• Inf: representa el número más grande (positivo o negativo) que se puede representar.
• NaN: representa números que no tienen representación numérica, como una división por 0 o la raíz cuadrada de un número negativo.

En la librería estándar de Python hay varias librerías que extienden las funcionalidades básicas de los float añadiendo constantes comúnmente utilizadas y operaciones complejas.
5.5 NÚMEROS COMPLEJOS
Los números complejos son una extensión de los números reales. Permiten hacer operaciones que no son posibles con lo números reales, como por ejemplo resolver la ecuación cuya solución sería x = i, siendo i un número imaginario.
Los números complejos tienen una parte real y una parte imaginaria. En Python están implementados igual que el resto de números, con la única diferencia de que la parte imaginaria se nombra añadiendo una 'j' o 'J' al lado del número que se quiera catalogar como imaginario (y no una i, como es costumbre).

El constructor de los números complejos en Python es complex. Se pueden hacer operaciones básicas sin necesidad de utilizar una librería externa, por ejemplo, sumas, multiplicaciones o elevar un número a otro, tanto si son números complejos como si es una combinación de complejos con enteros o reales.

Las operaciones disponibles para este tipo de dato se pueden obtener haciendo uso de la función dir aplicacada a una instancia de esta clase:

• complex.real: devuelve la parte real del número complejo asociado.
• complex.imag: devuelve la parte imaginaria del número complejo asociado.
• complex.conjugate(): devuelve el número complejo conjugado del número asociado.
A continuación, se muestran ejemplos de uso de esas operaciones:

En la librería estándar de Python se pueden encontrar librerías espécificas para el uso de números complejos, por ejemplo, cmath (https://docs.python.org/3/library/cmath.html).
5.6 UTILIZAR DISTINTOS TIPOS NUMÉRICOS
En Python se pueden realizar operaciones con los distintos tipos numéricos sin necesidad de cambiar el tipo de la variable usada constantemente, por ejemplo, se pueden sumar enteros con reales o con números complejos, o dividir reales y enteros.
El aspecto que hay que tener en cuenta es que, cuando se hacen este tipo de operaciones, el resultado final está en el tipo de dato más permisivo. Ordenados de menos a más, según su permisividad: int, float y complex.

6 SECUENCIAS
En esta sección se verán distintos tipos básicos basados en secuencias, como listas, tuplas y rangos, que están presentes en el núcleo de Python.
Los tipos de datos basados en secuencias se caracterizan por ser una concatenación de elementos (del mismo tipo o de tipos distintos) dispuestos de forma secuencial que permiten hacer operaciones de forma simple e intuitiva.
De forma abstracta y simple, se podría decir que los tipos secuencia son como un mueble que tiene N cajones, y todos están dispuestos uno detrás de otro. Cada cajón puede almacenar un tipo distinto de elemento y, según la naturaleza del mueble, que posee unas características propias, se podría expandir dinámicamente o tendría una longitud fija o se podría iterar de una determinada manera.
Para que un objeto se considere secuencia en Python, debe implementar el protocolo de secuencia. Para ello, la clase del objeto debe implemetar el método mágico __getitem__, basado en números enteros comenzando desde el 0, lo que permite que todos los objetos puedan ser accedidos usando el índice de la posición a la que se quiere acceder con la función index y el método mágico __len__, el cual permite saber la longitud de la secuencia. Esto permite usar operaciones propias de secuencias como len o count.
6.1 LISTAS
Las listas son secuencias de elementos de cualquier tipo y sin límite de longitud. El constructor y el tipo de dato es list en Python, aunque existe una representación más usual: una secuencia de elementos separados por comas y rodeados por corchetes.

Para conocer todas las operaciones disponibles en las listas, se puede utilizar la función dir aplicada sobre una lista:

A continuación, se presentan las diferentes operaciones disponibles para la manipulación de listas en Python (lst1 es un objeto de tipo lista; elem, un objeto arbitrario; iter1, un iterable diferente a lst1, y pos, un número entero que define la posición dentro de la lista):
• lst1.clear(): elimina todos los elementos de lst1. Es equivalente a del lst1[:].
• lst1.copy(): devuelve una copia superficial de lst1. Es equivalente a lst1[:]. Esta copia superficial solo copia las referencias de variables del primer nivel, por lo que, si los objetos o variables de niveles más profundos cambian, afectará a la copia y al original. Si se pretende hacer una copia real, es recomendable el uso de la librería copy usando las funciones copy.copy o copy.deepcopy.
• lst1.count(elem): cuenta el número de veces que el elemento elem aparece en la lista lst1.
• lst1.extend(iter1): extiende la lista lst1 añadiendo todos los elementos del iterable iter1. Esta función es equivalente a lst1[len(lst1):] = iter1.
• lst1.index(elem[, inicio[, final]]): devuelve el índice de la posición que ocupa el elemento elem dentro de la lista lst1. El índice comienza por 0. Si se especifican los parámetros opcionales "inicio" o "final", estos determinarán la zona en la que se debe buscar el elemento elem, aunque el índice devuelto siempre es relativo al inicio de la lista. Si elem no se encuentra en la lista o en la sublista especificada, se elevará una excepción del tipo ValueError.
• lst1.insert(pos, elem): inserta elem en la posición anterior definida por el índice pos. Por ejemplo, lst1.insert(0, elem) insertaría elem al inicio de la lista, y lst1.insert(len(lst1), elem) lo insertaría al final de la misma. Este último sería equivalente a lst1.append(elem).
• lst1.pop([pos]): elimina y devuelve el elemento de la posición definida por pos. El parámetro es opcional y, si se omite, se devuelve y elimina el último elemento de la lista.
• lst1.remove(elem): elimina la primera ocurrencia de elem en la lista. Si no existe ninguna ocurrencia de elem en la lista, se elevará una excepción del tipo ValueError.
• lst1.reverse(): invierte el orden de los elementos de la lista en el sitio (no genera una nueva lista, sino que lo hace en la misma; este método es conocido como reverse in place).
• lst1.sort(key=None, reverse=False): ordena la lista lst1 en el sitio. En el parámetro key se puede añadir cualquier función que se utilice al ordenar, y el parámetro reverse se utiliza para que el orden sea inverso o no. Por lo general, en el parámetro key se utiliza un tipo de función anónima denominada lambda.
A continuación, se muestran ejemplos en los que se utilizan las operaciones disponibles en listas:



Usando la función copy sobre listas solo se hace una copia superficial, por lo que los elementos más allá del primer nivel, como puede ser una lista dentro de otra lista, no se copian como copias, sino como referencias. Esto hace que realmente sigan estando enlazadas entre sí, aunque estén en dos variables distintas.


Sin embargo, si se utiliza una función que copie en profundidad, como copy.deepcopy, este problema desaparece:

Gracias a su gran facilidad de uso, las listas pueden crearse fácilmente y crecer muchísimo, pero hay que tener en cuenta que son objetos que se guardan en la memoria del sistema. Si no se hace un buen uso de los recursos disponibles o se usan listas realmente grandes con miles o millones de elementos, se puede provocar un bloqueo por falta de memoria.
Un claro ejemplo es que las listas se pueden concatenar con otras listas y crear nuevos objetos de tipo lista, por lo que hay que tener cuidado de no generar demasiados objetos y utilizar demasiada memoria. Para conocer el espacio que ocupa un objeto en memoria, se puede hacer uso de la función sys.getsizeof.


Para eliminar cualquier objeto y, en particular, cualquier elemento de una lista, se puede usar el comando del en la posición (o posiciones) que el objeto ocupa en la lista.

6.2 TUPLAS
Una tupla es un tipo de secuencia similar a una lista, pero es inmutable, por lo que, una vez inicializada, no se puede cambiar ninguno de sus elementos sin generar un nuevo objeto.
En Python, el tipo de las tuplas coindice con el constructor, en este caso, tuple. Asimismo, por defecto, cuando se añaden dos valores separados por una coma en cualquier parte de un código Python, estos son interpretados como una tupla de N elementos, y también ocurre si se deja una coma al final de la sentencia como se puede ver a continuación:


El uso de las tuplas en memoria es más eficiente que el de las listas, ya que el espacio que requieren está definido. Sin embargo, al ser inmutables, si se intenta cambiar o eliminar cualquier valor, se elevará una excepción y se necesitará crear otra y borrar la actual.
Las funciones asociadas a este tipo de dato son más reducidas en comparación con las que están disponibles en las listas. Se ofrecen solamente las funciones count e index aparte de las funciones propias de las secuencias en Python.

A continuación, se muestra la definición de las operaciones disponibles para tuplas (tp1 hace referencia a una tupla y elem, a un elemento arbitrario):
• tp1.count(elem): cuenta el número de veces que el elemento elem aparece en la tupla tp1.
• tp1.index(elem[, inicio[, final]]): devuelve el índice de la posición que ocupa el elemento elem dentro de la tupla tp1. El índice comienza por 0. Si se especifican los parámetros opcionales "inicio" o "final", estos determinarán la zona en la que se debe buscar el elemento elem, aunque el índice devuelto siempre es relativo al inicio de la tupla. Si elem no se encuentra en la tupla o en la subsecuencia especificada, se elevará una excepción del tipo ValueError.
A continuación, se muestran algunos ejemplos en los que se utilizan las operaciones de tuplas.


6.3 RANGOS
Un tipo particular de secuencias son los rangos, que son secuencias de números enteros inmutables predefinidas en Python. Se crean utilizando el constructor range, que también determina el tipo de dato:
• range(final): el parámetro final se define mediante un número entero y determina hasta dónde debe generar números el rango, comenzando desde el número 0 y sumando uno a cada paso.
• range(inicio, final[, paso]): el parámetro inicio define el número entero en el que el rango debe comenzar a contar, y el parámetro final define el máximo valor que puede tener el rango. Por defecto, el valor para añadir en cada iteración es 1, pero se puede ajustar utilizando el parámetro paso. Si el paso definido es 0, se eleva una excepción del tipo ValueError.
Un rango termina de producir elementos cuando el elemento actual más el paso es mayor que el final. Cabe destacar que los índices pueden ser negativos y tan grandes como se desee, incluso mayores que sys.maxsize. No obstante, al utilizarse con otras funciones, como len, pueden elevar una excepción del tipo OverflowError.

Las operaciones soportadas por los rangos son las siguientes (rang1 hace referencia a un objeto tipo range, y num, a un entero):
• rang1.count(num): devuelve el número de ocurrencias de num en el rango. Según la naturaleza del tipo range, solamente se tiene una ocurrencia, o ninguna, de cada número entero.
• rang1.index(num): devuelve el índice donde se encuentra el número num dentro del rango y, si no está presente, eleva una excepción del tipo ValueError.
• rang1.start: es un atributo que permite consultar el número con el que comienza el rango.
• rang1.step: es un atributo que permite consultar el paso numérico usado en el rango.
• rang1.stop: es un atributo que permite consultar el número máximo en el que el rango parará de generar nuevos números.
A continuación, se muestran algunos ejemplos de operaciones usando rangos:

Cabe mencionar que las comparaciones entre rangos se hacen comparando los valores que pueden generar los rangos, y no por los valores de start, stop y step. Por tanto, rangos que aparentemente pueden ser diferentes devolverán True al comparar si son iguales o no, como en los siguientes ejemplos:


En Python, los rangos son inmutables y presentan la gran ventaja de que son iteradores (concepto que se verá en profundidad más adelante), por lo que, cuando se crean, no guardan la información que representan, sino el procedimiento necesario para generar la secuencia de números. Esto hace que ocupen muy poco espacio y permitan controlar la memoria utilizada en los programas, ya que van generando los valores de uno en uno y no todos a la vez.
Los tipos range también implementan el protocolo de secuencia basado en collections.abc.Sequence, lo que permite acceder a un elemento en concreto del rango utilizando un índice, usar operaciones como len o in, e incluso usar selecciones de subsecuencias (se verá en profundidad en el siguiente apartado).

6.4 SELECCIÓN DE SUBSECUENCIAS BASADAS EN ÍNDICES (SLICES)
Una peculiaridad de los objetos que implementan el protocolo de secuencia en Python (ver collections.abc.Sequence) es que pueden ser seleccionados por partes de la secuencia utilizando los índices a modo de subsecuencias. De esta forma, si tenemos una lista, tupla, rango o cualquier tipo de secuencia de N elementos, se pueden elegir desde 0 hasta N elementos y definir tanto el orden como el paso (de manera opcional).
Técnicamente, la selección de elementos basadas en el índice se puede definir como sigue:

Como se puede observar en la definición, desde un objeto que implementa el protocolo de secuencia se puede acceder a los elementos en la posición de start usando la sintaxis marcada con "[" y "]". Opcionalmente, se pueden definir el número de parada y el paso que seguir en cada iteración.
Teniendo un objeto del tipo secuencia (sec), un número entero que se utiliza como índice de la secuencia (ind), un número entero para determinar el inicio de la subsecuencia (i_inicio), un entero para determinar el final de la subsecuencia (i_fin) y un número paso que determina el paso a sumar en cada iteración hasta llegar a i_fin, se pueden hacer las siguientes operaciones para obtener subsecuencias de la secuencia sec.
• sec[ind]: selección de un único elemento, el de la posición marcada por ind.
• sec[-ind]: selección de un único elemento, el de la posición ind comenzando a contar desde el fin de la secuencia.
• sec[i_inicio:i_fin]: selección de subsecuencia desde la posición i_inicio hasta i_fin.
• sec[i_inicio:]: selección de subsecuencia desde la posición i_inicio hasta el final de la secuencia con paso 1.
• sec[i_inicio:i_fin:paso]: selección de subsecuencia desde la posición i_inicio hasta la posición i_fin. Se define un paso para ir sumando en cada iteración hasta llegar a i_fin. El paso puede ser negativo.
• sec[:i_fin]: selección de la secuencia hasta i_fin. Similar a sec[0:i_fin].
• sec[:i_fin:paso]: selección de la secuencia hasta i_fin, pero con un paso que será sumado en cada iteración hasta llegar a i_fin.
• sec[::paso]: toda la secuencia, pero utilizando el paso para recorrerla.
• sec[::]: copia de la secuencia de forma superficial, similar a sec[::1].
Los pasos pueden ser negativos. En ese caso, actúan de la misma forma, pero recorriendo la secuencia en orden inverso.

6.5 OPERACIONES PREDEFINIDAS PARA SECUENCIAS
Las secuencias son iterables y, por tanto, aparte de las mencionadas anteriormente, tienen algunas operaciones especiales que ayudan a trabajar con ellas de forma fácil y sencilla.

7 SECUENCIAS DE CARACTERES
Un tipo particular de secuencias son las secuencias de caracteres, también conocidas como cadenas de caracteres o, simplemente, cadenas.
Los caracteres se crearon como herramienta para representar más información que los números, y facilitan la expresión de conceptos complejos en lenguaje natural. Pueden tener diferentes tamaños, soportar diferentes codificaciones y, en definitiva, plasmar mejor un lenguaje parecido a la comunicación que realizan los humanos.
La particularidad de estas secuencias es que su unidad mínima es un carácter perteneciente a la tabla Unicode. Además, son secuencias inmutables. Por tanto, una vez creadas, no se puede actualizar ninguno de los caracteres que tienen en cada posición. Esto obliga a generar una cadena nueva.
En Python 3, los caracteres y las secuencias de caracteres son del tipo str, aunque suelen crearse como literales de cadena (string literals). Esto requiere rodear una palabra o frase con comillas simples o dobles en bloques de una o de tres comillas, dependiendo de si se quiere obtener una simple frase o un párrafo donde haya saltos de línea.

Formalmente, las cadenas formadas con literales se definen de la siguiente manera:

• stringliteral: se basa en un prefijo del tipo stringprefix y una cadena shortstring (para una simple frase) o longstring (para un párrafo).
• stringprefix: representa cada uno de los prefijos disponibles para literales del tipo cadena. Pueden ser los siguientes o combinaciones de los mismos:
- R o r: representa que la cadena de caracteres es de tipo "crudo" (raw string) y que no deben respetarse los caracteres de escapado de caracteres que usan "\".
- U o u: representa que la cadena es de tipo Unicode. Este prefijo era muy utilizado en Python 2, pero en Python 3 todas las cadenas (sin prefijo) ya son Unicode por defecto. Está presente por retrocompatiblidad con Python 2, pero en desuso.
- F o f: representa una f-string o una cadena para formatear cadenas de caracteres. Este tipo de cadenas se introdujo recientemente en Python y es muy útil. Se verá en detalle en el apartado 7.10.
• shortstring: son cadenas de caracteres que pueden contener muchos o ningún carácter, tanto de tipo shortstringchar como de stringescapeseq. Están rodeadas usando una sola comilla simple o doble. Son la base de la construcción de palabras y frases de una sola línea.
• longstring: son cadenas de caracteres que pueden contener muchos o ningún carácter, tanto de tipo longstringchar como de stringescapeseq. Están rodeadas usando tres comillas simples o dobles. Son la base de la construcción de frases o párrafos de múltiples líneas.
• shortstringchar: cualquier carácter exceptuando el carácter de escape "\", el salto de línea y las comillas.
• longstringchar: cualquier carácter exceptuando el carácter de escape "\".
• stringescapeseq: cualquier carácter escapado utilizando el carácter "\".
Al existir dos tipos de comillas, se pueden crear frases en las que se use un tipo u otro de comillas sin tener que escapar los caracteres utilizando \" o \'. Si se pretende usar el carácter de comilla doble, se rodea la frase con comillas simples y viceversa.

Cuando se quieren crear frases con más de una línea (párrafos), se pueden usar comillas simples con el carácter de salto de línea de Python (\n) y el carácter que simboliza que una línea no ha terminado (\). Sin embargo, alternativamente, se utiliza un bloque de tres comillas (dobles o simples) para poder escribir el párrafo de manera simple y mejorar la legibilidad del código.
Estos bloques de tres comillas también son utilizados cuando se quieren añadir los dos tipos de comillas en la misma frase.

Puesto que hay diferentes formas de representar las cadenas, se pueden crear cadenas vacías usando cada método, pero todas ellas representan de igual manera la cadena vacía. La cadena vacía es evaluada como False en comparaciones.

Utilizando la función dir se pueden ver todas las operaciones disponibles para cadenas:


En la siguiente tabla se muestran todas las operaciones con una descripción simple, y más adelante se explica en detalle qué hace cada una de ellas.


A lo largo de los siguientes apartados se verán todas las operaciones en detalle, con ejemplos y separadas por bloques lógicos según la finalidad de cada operación.
7.1 CONSTRUIR CADENAS DE CARACTERES
El constructor de los objetos de cadenas de caracteres es str. Por defecto devuelve una cadena vacía, pero se le puede pasar como argumento cualquier tipo de dato, que intentará convertir en una cadena de caracteres como se explica a continuación:
• str(object=''): devuelve un objeto tipo str utilizando el parámetro object. Por defecto, utiliza una cadena vacía, por lo que crea también una cadena vacía. Para la creación del objeto tipo str, se le aplicará el método __str__ al objeto pasado como parámetro. Si este no dispone de ese método, se utilizará __repr__. A este método de crear objetos tipo string se le denomina informal.
• str(object=b'', encoding='utf-8', errors='strict'): si se añaden los parámetros encoding o errors, el objeto pasado como parámetro debe ser de tipo bytes o bytearray, y se devolverá el objeto tipo str resultante de la conversión utilizando estos parámetros. Este método es similar a usar bytes.decode(encoding, errors). Cabe destacar que, aunque el objeto sea del tipo bytes o bytearray, si no se añaden uno o los dos parámetros opcionales, la cadena resultante será la forma informal de representar tipos bytes en Python. Los posibles valores de errors son los siguientes:
- strict: valor por defecto que intentará hacer la codificación elegida en el parámetro encoding. Si no es posible, elevará una excepción del tipo ValueError.
- ignore: usando esta opción se ignorará cualquier error producido.
- replace: reemplaza el carácter mal formado por '?'.
- backslashreplace: reemplaza el carácter mal formado por su representación escapada usando '\'.
- xmlcharrefreplace: reemplaza el carácter mal formado por su referencia apropiada en XML.
- namereplace: reemplaza el carácter mal formado por su representación en texto plano en Unicode usando \N{…}.
- Nota: estos valores están disponibles por defecto, pero se pueden aplicar manejadores de errores propios usando la función codecs.register_error().
A continuación, se muestran ejemplos de construcción de cadenas de caracteres en Python:

Cabe mencionar que, por defecto, si hay dos cadenas de caracteres en el mismo contexto, se concatenarán sin necesidad de marcarlo explícitamente:

Darmowy fragment się skończył.