0% encontró este documento útil (0 votos)
354 vistas

El Tutorial de Python1 PDF

Cargado por

Arturo Perdomo
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
354 vistas

El Tutorial de Python1 PDF

Cargado por

Arturo Perdomo
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 166

El tutorial de Python

Python es un potente lenguaje de programación fácil de aprender. Tiene estructuras


de datos eficientes de alto nivel y un enfoque simple pero efectivo para la
programación orientada a objetos. La elegante sintaxis y la escritura dinámica de
Python, junto con su naturaleza interpretada, lo convierten en un lenguaje ideal para
la creación de scripts y el rápido desarrollo de aplicaciones en muchas áreas en la
mayoría de las plataformas.

El intérprete de Python y la extensa biblioteca estándar están disponibles


gratuitamente en formato fuente o binario para todas las plataformas principales
desde el sitio web de Python, https://www.python.org/ , y pueden distribuirse
libremente. El mismo sitio también contiene distribuciones y referencias a muchos
módulos, programas y herramientas de Python gratuitos de terceros, y
documentación adicional.

El intérprete de Python se amplía fácilmente con nuevas funciones y tipos de datos


implementados en C o C ++ (u otros lenguajes invocables desde C). Python también
es adecuado como lenguaje de extensión para aplicaciones personalizables.

Este tutorial presenta al lector de manera informal los conceptos y características


básicos del lenguaje y sistema Python. Es útil tener un intérprete de Python a mano
para la experiencia práctica, pero todos los ejemplos son independientes, por lo que
el tutorial también se puede leer fuera de línea.

Para obtener una descripción de los objetos y módulos estándar, consulte La


biblioteca estándar de Python . La Referencia del lenguaje Python ofrece una
definición más formal del lenguaje. A las extensiones de escritura en C o C ++,
leer Extensión y empotrado del intérprete de Python y Python / C Manual de
Referencia de la API . También hay varios libros que cubren Python en profundidad.

Este tutorial no intenta ser completo y cubrir todas las funciones, o incluso todas las
funciones de uso común. En cambio, presenta muchas de las características más
notables de Python y le dará una buena idea del sabor y estilo del lenguaje. Después
de leerlo, podrá leer y escribir módulos y programas de Python, y estará listo para
aprender más sobre los diversos módulos de la biblioteca de Python descritos en La
biblioteca estándar de Python .

1. Abriendo el apetito
Si trabaja mucho en las computadoras, eventualmente encontrará que hay alguna
tarea que le gustaría automatizar. Por ejemplo, es posible que desee realizar una
búsqueda y reemplazo en una gran cantidad de archivos de texto, o cambiar el
nombre y reorganizar un montón de archivos de fotos de una manera
complicada. Quizás le gustaría escribir una pequeña base de datos personalizada,
una aplicación GUI especializada o un juego simple.

Si eres un desarrollador de software profesional, es posible que tengas que trabajar


con varias bibliotecas C / C ++ / Java, pero el ciclo habitual de escritura / compilación
/ prueba / recompilación es demasiado lento. Quizás esté escribiendo un conjunto
de pruebas para una biblioteca de este tipo y encuentre que escribir el código de
prueba es una tarea tediosa. O tal vez ha escrito un programa que podría usar un
lenguaje de extensión y no desea diseñar e implementar un lenguaje
completamente nuevo para su aplicación.

Python es solo el lenguaje para ti.

Puede escribir un script de shell de Unix o archivos por lotes de Windows para
algunas de estas tareas, pero los scripts de shell son mejores para mover archivos
y cambiar datos de texto, no son adecuados para juegos o aplicaciones GUI. Podría
escribir un programa C / C ++ / Java, pero puede llevar mucho tiempo de desarrollo
obtener incluso un programa de primer borrador. Python es más fácil de usar, está
disponible en los sistemas operativos Windows, Mac OS X y Unix, y lo ayudará a
hacer el trabajo más rápidamente.

Python es fácil de usar, pero es un lenguaje de programación real, que ofrece mucha
más estructura y soporte para programas grandes de lo que pueden ofrecer los
scripts de shell o los archivos por lotes. Por otro lado, Python también ofrece mucha
más verificación de errores que C y, al ser un lenguaje de muy alto nivel , tiene tipos
de datos de alto nivel integrados, como matrices flexibles y diccionarios. Debido a
sus tipos de datos más generales, Python es aplicable a un dominio de problemas
mucho más grande que Awk o incluso Perl, sin embargo, muchas cosas son al
menos tan fáciles en Python como en esos lenguajes.
Python le permite dividir su programa en módulos que se pueden reutilizar en otros
programas de Python. Viene con una gran colección de módulos estándar que
puede usar como base de sus programas, o como ejemplos para comenzar a
aprender a programar en Python. Algunos de estos módulos proporcionan cosas
como E / S de archivos, llamadas al sistema, sockets e incluso interfaces para kits
de herramientas de interfaz gráfica de usuario como Tk.

Python es un lenguaje interpretado, que puede ahorrarle un tiempo considerable


durante el desarrollo del programa porque no es necesario compilar ni vincular. El
intérprete se puede utilizar de forma interactiva, lo que facilita la experimentación
con las características del lenguaje, la escritura de programas desechables o la
prueba de funciones durante el desarrollo de programas de abajo hacia
arriba. También es una práctica calculadora de escritorio.

Python permite que los programas se escriban de forma compacta y legible. Los
programas escritos en Python suelen ser mucho más cortos que los programas
equivalentes en C, C ++ o Java, por varias razones:

 los tipos de datos de alto nivel le permiten expresar operaciones complejas


en una sola declaración;
 la agrupación de sentencias se realiza mediante sangría en lugar de
corchetes al principio y al final;
 no son necesarias declaraciones de variables o argumentos.

Python es extensible : si sabe cómo programar en C, es fácil agregar una nueva


función incorporada o módulo al intérprete, ya sea para realizar operaciones críticas
a la máxima velocidad o para vincular programas de Python a bibliotecas que
pueden estar solo disponibles en formato binario (como una biblioteca de gráficos
específica del proveedor). Una vez que esté realmente enganchado, puede vincular
el intérprete de Python a una aplicación escrita en C y usarla como una extensión o
lenguaje de comandos para esa aplicación.

Por cierto, el lenguaje lleva el nombre del programa de la BBC "Monty Python's
Flying Circus" y no tiene nada que ver con los reptiles. Hacer referencias a las
parodias de Monty Python en la documentación no solo está permitido, ¡es
recomendable!
Ahora que está entusiasmado con Python, querrá examinarlo con más detalle. Dado
que la mejor manera de aprender un idioma es usarlo, el tutorial lo invita a jugar con
el intérprete de Python mientras lee.

En el siguiente capítulo, se explica la mecánica del uso del intérprete. Esta es una
información bastante mundana, pero esencial para probar los ejemplos que se
muestran más adelante.

El resto del tutorial presenta varias características del lenguaje y sistema Python a
través de ejemplos, comenzando con expresiones simples, declaraciones y tipos de
datos, a través de funciones y módulos, y finalmente tocando conceptos avanzados
como excepciones y clases definidas por el usuario.

2. Usando el intérprete de Python


2.1. Invocar al intérprete
El intérprete de Python generalmente se instala
como /usr/local/bin/python3.8 en aquellas máquinas donde está
disponible; poner /usr/local/binen la ruta de búsqueda de su shell de Unix hace
posible iniciarlo escribiendo el comando:

python3.8

a la cáscara. 1 Dado que la elección del directorio donde vive el intérprete es una
opción de instalación, son posibles otros lugares; consulte con su gurú de Python
local o administrador del sistema. (Por ejemplo, /usr/local/pythones una
ubicación alternativa popular).

En máquinas con Windows en las que haya instalado Python desde Microsoft Store ,
el python3.8comando estará disponible. Si tiene instalado el lanzador py.exe ,
puede usar el py comando. Consulte Excursus: Configuración de variables de
entorno para conocer otras formas de iniciar Python.

Escribir un carácter de fin de archivo ( Control-Den Unix, Control-Zen Windows) en


el indicador principal hace que el intérprete salga con un estado de salida cero. Si
esto no funciona, puede salir del intérprete escribiendo el comando
siguiente: quit().
Las funciones de edición de línea del intérprete incluyen edición interactiva,
sustitución de historial y finalización de código en sistemas que admiten
la biblioteca GNU Readline . Quizás la verificación más rápida para ver si se admite
la edición de línea de comandos es escribir Control-Pen el primer indicador de
Python que obtiene. Si emite un pitido, tiene edición de línea de comandos; consulte
el Apéndice Edición de entrada interactiva y sustitución del historial para obtener
una introducción a las teclas. Si no parece suceder nada, o si ^Pse repite, la edición
de la línea de comandos no está disponible; solo podrá utilizar la tecla de retroceso
para eliminar caracteres de la línea actual.

El intérprete funciona como el shell de Unix: cuando se le llama con una entrada
estándar conectada a un dispositivo tty, lee y ejecuta comandos de forma
interactiva; cuando se llama con un argumento de nombre de archivo o con un
archivo como entrada estándar, lee y ejecuta un script desde ese archivo.

Una segunda forma de iniciar el intérprete es , que ejecuta la (s) instrucción (es) en
el comando , de forma análoga a la opción del shell . Dado que las declaraciones
de Python a menudo contienen espacios u otros caracteres que son especiales para
el shell, generalmente se recomienda citar el comando en su totalidad con comillas
simples.python -c command [arg] ...-c

Algunos módulos de Python también son útiles como scripts. Estos se pueden
invocar usando , que ejecuta el archivo fuente para el módulo como si hubiera
escrito su nombre completo en la línea de comando. python -
m module [arg] ...

Cuando se usa un archivo de secuencia de comandos, a veces es útil poder ejecutar


la secuencia de comandos y luego ingresar al modo interactivo. Esto se puede hacer
pasando -i antes del script.

Todas las opciones de la línea de comandos se describen en Línea de comandos y


entorno .

2.1.1. Pasar argumentos

Cuando el intérprete lo conoce, el nombre del script y los argumentos adicionales a


partir de entonces se convierten en una lista de cadenas y se asignan a
la argv variable en el sysmódulo. Puede acceder a esta lista ejecutando . La
longitud de la lista es al menos uno; cuando no se proporciona ningún script ni
argumentos, es una cadena vacía. Cuando el nombre del script se da como (es
decir, entrada estándar), se establece en . Cuando se usa el comando , se establece
en . Cuando se utiliza módulo , se establece en el nombre completo del módulo
ubicado. Las opciones que se encuentran después del comando o módulo no son
consumidas por el procesamiento de opciones del intérprete de Python, sino que se
dejan enimport syssys.argv[0]'-'sys.argv[0]'-'-c sys.argv[0]'-c'-
m sys.argv[0]-c -m sys.argv para que el comando o módulo lo maneje.

2.1.2. Modo interactivo

Cuando los comandos se leen desde un tty, se dice que el intérprete está en modo
interactivo . En este modo, solicita el siguiente comando con el indicador principal ,
generalmente tres signos mayores que ( >>>); para las líneas de continuación, lo
solicita con el indicador secundario , por defecto tres puntos ( ...). El intérprete
imprime un mensaje de bienvenida que indica su número de versión y un aviso de
derechos de autor antes de imprimir el primer mensaje:

$ python3.8
Python 3.8 (default, Sep 16 2015, 09:25:04)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more
information.
>>>

Se necesitan líneas de continuación al ingresar a una construcción de varias


líneas. Como ejemplo, eche un vistazo a esta ifdeclaración:

>>>

>>> the_world_is_flat = True


>>> if the_world_is_flat:
... print("Be careful not to fall off!")
...
Be careful not to fall off!

Para obtener más información sobre el modo interactivo, consulte Modo interactivo .
2.2. El intérprete y su entorno
2.2.1. Codificación de código fuente

De forma predeterminada, los archivos fuente de Python se tratan como codificados


en UTF-8. En esa codificación, los caracteres de la mayoría de los idiomas del
mundo se pueden usar simultáneamente en cadenas literales, identificadores y
comentarios, aunque la biblioteca estándar solo usa caracteres ASCII para
identificadores, una convención que debe seguir cualquier código portátil. Para
mostrar todos estos caracteres correctamente, su editor debe reconocer que el
archivo es UTF-8 y debe usar una fuente que admita todos los caracteres del
archivo.

Para declarar una codificación diferente a la predeterminada, se debe agregar una


línea de comentario especial como la primera línea del archivo. La sintaxis es la
siguiente:

# -*- coding: encoding -*-

donde la codificación es una de las válidas codecsadmitidas por Python.

Por ejemplo, para declarar que se va a utilizar la codificación Windows-1252, la


primera línea de su archivo de código fuente debe ser:

# -*- coding: cp1252 -*-

Una excepción a la regla de la primera línea es cuando el código fuente comienza


con una línea "shebang" de UNIX . En este caso, la declaración de codificación debe
agregarse como la segunda línea del archivo. Por ejemplo:

#!/usr/bin/env python3
# -*- coding: cp1252 -*-

Notas al pie

1
En Unix, el intérprete de Python 3.x no está instalado por defecto con el
ejecutable nombrado python, de modo que no entra en conflicto con un
ejecutable de Python 2.x instalado simultáneamente.

3. Una introducción informal a Python


En los siguientes ejemplos, la entrada y la salida se distinguen por la presencia o
ausencia de mensajes ( >>> y … ): para repetir el ejemplo, debe escribir todo
después del mensaje, cuando aparece el mensaje; las líneas que no comienzan con
un mensaje se generan en el intérprete. Tenga en cuenta que un indicador
secundario en una línea por sí solo en un ejemplo significa que debe escribir una
línea en blanco; esto se usa para finalizar un comando de varias líneas.

Muchos de los ejemplos de este manual, incluso los introducidos en el indicador


interactivo, incluyen comentarios. Los comentarios en Python comienzan con el
carácter hash #, y se extienden hasta el final de la línea física. Un comentario puede
aparecer al comienzo de una línea o después de un espacio en blanco o código,
pero no dentro de una cadena literal. Un carácter hash dentro de una cadena literal
es solo un carácter hash. Dado que los comentarios son para aclarar el código y
Python no los interpreta, pueden omitirse al escribir ejemplos.

Algunos ejemplos:

# this is the first comment


spam = 1 # and this is the second comment
# ... and now a third!
text = "# This is not a comment because it's inside quotes."

3.1. Usando Python como calculadora


Probemos algunos comandos simples de Python. Iniciar el intérprete y esperar a
que el indicador primario, >>>. (No debería tardar mucho).

3.1.1. Números

El intérprete actúa como una simple calculadora: puedes escribir una expresión en
él y escribirá el valor. Sintaxis de expresión es sencillo: los operadores +, -
, *y /funcionan igual que en la mayoría de otros idiomas (por ejemplo, Pascal o
C); Los paréntesis ( ()) se pueden utilizar para agrupar. Por ejemplo:
>>>

>>> 2 + 2
4
>>> 50 - 5*6
20
>>> (50 - 5*6) / 4
5.0
>>> 8 / 5 # division always returns a floating point number
1.6

Los números enteros (por ejemplo 2, 4, 20) tiene tipo int, los que tienen una parte
fraccionaria (por ejemplo 5.0, 1.6) tienen tipo float. Veremos más sobre los tipos
numéricos más adelante en el tutorial.

Division ( /) siempre devuelve un flotante. Para hacer una división de piso y obtener
un resultado entero (descartando cualquier resultado fraccionario) puede usar
el // operador; para calcular el resto puede utilizar %:

>>>

>>> 17 / 3 # classic division returns a float


5.666666666666667
>>>
>>> 17 // 3 # floor division discards the fractional part
5
>>> 17 % 3 # the % operator returns the remainder of the
division
2
>>> 5 * 3 + 2 # result * divisor + remainder
17

Con Python, es posible usar el **operador para calcular las potencias 1 :

>>>

>>> 5 ** 2 # 5 squared
25
>>> 2 ** 7 # 2 to the power of 7
128

El signo igual ( =) se utiliza para asignar un valor a una variable. Posteriormente, no


se muestra ningún resultado antes del siguiente mensaje interactivo:
>>>

>>> width = 20
>>> height = 5 * 9
>>> width * height
900

Si una variable no está “definida” (se le asigna un valor), intentar usarla le dará un
error:

>>>

>>> n # try to access an undefined variable


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined

Hay soporte completo para punto flotante; Los operadores con operandos de tipo
mixto convierten el operando entero en punto flotante:

>>>

>>> 4 * 3.75 - 1
14.0

En modo interactivo, la última expresión impresa se asigna a la variable _. Esto


significa que cuando usa Python como calculadora de escritorio, es algo más fácil
continuar con los cálculos, por ejemplo:

>>>

>>> tax = 12.5 / 100


>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06
El usuario debe tratar esta variable como de solo lectura. No le asigne un valor
explícitamente: crearía una variable local independiente con el mismo nombre
enmascarando la variable incorporada con su comportamiento mágico.

Además de inty float, Python admite otros tipos de números,


como Decimaly Fraction. Python también tiene soporte integrado para números
complejos y usa el sufijo jo Jpara indicar la parte imaginaria (por ejemplo 3+5j).

3.1.2. Cadenas

Además de los números, Python también puede manipular cadenas, que se pueden
expresar de varias formas. Pueden encerrarse entre comillas simples ( '...') o
comillas dobles ( "...") con el mismo resultado 2 . \se puede utilizar para escapar
de las comillas:

>>>

>>> 'spam eggs' # single quotes


'spam eggs'
>>> 'doesn\'t' # use \' to escape the single quote...
"doesn't"
>>> "doesn't" # ...or use double quotes instead
"doesn't"
>>> '"Yes," they said.'
'"Yes," they said.'
>>> "\"Yes,\" they said."
'"Yes," they said.'
>>> '"Isn\'t," they said.'
'"Isn\'t," they said.'

En el intérprete interactivo, la cadena de salida se encierra entre comillas y los


caracteres especiales se escapan con barras invertidas. Si bien esto a veces puede
verse diferente de la entrada (las comillas adjuntas pueden cambiar), las dos
cadenas son equivalentes. La cadena se incluye entre comillas dobles si la cadena
contiene comillas simples y sin comillas dobles; de lo contrario, se incluye entre
comillas simples. La print()función produce una salida más legible al omitir las
comillas adjuntas e imprimir caracteres especiales y de escape:

>>>

>>> '"Isn\'t," they said.'


'"Isn\'t," they said.'
>>> print('"Isn\'t," they said.')
"Isn't," they said.
>>> s = 'First line.\nSecond line.' # \n means newline
>>> s # without print(), \n is included in the output
'First line.\nSecond line.'
>>> print(s) # with print(), \n produces a new line
First line.
Second line.

Si no desea que los caracteres precedidos por \se interpreten como caracteres
especiales, puede utilizar cadenas sin formato añadiendo un rantes de la primera
cita:

>>>

>>> print('C:\some\name') # here \n means newline!


C:\some
ame
>>> print(r'C:\some\name') # note the r before the quote
C:\some\name

Los literales de cadena pueden abarcar varias líneas. Una forma es usar comillas
triples: """..."""o '''...'''. El final de las líneas se incluye automáticamente
en la cadena, pero es posible evitarlo agregando un \al final de la línea. El siguiente
ejemplo:

print("""\
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
""")

produce la siguiente salida (tenga en cuenta que la nueva línea inicial no está
incluida):

Usage: thingy [OPTIONS]


-h Display this usage message
-H hostname Hostname to connect to

Las cadenas se pueden concatenar (pegar) con el +operador y repetir con *:


>>>

>>> # 3 times 'un', followed by 'ium'


>>> 3 * 'un' + 'ium'
'unununium'

Dos o más cadenas literales (es decir, las que están entre comillas) una al lado de
la otra se concatenan automáticamente.

>>>

>>> 'Py' 'thon'


'Python'

Esta función es particularmente útil cuando desea romper cadenas largas:

>>>

>>> text = ('Put several strings within parentheses '


... 'to have them joined together.')
>>> text
'Put several strings within parentheses to have them joined
together.'

Sin embargo, esto solo funciona con dos literales, no con variables o expresiones:

>>>

>>> prefix = 'Py'


>>> prefix 'thon' # can't concatenate a variable and a string
literal
File "<stdin>", line 1
prefix 'thon'
^
SyntaxError: invalid syntax
>>> ('un' * 3) 'ium'
File "<stdin>", line 1
('un' * 3) 'ium'
^
SyntaxError: invalid syntax

Si desea concatenar variables o una variable y un literal, use +:


>>>

>>> prefix + 'thon'


'Python'

Las cadenas se pueden indexar (subíndices), y el primer carácter tiene un índice 0.


No hay un tipo de carácter separado; un carácter es simplemente una cadena de
tamaño uno:

>>>

>>> word = 'Python'


>>> word[0] # character in position 0
'P'
>>> word[5] # character in position 5
'n'

Los índices también pueden ser números negativos, para empezar a contar desde
la derecha:

>>>

>>> word[-1] # last character


'n'
>>> word[-2] # second-last character
'o'
>>> word[-6]
'P'

Tenga en cuenta que dado que -0 es lo mismo que 0, los índices negativos
comienzan desde -1.

Además de indexación, corte en rodajas también es compatible. Mientras que la


indexación se usa para obtener caracteres individuales, la división le permite
obtener una subcadena:

>>>

>>> word[0:2] # characters from position 0 (included) to 2


(excluded)
'Py'
>>> word[2:5] # characters from position 2 (included) to 5
(excluded)
'tho'

Tenga en cuenta que el inicio siempre se incluye y el final siempre se excluye. Esto
asegura que siempre sea igual a :s[:i] + s[i:]s

>>>

>>> word[:2] + word[2:]


'Python'
>>> word[:4] + word[4:]
'Python'

Los índices de sector tienen valores predeterminados útiles; un primer índice omitido
toma como valor predeterminado cero, un segundo índice omitido toma como valor
predeterminado el tamaño de la cadena que se corta.

>>>

>>> word[:2] # character from the beginning to position 2


(excluded)
'Py'
>>> word[4:] # characters from position 4 (included) to the
end
'on'
>>> word[-2:] # characters from the second-last (included) to
the end
'on'

Una forma de recordar cómo funcionan los sectores es pensar en los índices como
apuntando entre caracteres, con el borde izquierdo del primer carácter numerado
como 0. Luego, el borde derecho del último carácter de una cadena de n caracteres
tiene el índice n , por ejemplo:

+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
La primera fila de números da la posición de los índices 0… 6 en la cadena; la
segunda fila da los índices negativos correspondientes. El corte de i a j consta de
todos los caracteres entre los bordes etiquetados como i y j , respectivamente.

Para índices no negativos, la longitud de un segmento es la diferencia de los índices,


si ambos están dentro de los límites. Por ejemplo, la longitud de word[1:3]es 2.

Si intenta utilizar un índice demasiado grande, se producirá un error:

>>>

>>> word[42] # the word only has 6 characters


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range

Sin embargo, los índices de corte fuera de rango se manejan con elegancia cuando
se usan para cortar:

>>>

>>> word[4:42]
'on'
>>> word[42:]
''

Las cadenas de Python no se pueden cambiar, son inmutables . Por lo tanto, la


asignación a una posición indexada en la cadena da como resultado un error:

>>>

>>> word[0] = 'J'


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> word[2:] = 'py'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

Si necesita una cadena diferente, debe crear una nueva:


>>>

>>> 'J' + word[1:]


'Jython'
>>> word[:2] + 'py'
'Pypy'

La función incorporada len()devuelve la longitud de una cadena:

>>>

>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34
Ver también
Tipo de secuencia de texto: str

Las cadenas son ejemplos de tipos de secuencia y admiten las operaciones


comunes admitidas por dichos tipos.
Métodos de cadena

Las cadenas admiten una gran cantidad de métodos para realizar


transformaciones y búsquedas básicas.
Literales de cadena formateados

Literales de cadena que tienen expresiones incrustadas.


Sintaxis de cadena de formato

Información sobre el formato de cadena con str.format().


Formato de cadena de estilo printf

Las antiguas operaciones de formato que se invocan cuando las cadenas


son el operando izquierdo del %operador se describen con más detalle aquí.
3.1.3. Listas

Python conoce varios tipos de datos compuestos , que se utilizan para agrupar otros
valores. La más versátil es la lista , que se puede escribir como una lista de valores
(elementos) separados por comas entre corchetes. Las listas pueden contener
elementos de diferentes tipos, pero normalmente todos los elementos tienen el
mismo tipo.
>>>

>>> squares = [1, 4, 9, 16, 25]


>>> squares
[1, 4, 9, 16, 25]

Al igual que las cadenas (y todos los demás tipos de secuencia integrados ), las
listas se pueden indexar y dividir:

>>>

>>> squares[0] # indexing returns the item


1
>>> squares[-1]
25
>>> squares[-3:] # slicing returns a new list
[9, 16, 25]

Todas las operaciones de corte devuelven una nueva lista que contiene los
elementos solicitados. Esto significa que el siguiente segmento devuelve una copia
superficial de la lista>>>

>>> squares[:]
[1, 4, 9, 16, 25]

Las listas también admiten operaciones como la concatenación:

>>>

>>> squares + [36, 49, 64, 81, 100]


[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

A diferencia de las cadenas, que son inmutables , las listas son de tipo mutable , es
decir, es posible cambiar su contenido:

>>>

>>> cubes = [1, 8, 27, 65, 125] # something's wrong here


>>> 4 ** 3 # the cube of 4 is 64, not 65!
64
>>> cubes[3] = 64 # replace the wrong value
>>> cubes
[1, 8, 27, 64, 125]

También puede agregar nuevos elementos al final de la lista, utilizando


el append() método (veremos más sobre los métodos más adelante):

>>>

>>> cubes.append(216) # add the cube of 6


>>> cubes.append(7 ** 3) # and the cube of 7
>>> cubes
[1, 8, 27, 64, 125, 216, 343]

La asignación a sectores también es posible, y esto incluso puede cambiar el


tamaño de la lista o borrarla por completo:

>>>

>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']


>>> letters
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> # replace some values
>>> letters[2:5] = ['C', 'D', 'E']
>>> letters
['a', 'b', 'C', 'D', 'E', 'f', 'g']
>>> # now remove them
>>> letters[2:5] = []
>>> letters
['a', 'b', 'f', 'g']
>>> # clear the list by replacing all the elements with an
emptylist
>>> letters[:] = []
>>> letters
[]La función incorporada len()también se aplica a las listas:
>>>

>>> letters = ['a', 'b', 'c', 'd']


>>> len(letters)
4

Es posible anidar listas (crear listas que contengan otras listas), por ejemplo:

>>>
>>> a = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> x = [a, n]
>>> x
[['a', 'b', 'c'], [1, 2, 3]]
>>> x[0]
['a', 'b', 'c']
>>> x[0][1]
'b'

3.2. Primeros pasos hacia la programación


Por supuesto, podemos usar Python para tareas más complicadas que sumar dos
y dos. Por ejemplo, podemos escribir una subsecuencia inicial de la serie de
Fibonacci de la siguiente manera:

>>>

>>> # Fibonacci series:


... # the sum of two elements defines the next
... a, b = 0, 1
>>> while a < 10:
... print(a)
... a, b = b, a+b
...
0
1
1
2
3
5
8

Este ejemplo presenta varias características nuevas.

La primera línea contiene una asignación múltiple : las


variables ay b simultáneamente obtienen los nuevos valores 0 y 1. En la última línea,
esto se usa nuevamente, demostrando que las expresiones en el lado derecho se
evalúan primero antes de que se lleve a cabo cualquiera de las asignaciones . Las
expresiones del lado derecho se evalúan de izquierda a derecha.
El whileciclo se ejecuta siempre que la condición (aquí:) permanezca
verdadera. En Python, como en C, cualquier valor entero distinto de cero es
verdadero; cero es falso. La condición también puede ser una cadena o un valor de
lista, de hecho, cualquier secuencia; cualquier cosa con una longitud distinta de cero
es verdadera, las secuencias vacías son falsas. La prueba utilizada en el ejemplo
es una comparación simple. Los operadores de comparación estándar se escriben
igual que en C: (menor que), (mayor que), (igual a), (menor o igual que), (mayor o
igual que) y (no igual a).a < 10<>==<=>=!=
El cuerpo del bucle tiene sangría : la sangría es la forma en que Python agrupa las
declaraciones. En el indicador interactivo, debe escribir una tabulación o espacios
para cada línea con sangría. En la práctica, preparará entradas más complicadas
para Python con un editor de texto; Todos los editores de texto decentes tienen una
función de sangría automática. Cuando una declaración compuesta se ingresa de
forma interactiva, debe ir seguida de una línea en blanco para indicar que se
completó (ya que el analizador no puede adivinar cuándo ha escrito la última
línea). Tenga en cuenta que cada línea dentro de un bloque básico debe tener la
misma sangría.
La print()función escribe el valor de los argumentos que se le dan. Se diferencia
de simplemente escribir la expresión que desea escribir (como hicimos
anteriormente en los ejemplos de la calculadora) en la forma en que maneja
múltiples argumentos, cantidades de punto flotante y cadenas. Las cadenas se
imprimen sin comillas y se inserta un espacio entre los elementos, por lo que puede
formatear las cosas bien, así:

>>>

>>> i = 256*256
>>> print('The value of i is', i)
The value of i is 65536

El argumento de palabra clave end se puede usar para evitar el salto de línea
después de la salida, o finalizar la salida con una cadena diferente:

>>>

Dado que **tiene mayor precedencia que -, -3**2se interpretará como -


(3**2)y, por lo tanto, resultará en -9. Para evitar esto y obtenerlo 9, puede
usar (-3)**2.

A diferencia de otros idiomas, los caracteres especiales como \ntienen el


mismo significado con comillas simples ( '...') y dobles ( "..."). La única
diferencia entre los dos es que entre comillas simples no es necesario
escapar "(pero debe escapar \') y viceversa.

4. Más herramientas de control de flujo


Además de la whiledeclaración que se acaba de presentar, Python usa las
declaraciones de control de flujo habituales conocidas en otros lenguajes, con
algunos cambios.

4.1. ifDeclaraciones
Quizás el tipo de declaración más conocido es la ifdeclaración. Por ejemplo:

>>>

>>> x = int(input("Please enter an integer: "))


Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More

Puede haber cero o más elifpartes, y la elseparte es opcional. La palabra clave


" elif" es la abreviatura de "else if" y es útil para evitar una sangría
excesiva. Una if... elif... elif... secuencia es un sustituto de
las declaraciones switcho caseque se encuentran en otros idiomas.

4.2. forDeclaraciones
La fordeclaración en Python difiere un poco de lo que puede estar acostumbrado
en C o Pascal. En lugar de iterar siempre sobre una progresión aritmética de
números (como en Pascal), o darle al usuario la capacidad de definir tanto el paso
de iteración como la condición de detención (como C), la fordeclaración de
Python itera sobre los elementos de cualquier secuencia (una lista o una cadena),
en el orden en que aparecen en la secuencia. Por ejemplo (sin juego de palabras):

>>>

>>> # Measure some strings:


... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12

El código que modifica una colección mientras se itera sobre esa misma colección
puede ser complicado de acertar. En cambio, suele ser más sencillo recorrer una
copia de la colección o crear una nueva colección:

# Strategy: Iterate over a copy


for user, status in users.copy().items():
if status == 'inactive':
del users[user]

# Strategy: Create a new collection


active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status

4.3. La range()función
Si necesita iterar sobre una secuencia de números, la función range()incorporada
es útil. Genera progresiones aritméticas:

>>>

>>> for i in range(5):


... print(i)
...
0
1
2
3
4

El punto final dado nunca es parte de la secuencia generada; range(10)genera 10


valores, los índices legales para elementos de una secuencia de longitud 10. Es
posible dejar que el rango comience en otro número, o especificar un incremento
diferente (incluso negativo; a veces esto se llama el 'paso'):

range(5, 10)
5, 6, 7, 8, 9

range(0, 10, 3)
0, 3, 6, 9

range(-10, -100, -30)


-10, -40, -70

Para iterar sobre los índices de una secuencia, puede combinar range()y de
la len()siguiente manera:

>>>

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']


>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb

En la mayoría de estos casos, sin embargo, es conveniente utilizar


la enumerate() función; consulte Técnicas de bucle .

Algo extraño sucede si solo imprime un rango:

>>>

>>> print(range(10))
range(0, 10)
En muchos sentidos, el objeto devuelto por se range()comporta como si fuera una
lista, pero en realidad no lo es. Es un objeto que devuelve los elementos sucesivos
de la secuencia deseada cuando itera sobre él, pero realmente no forma parte de la
lista, lo que ahorra espacio.

Decimos que tal objeto es iterable , es decir, adecuado como objetivo para funciones
y construcciones que esperan algo de lo que puedan obtener elementos sucesivos
hasta que se agote el suministro. Hemos visto que la fordeclaración es una
construcción de este tipo, mientras que un ejemplo de una función que toma un
iterable es sum():

>>>

>>> sum(range(4)) # 0 + 1 + 2 + 3
6

Más adelante veremos más funciones que devuelven iterables y toman iterables
como argumentos. Por último, tal vez sienta curiosidad por saber cómo obtener una
lista de un rango. Esta es la solucion:

>>>

>>> list(range(4))
[0, 1, 2, 3]

En el capítulo Estructuras de datos , discutiremos con más detalle acerca


de list().

4.4. breaky continuedeclaraciones


y elsecláusulas sobre bucles
La breakdeclaración, como en C, sale del bucle foro encierro más interno while.

Las declaraciones de bucle pueden tener una elsecláusula; se ejecuta cuando el


ciclo termina por agotamiento del iterable (con for) o cuando la condición se vuelve
falsa (con while), pero no cuando el ciclo termina con una breakdeclaración. Esto
se ejemplifica con el siguiente ciclo, que busca números primos:

>>>
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(Sí, este es el código correcto. Mire de cerca: la elsecláusula pertenece


al forciclo, no a la ifdeclaración).

Cuando se usa con un bucle, la elsecláusula tiene más en común con


la elsecláusula de una trydeclaración que con la de
las ifdeclaraciones: tryla elsecláusula de una declaración se ejecuta cuando no
ocurre ninguna excepción y la elsecláusula de un bucle se ejecuta cuando
no break ocurre. Para obtener más información sobre la trydeclaración y las
excepciones, consulte Manejo de excepciones .

La continuedeclaración, también tomada de C, continúa con la siguiente iteración


del ciclo:

>>>

>>> for num in range(2, 10):


... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found an odd number", num)
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9

4.5. passDeclaraciones
La passdeclaración no hace nada. Se puede usar cuando se requiere una
declaración sintácticamente pero el programa no requiere ninguna acción. Por
ejemplo:

>>>

>>> while True:


... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...

Esto se usa comúnmente para crear clases mínimas:

>>>

>>> class MyEmptyClass:


... pass
...

Otro lugar que passse puede utilizar es como marcador de posición para una
función o cuerpo condicional cuando está trabajando en un nuevo código, lo que le
permite seguir pensando en un nivel más abstracto. El passse ignora en silencio:

>>>

>>> def initlog(*args):


... pass # Remember to implement this!
...

4.6. Definición de funciones


Podemos crear una función que escriba la serie de Fibonacci en un límite arbitrario:

>>>

>>> def fib(n): # write Fibonacci series up to n


... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

La palabra clave defintroduce una definición de función . Debe ir seguido del


nombre de la función y la lista entre paréntesis de parámetros formales. Las
declaraciones que forman el cuerpo de la función comienzan en la siguiente línea y
deben tener sangría.

La primera declaración del cuerpo de la función puede ser opcionalmente un literal


de cadena; este literal de cadena es la cadena de documentación de la función,
o docstring . (Puede encontrar más información sobre cadenas de documentos en
la sección Cadenas de documentación ). Existen herramientas que utilizan cadenas
de documentos para producir automáticamente documentación en línea o impresa,
o para permitir al usuario navegar de forma interactiva a través del código; Es una
buena práctica incluir cadenas de documentos en el código que escribe, así que
conviértalo en un hábito.

La ejecución de una función introduce una nueva tabla de símbolos utilizada para
las variables locales de la función. Más precisamente, todas las asignaciones de
variables en una función almacenan el valor en la tabla de símbolos local; mientras
que las referencias de variables primero se buscan en la tabla de símbolos local,
luego en las tablas de símbolos locales de las funciones adjuntas, luego en la tabla
de símbolos global y finalmente en la tabla de nombres incorporados. Por lo tanto,
a las variables globales y a las variables de funciones adjuntas no se les puede
asignar directamente un valor dentro de una función (a menos que, para las
variables globales, se globalmencionen en una declaración o, para las variables
de funciones adjuntas, que se nonlocalmencionen en una declaración), aunque
pueden ser referenciadas.

Los parámetros (argumentos) reales para una llamada de función se introducen en


la tabla de símbolos local de la función llamada cuando se llama; por lo tanto, los
argumentos se pasan usando la llamada por valor (donde el valor es siempre
una referencia de objeto , no el valor del objeto). 1 Cuando una función llama a otra
función, se crea una nueva tabla de símbolos locales para esa llamada.

Una definición de función asocia el nombre de la función con el objeto de función en


la tabla de símbolos actual. El intérprete reconoce el objeto señalado por ese
nombre como una función definida por el usuario. Otros nombres también pueden
apuntar a ese mismo objeto de función y también se pueden usar para acceder a la
función:

>>>

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89

Viniendo de otros lenguajes, puede objetar que fibno es una función sino un
procedimiento, ya que no devuelve un valor. De hecho, incluso las funciones sin
una returndeclaración devuelven un valor, aunque bastante aburrido. Este valor
se llama None(es un nombre integrado). NoneNormalmente, el intérprete suprime la
escritura del valor si fuera el único valor escrito. Puedes verlo si realmente quieres
usar print():

>>>

>>> fib(0)
>>> print(fib(0))
None

Es simple escribir una función que devuelva una lista de los números de la serie de
Fibonacci, en lugar de imprimirla:

>>>

>>> def fib2(n): # return Fibonacci series up to n


... """Return a list containing the Fibonacci series up to
n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Este ejemplo, como de costumbre, demuestra algunas características nuevas de


Python:

 La returndeclaración regresa con un valor de una función. returnsin un


argumento de expresión devuelve None. También regresa caer al final de una
función None.
 La declaración result.append(a)llama a un método del objeto de
lista result. Un método es una función que 'pertenece' a un objeto y se
nombra obj.methodname, donde objestá algún objeto (esto puede ser una
expresión), y methodnamees el nombre de un método que está definido por
el tipo de objeto. Los diferentes tipos definen diferentes métodos. Los
métodos de diferentes tipos pueden tener el mismo nombre sin causar
ambigüedad. (Es posible definir sus propios tipos de objetos y métodos,
usando clases , vea Clases ) El método append()mostrado en el ejemplo
está definido para objetos de lista; agrega un nuevo elemento al final de la
lista. En este ejemplo es equivalente a , pero más
eficiente.result = result + [a]
4.7. Más sobre la definición de funciones
También es posible definir funciones con un número variable de argumentos. Hay
tres formas que se pueden combinar.

4.7.1. Valores de argumento predeterminados

La forma más útil es especificar un valor predeterminado para uno o más


argumentos. Esto crea una función que se puede llamar con menos argumentos de
los que está definida para permitir. Por ejemplo:

def ask_ok(prompt, retries=4, reminder='Please try again!'):


while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)

Esta función se puede llamar de varias formas:

 dando solo el argumento


obligatorio: ask_ok('Do you really want to quit?')
 dando uno de los argumentos
opcionales: ask_ok('OK to overwrite the file?', 2)
 o incluso dando todos los
argumentos: ask_ok('OK to overwrite the file?', 2, 'Come on,
only yes or no!')

Este ejemplo también presenta la inpalabra clave. Esto prueba si una secuencia
contiene un cierto valor.

Los valores predeterminados se evalúan en el punto de definición de la función en


el ámbito de definición , de modo que

i = 5

def f(arg=i):
print(arg)

i = 6
f()

imprimirá 5.

Advertencia importante: el valor predeterminado se evalúa solo una vez. Esto


marca la diferencia cuando el valor predeterminado es un objeto mutable como una
lista, diccionario o instancias de la mayoría de las clases. Por ejemplo, la siguiente
función acumula los argumentos que se le pasan en llamadas posteriores:

def f(a, L=[]):


L.append(a)
return L

print(f(1))
print(f(2))
print(f(3))

Esto imprimirá

[1]
[1, 2]
[1, 2, 3]

Si no desea que el valor predeterminado se comparta entre llamadas posteriores,


puede escribir la función de esta manera:

def f(a, L=None):


if L is None:
L = []
L.append(a)
return L

4.7.2. Argumentos de palabras clave

Las funciones también se pueden llamar utilizando argumentos de palabras


clave del formulario kwarg=value. Por ejemplo, la siguiente función:

def parrot(voltage, state='a stiff', action='voom',


type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")

acepta un argumento necesario ( voltage) y tres argumentos opcionales


( state, actiony type). Esta función se puede llamar de cualquiera de las
siguientes formas:

parrot(1000) # 1
positional argument
parrot(voltage=1000) # 1
keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2
keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2
keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3
positional arguments
parrot('a thousand', state='pushing up the daisies') # 1
positional, 1 keyword

pero todas las siguientes llamadas serían inválidas:

parrot() # required argument missing


parrot(voltage=5.0, 'dead') # non-keyword argument after a
keyword argument
parrot(110, voltage=220) # duplicate value for the same
argument
parrot(actor='John Cleese') # unknown keyword argument

En una llamada a función, los argumentos de palabras clave deben seguir


argumentos posicionales. Todos los argumentos de palabra clave pasados deben
coincidir con uno de los argumentos aceptados por la función (por ejemplo, actorno
es un argumento válido para la parrotfunción), y su orden no es importante. Esto
también incluye argumentos no opcionales (por ejemplo,
también parrot(voltage=1000)es válido). Ningún argumento puede recibir un
valor más de una vez. Aquí hay un ejemplo que falla debido a esta restricción:

>>>

>>> def function(a):


... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for keyword argument
'a'

Cuando un parámetro formal final del formulario **nameestá presente, recibe un


diccionario (ver Tipos de mapeo - dict ) que contiene todos los argumentos de
palabras clave excepto los correspondientes a un parámetro formal. Esto puede
combinarse con un parámetro formal del formulario *name(descrito en la siguiente
subsección) que recibe una tupla que contiene los argumentos posicionales más
allá de la lista de parámetros formales. ( *namedebe ocurrir antes **name). Por
ejemplo, si definimos una función como esta:

def cheeseshop(kind, *arguments, **keywords):


print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])

Podría llamarse así:

cheeseshop("Limburger", "It's very runny, sir.",


"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")

y por supuesto imprimiría:

-- Do you have any Limburger ?


-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch

Tenga en cuenta que se garantiza que el orden en el que se imprimen los


argumentos de las palabras clave coincidirá con el orden en el que se
proporcionaron en la llamada a la función.

4.7.3. Parámetros especiales

De forma predeterminada, los argumentos se pueden pasar a una función de Python


por posición o explícitamente por palabra clave. Para mejorar la legibilidad y el
rendimiento, tiene sentido restringir la forma en que se pueden pasar los
argumentos de modo que un desarrollador solo necesite mirar la definición de la
función para determinar si los elementos se pasan por posición, por posición o
palabra clave, o por palabra clave.

La definición de una función puede verse así:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):


----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only

donde /y *son opcionales. Si se utilizan, estos símbolos indican el tipo de parámetro


según la forma en que los argumentos se pueden pasar a la función: solo posicional,
posicional o palabra clave y solo palabra clave. Los parámetros de palabras clave
también se denominan parámetros con nombre.

4.7.3.1. Argumentos posicionales o de palabras clave

Si /y *no están presentes en la definición de la función, los argumentos se pueden


pasar a una función por posición o por palabra clave.

4.7.3.2. Parámetros solo posicionales

Mirando esto con un poco más de detalle, es posible marcar ciertos parámetros
como solo posicionales . Si es solo posicional , el orden de los parámetros es
importante y los parámetros no se pueden pasar por palabra clave. Los parámetros
de solo posición se colocan antes de una /(barra inclinada). Se /utiliza para separar
lógicamente los parámetros de solo posición del resto de parámetros. Si no hay /en
la definición de la función, no hay parámetros solo posicionales.

Los parámetros que siguen a /pueden ser posicionales o de palabra clave o solo
de palabras clave .

4.7.3.3. Argumentos de solo palabras clave

Para marcar los parámetros como solo palabras clave , indicando que los
parámetros deben pasarse mediante un argumento de palabra clave, coloque
un *en la lista de argumentos justo antes del primer parámetro solo de palabras
clave .
4.7.3.4. Ejemplos de funciones

Considere las siguientes definiciones de funciones de ejemplo prestando mucha


atención a los marcadores /y *:

>>>

>>> def standard_arg(arg):


... print(arg)
...
>>> def pos_only_arg(arg, /):
... print(arg)
...
>>> def kwd_only_arg(*, arg):
... print(arg)
...
>>> def combined_example(pos_only, /, standard, *, kwd_only):
... print(pos_only, standard, kwd_only)

La primera definición de función standard_arg, la forma más familiar, no impone


restricciones a la convención de llamada y los argumentos pueden pasarse por
posición o palabra clave:

>>>

>>> standard_arg(2)
2

>>> standard_arg(arg=2)
2

La segunda función pos_only_argestá restringida a usar solo parámetros


posicionales, ya que hay un /en la definición de la función:

>>>

>>> pos_only_arg(1)
1

>>> pos_only_arg(arg=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pos_only_arg() got an unexpected keyword argument
'arg'

La tercera función kwd_only_argssolo permite argumentos de palabras clave


como lo indica a *en la definición de la función:

>>>

>>> kwd_only_arg(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was
given

>>> kwd_only_arg(arg=3)
3

Y el último usa las tres convenciones de llamada en la misma definición de función:

>>>

>>> combined_example(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() takes 2 positional arguments but 3
were given

>>> combined_example(1, 2, kwd_only=3)


1 2 3

>>> combined_example(1, standard=2, kwd_only=3)


1 2 3

>>> combined_example(pos_only=1, standard=2, kwd_only=3)


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: combined_example() got an unexpected keyword argument
'pos_only'

Finalmente, considere esta definición de función que tiene una posible colisión entre
el argumento posicional name y **kwdsque tiene namecomo clave:

def foo(name, **kwds):


return 'name' in kwds

No hay una llamada posible que lo haga regresar Trueya que la palabra
clave 'name' siempre se vinculará al primer parámetro. Por ejemplo:

>>>

>>> foo(1, **{'name': 2})


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'name'
>>>

Pero usando /(solo argumentos posicionales), es posible ya que permite namecomo


argumento posicional y 'name'como clave en los argumentos de palabras clave:

def foo(name, /, **kwds):


return 'name' in kwds
>>> foo(1, **{'name': 2})
True

En otras palabras, los nombres de los parámetros solo posicionales se pueden


utilizar **kwdssin ambigüedad.

4.7.3.5. Resumen

El caso de uso determinará qué parámetros usar en la definición de la función:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Como orientación:

 Utilice solo posicional si desea que el nombre de los parámetros no esté


disponible para el usuario. Esto es útil cuando los nombres de los parámetros
no tienen un significado real, si desea hacer cumplir el orden de los
argumentos cuando se llama a la función o si necesita tomar algunos
parámetros posicionales y palabras clave arbitrarias.
 Use solo palabras clave cuando los nombres tengan significado y la definición
de la función sea más comprensible al ser explícito con los nombres o si
desea evitar que los usuarios confíen en la posición del argumento que se
pasa.
 En el caso de una API, use solo posicional para evitar cambios rotos en la
API si el nombre del parámetro se modifica en el futuro.
4.7.4. Listas de argumentos arbitrarios

Finalmente, la opción que se usa con menos frecuencia es especificar que se puede
llamar a una función con un número arbitrario de argumentos. Estos argumentos se
incluirán en una tupla (consulte Tuplas y secuencias ). Antes de la variable número
de argumentos, pueden aparecer cero o más argumentos normales.

def write_multiple_items(file, separator, *args):


file.write(separator.join(args))

Normalmente, estos variadicargumentos serán los últimos en la lista de


parámetros formales, porque recogen todos los argumentos de entrada restantes
que se pasan a la función. Todos los parámetros formales que aparecen después
del *args parámetro son argumentos 'solo de palabras clave', lo que significa que
solo pueden usarse como palabras clave en lugar de argumentos posicionales.

>>>

>>> def concat(*args, sep="/"):


... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

4.7.5. Desempaquetando listas de argumentos

La situación inversa ocurre cuando los argumentos ya están en una lista o tupla,
pero necesitan ser descomprimidos para una llamada de función que requiere
argumentos posicionales separados. Por ejemplo, la range()función incorporada
espera argumentos de inicio y detención separados . Si no están disponibles por
separado, escriba la llamada a la función con el *-operador para descomprimir los
argumentos de una lista o tupla:

>>>
>>> list(range(3, 6)) # normal call with separate
arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked
from a list
[3, 4, 5]

De la misma manera, los diccionarios pueden entregar argumentos de palabras


clave con el **operador:

>>>

>>> def parrot(voltage, state='a stiff', action='voom'):


... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end='
')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised",
"action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts
through it. E's bleedin' demised !

4.7.6. Expresiones Lambda

Se pueden crear pequeñas funciones anónimas con la lambdapalabra clave. Esta


función devuelve la suma de sus dos argumentos: . Las funciones Lambda se
pueden utilizar siempre que se requieran objetos de función. Están restringidos
sintácticamente a una sola expresión. Semánticamente, son simplemente azúcar
sintáctico para una definición de función normal. Al igual que las definiciones de
funciones anidadas, las funciones lambda pueden hacer referencia a variables del
ámbito contenedor:lambda a, b: a+b

>>>

>>> def make_incrementor(n):


... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

El ejemplo anterior usa una expresión lambda para devolver una función. Otro uso
es pasar una pequeña función como argumento:

>>>

>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

4.7.7. Cadenas de documentación

A continuación, se muestran algunas convenciones sobre el contenido y el formato


de las cadenas de documentación.

La primera línea debe ser siempre un resumen breve y conciso del propósito del
objeto. En aras de la brevedad, no debe indicar explícitamente el nombre o el tipo
del objeto, ya que están disponibles por otros medios (excepto si el nombre es un
verbo que describe la operación de una función). Esta línea debe comenzar con una
letra mayúscula y terminar con un punto.

Si hay más líneas en la cadena de documentación, la segunda línea debe estar en


blanco, separando visualmente el resumen del resto de la descripción. Las
siguientes líneas deben ser uno o más párrafos que describan las convenciones de
llamada del objeto, sus efectos secundarios, etc.

El analizador de Python no elimina la sangría de los literales de cadenas de varias


líneas en Python, por lo que las herramientas que procesan la documentación deben
eliminar la sangría si se desea. Esto se hace usando la siguiente convención. La
primera línea que no está en blanco después de la primera línea de la cadena
determina la cantidad de sangría para toda la cadena de documentación. (No
podemos usar la primera línea ya que generalmente es adyacente a las comillas de
apertura de la cadena, por lo que su sangría no es aparente en el literal de la
cadena). El espacio en blanco "equivalente" a esta sangría se elimina del principio
de todas las líneas de la cadena. . Las líneas que tienen menos sangría no deberían
aparecer, pero si aparecen, se deberían eliminar todos los espacios en blanco
iniciales. La equivalencia de espacios en blanco debe probarse después de la
expansión de las pestañas (a 8 espacios, normalmente).

A continuación, se muestra un ejemplo de una cadena de documentos de varias


líneas:

>>>

>>> def my_function():


... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.

No, really, it doesn't do anything.

4.7.8. Anotaciones de funciones

Las anotaciones de funciones son información de metadatos completamente


opcional sobre los tipos utilizados por las funciones definidas por el usuario
(consultePEP 3107 y PEP 484 para más información).

Las anotaciones se almacenan en el__annotations__ atributo de la función como


un diccionario y no tienen ningún efecto en ninguna otra parte de la función. Las
anotaciones de parámetros se definen con dos puntos después del nombre del
parámetro, seguidas de una expresión que evalúa el valor de la anotación. Las
anotaciones de retorno se definen mediante un literal->, seguido de una expresión,
entre la lista de parámetros y los dos puntos que indican el final de
ladefdeclaración. El siguiente ejemplo tiene un argumento posicional, un
argumento de palabra clave y el valor de retorno anotado:

>>>

>>> def f(ham: str, eggs: str = 'eggs') -> str:


... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>,
'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'

4.8. Intermezzo: Estilo de codificación


Ahora que está a punto de escribir piezas más largas y complejas de Python, es un
buen momento para hablar sobre el estilo de codificación . La mayoría de los
lenguajes se pueden escribir (o más conciso, formatear ) en diferentes
estilos; algunos son más legibles que otros. Hacer que sea fácil para otros leer su
código siempre es una buena idea, y adoptar un estilo de codificación agradable
ayuda enormemente a eso.

Para Python, PEP 8 ha surgido como la guía de estilo a la que se adhieren la


mayoría de los proyectos; promueve un estilo de codificación muy legible y
agradable a la vista. Todo desarrollador de Python debería leerlo en algún
momento; aquí están los puntos más importantes extraídos para usted:

 Utilice sangría de 4 espacios y no tabulaciones.

4 espacios son un buen compromiso entre una sangría pequeña (permite una
mayor profundidad de anidación) y una sangría grande (más fácil de
leer). Las pestañas introducen confusión y es mejor omitirlas.
 Envuelva las líneas para que no superen los 79 caracteres.

Esto ayuda a los usuarios con pantallas pequeñas y hace posible tener varios
archivos de código uno al lado del otro en pantallas más grandes.
 Utilice líneas en blanco para separar funciones y clases, y bloques de código
más grandes dentro de las funciones.
 Cuando sea posible, ponga los comentarios en una línea propia.
 Utilice cadenas de documentación.
 Use espacios alrededor de operadores y después de las comas, pero no
directamente en el interior de horquillado
construcciones: .a = f(1, 2) + g(3, 4)
 Nombra tus clases y funciones de manera consistente; la convención es
usar UpperCamelCasepara clases y lowercase_with_underscorespara
funciones y métodos. Úselo siempre selfcomo nombre para el primer
argumento del método (consulte Un primer vistazo a las clases para obtener
más información sobre clases y métodos).
 No utilice codificaciones sofisticadas si su código está destinado a ser
utilizado en entornos internacionales. El valor predeterminado de Python,
UTF-8 o incluso el ASCII simple funcionan mejor en cualquier caso.
 Del mismo modo, no utilice caracteres que no sean ASCII en los
identificadores si existe la mínima posibilidad de que personas que hablen un
idioma diferente lean o mantengan el código.

Notas al pie

En realidad, llamar por referencia de objeto sería una mejor descripción, ya


que si se pasa un objeto mutable, la persona que llama verá cualquier cambio
que le haga el destinatario (elementos insertados en una lista).

5. Estructuras de datos
Este capítulo describe algunas cosas que ya ha aprendido con más detalle y
también agrega algunas cosas nuevas.

5.1. Más sobre listas


El tipo de datos de lista tiene algunos métodos más. Aquí están todos los métodos
de los objetos de lista:

list.append( x )

Agrega un elemento al final de la lista. Equivalente a .a[len(a):] = [x]

list.extend( iterable )

Amplíe la lista añadiendo todos los elementos del iterable. Equivalente


a .a[len(a):] = iterable

list.insert( yo , x )

Inserte un elemento en una posición determinada. El primer argumento es el


índice del elemento antes del cual insertar, por lo que se inserta
al principio de la lista y es equivalente
a .a.insert(0, x)a.insert(len(a), x)a.append(x)

list.remove( x )

Elimine el primer elemento de la lista cuyo valor sea igual ax . Genera


un ValueErrorsi no existe tal elemento.

list.pop( [ i] )

Quite el artículo en la posición dada en la lista y devuélvalo. Si no se


especifica ningún índice, a.pop()elimina y devuelve el último elemento de
la lista. (Los corchetes alrededor de la i en la firma del método indican que el
parámetro es opcional, no que debe escribir corchetes en esa posición. Verá
esta notación con frecuencia en la Referencia de la biblioteca de Python).

list.clear( )

Elimina todos los elementos de la lista. Equivalente a .del a[:]

list.index( x [ , inicio [ , final ] ])

Devuelve un índice de base cero en la lista del primer elemento cuyo valor
es igual ax . Aumenta un ValueErrorsi no existe tal elemento.

Los argumentos opcionales start y end se interpretan como en la notación de


sector y se utilizan para limitar la búsqueda a una subsecuencia particular de
la lista. El índice devuelto se calcula en relación con el comienzo de la
secuencia completa en lugar del argumento de inicio .

list.count( x )

Devuelve el número de veces que x aparece en la lista.

list.sort( clave = Ninguno , reverso = Falso )

Ordene los elementos de la lista en su lugar (los argumentos se pueden


utilizar para personalizar la clasificación, consulte sorted()su explicación).

list.reverse( )
Invierta los elementos de la lista en su lugar.

list.copy( )

Devuelve una copia superficial de la lista. Equivalente a a[:].

Un ejemplo que usa la mayoría de los métodos de lista:

>>>

>>> fruits.index('banana', 4) # Find next banana starting a


position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
1

Otros lenguajes pueden devolver el objeto mutado, lo que permite el


encadenamiento de métodos, como d->insert("a")->remove("b")-
>sort();.

6. Módulos
Si sale del intérprete de Python y lo vuelve a ingresar, las definiciones que ha
realizado (funciones y variables) se perderán. Por lo tanto, si desea escribir un
programa algo más largo, es mejor usar un editor de texto para preparar la entrada
para el intérprete y ejecutarlo con ese archivo como entrada. Esto se conoce como
crear un guión . A medida que su programa se alarga, es posible que desee dividirlo
en varios archivos para facilitar el mantenimiento. Es posible que también desee
utilizar una función útil que haya escrito en varios programas sin copiar su definición
en cada programa.

Para respaldar esto, Python tiene una forma de poner definiciones en un archivo y
usarlas en un script o en una instancia interactiva del intérprete. Dicho archivo se
denomina módulo ; las definiciones de un módulo se pueden importar a otros
módulos o al módulo principal (la colección de variables a las que tiene acceso en
un script ejecutado en el nivel superior y en modo calculadora).
Un módulo es un archivo que contiene definiciones y declaraciones de Python. El
nombre del archivo es el nombre del módulo con el sufijo .pyagregado. Dentro de
un módulo, el nombre del módulo (como una cadena) está disponible como el valor
de la variable global __name__. Por ejemplo, use su editor de texto favorito para
crear un archivo llamado fibo.pyen el directorio actual con el siguiente contenido:

# Fibonacci numbers module

def fib(n): # write Fibonacci series up to n


a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()

def fib2(n): # return Fibonacci series up to n


result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result

Ahora ingrese al intérprete de Python e importe este módulo con el siguiente


comando:

>>>

>>> import fibo

Esto no introduce los nombres de las funciones definidas fibo directamente en la


tabla de símbolos actual; solo ingresa el nombre del módulo fiboallí. Usando el
nombre del módulo puede acceder a las funciones:

>>>

>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'
Si tiene la intención de utilizar una función con frecuencia, puede asignarla a un
nombre local:

>>>

>>> fib = fibo.fib


>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1. Más sobre módulos


Un módulo puede contener declaraciones ejecutables y definiciones de
funciones. Estas declaraciones están destinadas a inicializar el módulo. Se ejecutan
solo la primera vez que se encuentra el nombre del módulo en una declaración de
importación. 1 (También se ejecutan si el archivo se ejecuta como un script).

Cada módulo tiene su propia tabla de símbolos privada, que todas las funciones
definidas en el módulo utilizan como tabla de símbolos global. Por lo tanto, el autor
de un módulo puede usar variables globales en el módulo sin preocuparse por
choques accidentales con las variables globales de un usuario. Por otro lado, si
sabe lo que está haciendo, puede tocar las variables globales de un módulo con la
misma notación utilizada para referirse a sus funciones modname.itemname,.

Los módulos pueden importar otros módulos. Es habitual, pero no obligatorio,


colocar todas las importdeclaraciones al principio de un módulo (o script, para el
caso). Los nombres de los módulos importados se colocan en la tabla de símbolos
global del módulo de importación.

Existe una variante de la importdeclaración que importa nombres de un módulo


directamente a la tabla de símbolos del módulo de importación. Por ejemplo:

>>>

>>> from fibo import fib, fib2


>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Esto no introduce el nombre del módulo del que se toman las importaciones en la
tabla de símbolos local (por lo que en el ejemplo, fibono está definido).

Incluso existe una variante para importar todos los nombres que define un módulo:
>>>

>>> from fibo import *


>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Esto importa todos los nombres excepto los que comienzan con un guión bajo
( _). En la mayoría de los casos, los programadores de Python no utilizan esta
función, ya que introduce un conjunto desconocido de nombres en el intérprete,
posiblemente ocultando algunas cosas que ya ha definido.

Tenga en cuenta que, en general, la práctica de importar *desde un módulo o


paquete está mal vista, ya que a menudo provoca un código de mala lectura. Sin
embargo, está bien usarlo para evitar escribir en sesiones interactivas.

Si el nombre del módulo va seguido de as, el siguiente nombre asestá vinculado


directamente al módulo importado.

>>>

>>> import fibo as fib


>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Esto es efectivamente importar el módulo de la misma manera que lo hará, con la


única diferencia de que está disponible como .import fibofib

También se puede utilizar cuando se utiliza fromcon efectos similares:

>>>

>>> from fibo import fib as fibonacci


>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Nota

Por razones de eficiencia, cada módulo solo se importa una vez por sesión de
intérprete. Por lo tanto, si cambia sus módulos, debe reiniciar el intérprete o, si es
solo un módulo que desea probar de forma interactiva, use importlib.reload(),
por ejemplo .import importlib; importlib.reload(modulename)
6.1.1. Ejecutando módulos como scripts

Cuando ejecuta un módulo de Python con

python fibo.py <arguments>

el código del módulo se ejecutará, como si lo hubiera importado, pero con


el __name__ajuste en "__main__". Eso significa que al agregar este código al final
de su módulo:

if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))

puede hacer que el archivo sea utilizable como un script y también como un módulo
importable, porque el código que analiza la línea de comando solo se ejecuta si el
módulo se ejecuta como el archivo "principal":

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

Si se importa el módulo, el código no se ejecuta:

>>>

>>> import fibo


>>>

Esto se usa a menudo para proporcionar una interfaz de usuario conveniente a un


módulo o con fines de prueba (ejecutar el módulo como un script ejecuta un conjunto
de pruebas).

6.1.2. La ruta de búsqueda del módulo

Cuando spamse importa un módulo con nombre , el intérprete busca primero un


módulo integrado con ese nombre. Si no lo encuentra, busca un archivo
nombrado spam.pyen una lista de directorios proporcionada por la
variable sys.path. sys.pathse inicializa desde estas ubicaciones:
 El directorio que contiene el script de entrada (o el directorio actual cuando
no se especifica ningún archivo).
 PYTHONPATH (una lista de nombres de directorio, con la misma sintaxis que
la variable de shell PATH).
 El valor predeterminado que depende de la instalación.
Nota

En los sistemas de archivos que admiten enlaces simbólicos, el directorio que


contiene el script de entrada se calcula después de seguir el enlace simbólico. En
otras palabras, el directorio que contiene el enlace simbólico no se agrega a la ruta
de búsqueda del módulo.

Después de la inicialización, los programas de Python pueden


modificar sys.path. El directorio que contiene la secuencia de comandos que se
está ejecutando se coloca al principio de la ruta de búsqueda, antes de la ruta de la
biblioteca estándar. Esto significa que los scripts de ese directorio se cargarán en
lugar de los módulos del mismo nombre en el directorio de la biblioteca. Esto es un
error a menos que se pretenda reemplazarlo. Consulte la sección Módulos
estándar para obtener más información.

6.1.3. Archivos de Python “compilados”

Para acelerar la carga de módulos, Python almacena en caché la versión compilada


de cada módulo en el __pycache__directorio bajo el nombre , donde la versión
codifica el formato del archivo compilado; generalmente contiene el número de
versión de Python. Por ejemplo, en la versión 3.3 de CPython, la versión compilada
de spam.py se almacenaría en caché como . Esta convención de nomenclatura
permite que coexistan módulos compilados de diferentes lanzamientos y diferentes
versiones de Python.module.version.pyc__pycache__/spam.cpython-
33.pyc

Python compara la fecha de modificación de la fuente con la versión compilada para


ver si está desactualizada y necesita ser recompilada. Este es un proceso
completamente automático. Además, los módulos compilados son independientes
de la plataforma, por lo que la misma biblioteca se puede compartir entre sistemas
con diferentes arquitecturas.
Python no verifica el caché en dos circunstancias. Primero, siempre vuelve a
compilar y no almacena el resultado del módulo que se carga directamente desde
la línea de comando. En segundo lugar, no comprueba la caché si no hay un módulo
de origen. Para admitir una distribución que no sea de origen (solo compilado), el
módulo compilado debe estar en el directorio de origen y no debe haber un módulo
de origen.

Algunos consejos para expertos:

 Puede usar los interruptores -Oo -OOen el comando de Python para reducir
el tamaño de un módulo compilado. El -Oconmutador elimina las
declaraciones de aserción, el -OOconmutador elimina tanto las declaraciones
de aserción como las cadenas __doc__. Dado que algunos programas
pueden depender de tenerlos disponibles, solo debe usar esta opción si sabe
lo que está haciendo. Los módulos "optimizados" tienen una opt-etiqueta y
suelen ser más pequeños. Las versiones futuras pueden cambiar los efectos
de la optimización.
 Un programa no se ejecuta más rápido cuando se lee de un .pyc archivo
que cuando se lee de un .pyarchivo; lo único que es más rápido en
los .pycarchivos es la velocidad con la que se cargan.
 El módulo compileallpuede crear archivos .pyc para todos los módulos de
un directorio.
 Hay más detalles sobre este proceso, incluido un diagrama de flujo de las
decisiones, en PEP 3147 .
6.2. Módulos estándar
Python viene con una biblioteca de módulos estándar, descritos en un documento
separado, la Referencia de la biblioteca de Python ("Referencia de la biblioteca" de
aquí en adelante). Algunos módulos están integrados en el intérprete; estos
proporcionan acceso a operaciones que no forman parte del núcleo del lenguaje
pero que, sin embargo, están integradas, ya sea por eficiencia o para proporcionar
acceso a primitivas del sistema operativo, como las llamadas al sistema. El conjunto
de dichos módulos es una opción de configuración que también depende de la
plataforma subyacente. Por ejemplo, el winregmódulo solo se proporciona en
sistemas Windows. Un módulo en particular merece un poco de atención: sysque
está integrado en cada intérprete de Python. Las
variables sys.ps1y sys.ps2definen las cadenas utilizadas como avisos primarios
y secundarios:
>>>

>>> import sys


>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>

Estas dos variables solo se definen si el intérprete está en modo interactivo.

La variable sys.pathes una lista de cadenas que determina la ruta de búsqueda


de módulos del intérprete. Se inicializa a una ruta predeterminada tomada de la
variable de entorno.PYTHONPATH, o de un valor predeterminado incorporado
si PYTHONPATHno está configurado. Puede modificarlo utilizando operaciones de
lista estándar:

>>>

>>> import sys


>>> sys.path.append('/ufs/guido/lib/python')

6.3. La dir()función
La función incorporada dir()se utiliza para averiguar qué nombres define un
módulo. Devuelve una lista ordenada de cadenas:

>>>

>>> import fibo, sys


>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__loader__',
'__name__',
'__package__', '__stderr__', '__stdin__', '__stdout__',
'_clear_type_cache', '_current_frames', '_debugmallocstats',
'_getframe',
'_home', '_mercurial', '_xoptions', 'abiflags', 'api_version',
'argv',
'base_exec_prefix', 'base_prefix', 'builtin_module_names',
'byteorder',
'call_tracing', 'callstats', 'copyright', 'displayhook',
'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
'executable', 'exit', 'flags', 'float_info',
'float_repr_style',
'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencoding', 'getobjects', 'getprofile',
'getrecursionlimit',
'getrefcount', 'getsizeof', 'getswitchinterval',
'gettotalrefcount',
'gettrace', 'hash_info', 'hexversion', 'implementation',
'int_info',
'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules',
'path',
'path_hooks', 'path_importer_cache', 'platform', 'prefix',
'ps1',
'setcheckinterval', 'setdlopenflags', 'setprofile',
'setrecursionlimit',
'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
'thread_info', 'version', 'version_info', 'warnoptions']

Sin argumentos, dir()enumera los nombres que ha definido actualmente:

>>>

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

Tenga en cuenta que enumera todo tipo de nombres: variables, módulos, funciones,
etc.

dir()no enumera los nombres de funciones y variables integradas. Si desea una


lista de esos, están definidos en el módulo estándar builtins:

>>>

>>> import builtins


>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException',
'BlockingIOError', 'BrokenPipeError', 'BufferError',
'BytesWarning',
'ChildProcessError', 'ConnectionAbortedError',
'ConnectionError',
'ConnectionRefusedError', 'ConnectionResetError',
'DeprecationWarning',
'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception',
'False',
'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
'ImportWarning', 'IndentationError', 'IndexError',
'InterruptedError',
'IsADirectoryError', 'KeyError', 'KeyboardInterrupt',
'LookupError',
'MemoryError', 'NameError', 'None', 'NotADirectoryError',
'NotImplemented',
'NotImplementedError', 'OSError', 'OverflowError',
'PendingDeprecationWarning', 'PermissionError',
'ProcessLookupError',
'ReferenceError', 'ResourceWarning', 'RuntimeError',
'RuntimeWarning',
'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
'UnboundLocalError', 'UnicodeDecodeError',
'UnicodeEncodeError',
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning',
'UserWarning',
'ValueError', 'Warning', 'ZeroDivisionError', '_',
'__build_class__',
'__debug__', '__doc__', '__import__', '__name__',
'__package__', 'abs',
'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes',
'callable',
'chr', 'classmethod', 'compile', 'complex', 'copyright',
'credits',
'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
'exec', 'exit',
'filter', 'float', 'format', 'frozenset', 'getattr', 'globals',
'hasattr',
'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
'issubclass',
'iter', 'len', 'license', 'list', 'locals', 'map', 'max',
'memoryview',
'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print',
'property',
'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr',
'slice',
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple',
'type', 'vars',
'zip']

6.4. Paquetes
Los paquetes son una forma de estructurar el espacio de nombres del módulo de
Python mediante el uso de "nombres de módulo con puntos". Por ejemplo, el
nombre del módulo A.Bdesigna un submódulo nombrado Ben un paquete
llamado A. Así como el uso de módulos evita que los autores de diferentes módulos
tengan que preocuparse por los nombres de las variables globales de los demás, el
uso de nombres de módulos con puntos evita que los autores de paquetes de varios
módulos como NumPy o Pillow tengan que preocuparse por los nombres de los
módulos de los demás. .

Suponga que desea diseñar una colección de módulos (un "paquete") para el
manejo uniforme de archivos de sonido y datos de sonido. Hay muchos formatos de
archivo de sonido diferentes (generalmente reconocidas por su extensión, por
ejemplo: .wav, .aiff, .au), por lo que puede que tenga que crear y mantener una
colección cada vez mayor de módulos para la conversión entre los distintos formatos
de archivo. También hay muchas operaciones diferentes que quizás desee realizar
con datos de sonido (como mezclar, agregar eco, aplicar una función de
ecualizador, crear un efecto estéreo artificial), por lo que, además, estará
escribiendo un flujo interminable de módulos para realizar estas operaciones. Aquí
hay una posible estructura para su paquete (expresada en términos de un sistema
de archivos jerárquico):

sound/ Top-level package


__init__.py Initialize the sound package
formats/ Subpackage for file format
conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...

Al importar el paquete, Python busca en los directorios al sys.pathbuscar el


subdirectorio del paquete.

Los __init__.pyarchivos son necesarios para que Python trate los directorios que
contienen el archivo como paquetes. Esto evita que los directorios con un nombre
común, como por ejemplo string, oculten involuntariamente módulos válidos que
aparecen más adelante en la ruta de búsqueda del módulo. En el caso más
simple, __init__.pypuede ser simplemente un archivo vacío, pero también puede
ejecutar código de inicialización para el paquete o establecer la __all__variable,
que se describe más adelante.

Los usuarios del paquete pueden importar módulos individuales del paquete, por
ejemplo:

import sound.effects.echo

Esto carga el submódulo sound.effects.echo. Debe estar referenciado con su


nombre completo.

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Una forma alternativa de importar el submódulo es:

from sound.effects import echo

Esto también carga el submódulo echoy lo hace disponible sin su prefijo de paquete,
por lo que se puede usar de la siguiente manera:
echo.echofilter(input, output, delay=0.7, atten=4)

Otra variación más es importar la función o variable deseada directamente:

from sound.effects.echo import echofilter

Nuevamente, esto carga el submódulo echo, pero hace que su


función echofilter()esté directamente disponible:

echofilter(input, output, delay=0.7, atten=4)

Tenga en cuenta que al usarlo , el elemento puede ser un submódulo (o


subpaquete) del paquete, o algún otro nombre definido en el paquete, como una
función, clase o variable. La declaración primero prueba si el artículo está definido
en el paquete; si no, asume que es un módulo e intenta cargarlo. Si no lo encuentra,
se genera una excepción.from package import itemimportImportError

Por el contrario, cuando se usa una sintaxis como , cada elemento, excepto el
último, debe ser un paquete; el último elemento puede ser un módulo o un paquete,
pero no puede ser una clase, función o variable definida en el elemento
anterior.import item.subitem.subsubitem

6.4.1. Importando * desde un paquete

Ahora, ¿qué pasa cuando el usuario escribe ? Idealmente, uno esperaría que esto
de alguna manera salga al sistema de archivos, encuentre qué submódulos están
presentes en el paquete y los importe todos. Esto podría llevar mucho tiempo y la
importación de submódulos podría tener efectos secundarios no deseados que solo
deberían ocurrir cuando el submódulo se importa
explícitamente.from sound.effects import *

La única solución es que el autor del paquete proporcione un índice explícito del
paquete. La importdeclaración usa la siguiente convención: si
el __init__.pycódigo de un paquete define una lista nombrada __all__, se toma
como la lista de nombres de módulos que deben importarse cuando se
encuentra. Depende del autor del paquete mantener esta lista actualizada cuando
se lanza una nueva versión del paquete. Los autores de paquetes también pueden
decidir no admitirlo, si no ven un uso para importar * desde su paquete. Por ejemplo,
el archivo podría contener el siguiente
código:from package import *sound/effects/__init__.py

__all__ = ["echo", "surround", "reverse"]

Esto significaría que importaría los tres submódulos del paquete con
nombre.from sound.effects import *sound

Si __all__no está definido, la declaración no no importar todos los submódulos del


paquete en el espacio de nombres actual; solo asegura que el paquete ha sido
importado (posiblemente ejecutando cualquier código de inicialización ) y luego
importa los nombres definidos en el paquete. Esto incluye cualquier nombre definido
(y submódulos cargados explícitamente) por . También incluye los submódulos del
paquete que fueron cargados explícitamente
por declaraciones anteriores . Considere este
código:from sound.effects import *sound.effectssound.effects__ini
t__.py__init__.pyimport

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

En este ejemplo, los módulos echoy surroundse importan en el espacio de


nombres actual porque están definidos en el sound.effectspaquete
cuando from...importse ejecuta la instrucción. (Esto también funciona
cuando __all__está definido).

Aunque ciertos módulos están diseñados para exportar solo nombres que siguen
ciertos patrones cuando los usa , todavía se considera una mala práctica en el
código de producción.import *

Recuerde, ¡no hay nada de malo en usar ! De hecho, esta es la notación


recomendada a menos que el módulo de importación necesite usar submódulos con
el mismo nombre de diferentes
paquetes.from package import specific_submodule
6.4.2. Referencias dentro del paquete

Cuando los paquetes están estructurados en subpaquetes (como con


el soundpaquete en el ejemplo), puede usar importaciones absolutas para referirse
a submódulos de paquetes hermanos. Por ejemplo, si el
módulo sound.filters.vocodernecesita usar el echomódulo en
el sound.effectspaquete, puede usar .from sound.effects import echo

También puede escribir importaciones relativas, con la forma de declaración de


importación. Estas importaciones usan puntos iniciales para indicar los paquetes
actuales y principales involucrados en la importación relativa. Desde el módulo, por
ejemplo, puede usar:from module import namesurround

from . import echo


from .. import formats
from ..filters import equalizer

Tenga en cuenta que las importaciones relativas se basan en el nombre del módulo
actual. Dado que el nombre del módulo principal es siempre "__main__", los
módulos destinados a ser utilizados como módulo principal de una aplicación Python
siempre deben utilizar importaciones absolutas.

6.4.3. Paquetes en varios directorios

Paquetes apoyan un atributo más especial, __path__. Esto se inicializa para ser
una lista que contiene el nombre del directorio que contiene el
paquete __init__.pyantes de que se ejecute el código en ese archivo. Esta
variable se puede modificar; si lo hace, afectará a futuras búsquedas de módulos y
subpaquetes contenidos en el paquete.

Si bien esta función no se necesita a menudo, se puede utilizar para ampliar el


conjunto de módulos que se encuentran en un paquete.

Notas al pie

1
De hecho, las definiciones de funciones también son 'declaraciones' que se
'ejecutan'; la ejecución de una definición de función a nivel de módulo ingresa
el nombre de la función en la tabla de símbolos global del módulo.

7. Entrada y salida
Hay varias formas de presentar el resultado de un programa; los datos pueden
imprimirse en un formato legible por humanos o escribirse en un archivo para uso
futuro. Este capítulo discutirá algunas de las posibilidades.

7.1. Formateo de salida más sofisticado


Hasta ahora hemos encontrado dos formas de escribir valores: declaraciones de
expresión y print()función. (Una tercera forma es usar el write()método de objetos de
archivo; se puede hacer referencia al archivo de salida estándar como sys.stdout.
Consulte la Referencia de la biblioteca para obtener más información sobre esto).

A menudo, querrá tener más control sobre el formato de su salida que simplemente
imprimir valores separados por espacios. Hay varias formas de formatear la salida.

 Para usar literales de cadena formateados , comience una cadena


con fo Fantes de las comillas de apertura o comillas triples. Dentro de esta
cadena, puede escribir una expresión de Python entre {y } caracteres que
pueden hacer referencia a variables o valores literales.

>>>

>>> year = 2016


>>> event = 'Referendum'
>>> f'Results of the {year} {event}'
'Results of the 2016 Referendum'
 El str.format()método de cuerdas requiere más esfuerzo manual. Todavía
usará {y }para marcar dónde se sustituirá una variable y puede proporcionar
directivas de formato detalladas, pero también deberá proporcionar la
información para formatear.

>>>

>>> yes_votes = 42_572_654


>>> no_votes = 43_132_495
>>> percentage = yes_votes / (yes_votes + no_votes)
>>> '{:-9} YES votes {:2.2%}'.format(yes_votes, percentage)
' 42572654 YES votes 49.67%'
 Finalmente, puede hacer todo el manejo de cadenas usted mismo utilizando
operaciones de corte y concatenación de cadenas para crear cualquier
diseño que pueda imaginar. El tipo de cadena tiene algunos métodos que
realizan operaciones útiles para rellenar cadenas a un ancho de columna
dado.

Cuando no necesita una salida elegante, pero solo desea una visualización rápida
de algunas variables con fines de depuración, puede convertir cualquier valor en
una cadena con las funciones repr()o str().

La str()función está destinada a devolver representaciones de valores que son


bastante legibles por humanos, mientras que repr()está destinada a generar
representaciones que pueden ser leídas por el intérprete (o forzarán a SyntaxErrorsi
no hay una sintaxis equivalente). Para los objetos que no tienen una representación
particular para el consumo humano, str()devolverán el mismo valor
que repr(). Muchos valores, como números o estructuras como listas y diccionarios,
tienen la misma representación con cualquiera de las funciones. Las cadenas, en
particular, tienen dos representaciones distintas.

Algunos ejemplos:

>>>

>>> s = 'Hello, world.'


>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y)
+ '...'
>>> print(s)
The value of x is 32.5, and y is 40000...
>>> # The repr() of a string adds string quotes and backslashes:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # The argument to repr() may be any Python object:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"

El stringmódulo contiene una Templateclase que ofrece otra forma de sustituir valores
en cadenas, usando marcadores de posición como $xy reemplazándolos con
valores de un diccionario, pero ofrece mucho menos control del formato.

7.1.1. Literales de cadena formateados

Los literales de cadena formateados (también llamados f-strings para abreviar) le


permiten incluir el valor de las expresiones de Python dentro de una cadena
prefijando la cadena confoFy escribiendo expresiones como {expression}.

Un especificador de formato opcional puede seguir a la expresión. Esto permite un


mayor control sobre cómo se formatea el valor. El siguiente ejemplo redondea pi a
tres lugares después del decimal:

>>>

>>> import math


>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.

Pasar un entero después de ':'hará que ese campo tenga un número mínimo de
caracteres de ancho. Esto es útil para alinear las columnas.

>>>

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}


>>> for name, phone in table.items():
... print(f'{name:10} ==> {phone:10d}')
...
Sjoerd ==> 4127
Jack ==> 4098
Dcab ==> 7678
Se pueden utilizar otros modificadores para convertir el valor antes de
formatearlo. '!a'aplica ascii(), '!s'aplica str()y '!r' aplica repr():

>>>

>>> animals = 'eels'


>>> print(f'My hovercraft is full of {animals}.')
My hovercraft is full of eels.
>>> print(f'My hovercraft is full of {animals!r}.')
My hovercraft is full of 'eels'.

Para obtener una referencia sobre estas especificaciones de formato, consulte la


guía de referencia del minilenguaje de especificación de formato .

7.1.2. El método String format ()

El uso básico del str.format()método se ve así:

>>>

>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))


We are the knights who say "Ni!"

Los corchetes y los caracteres dentro de ellos (llamados campos de formato) se


reemplazan con los objetos que se pasan al str.format()método. Se puede usar un
número entre corchetes para hacer referencia a la posición del objeto pasado
al str.format()método.

>>>

>>> print('{0} and {1}'.format('spam', 'eggs'))


spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

Si se utilizan argumentos de palabras clave en el str.format()método, se hace


referencia a sus valores mediante el nombre del argumento.

>>>

>>> print('This {food} is {adjective}.'.format(


... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.

Los argumentos posicionales y de palabras clave se pueden combinar


arbitrariamente:

>>>

>>> print('The story of {0}, {1}, and {other}.'.format('Bill',


'Manfred',

other='Georg'))
The story of Bill, Manfred, and Georg.

Si tiene una cadena de formato realmente larga que no desea dividir, sería bueno
si pudiera hacer referencia a las variables que se formatearán por nombre en lugar
de por posición. Esto se puede hacer simplemente pasando el dict y usando
corchetes '[]'para acceder a las claves.

>>>

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}


>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
... 'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Esto también se puede hacer pasando la tabla como argumentos de palabras clave
con la notación '**'.

>>>

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}


>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab:
{Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Esto es particularmente útil en combinación con la función incorporada vars(), que


devuelve un diccionario que contiene todas las variables locales.

Como ejemplo, las siguientes líneas producen un conjunto de columnas


ordenadamente alineadas que dan números enteros y sus cuadrados y cubos:
>>>

>>> for x in range(1, 11):


... print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

Para obtener una descripción general completa del formato de cadena


con str.format(), consulte Sintaxis de cadena de formato .

7.1.3. Formateo manual de cadenas

Aquí está la misma tabla de cuadrados y cubos, formateada manualmente:

>>>

>>> for x in range(1, 11):


... print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
... # Note use of 'end' on previous line
... print(repr(x*x*x).rjust(4))
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

(Tenga en cuenta que, por cierto, se agregó un espacio entre cada columna print():
siempre agrega espacios entre sus argumentos).
El str.rjust()método de objetos de cadena justifica a la derecha una cadena en un
campo de un ancho dado rellenándola con espacios a la izquierda. Hay métodos
similares str.ljust()y str.center(). Estos métodos no escriben nada, solo devuelven una
nueva cadena. Si la cadena de entrada es demasiado larga, no la truncan, sino que
la devuelven sin cambios; esto estropeará el diseño de su columna, pero suele ser
mejor que la alternativa, que sería mentir sobre un valor. (Si realmente desea el
truncamiento, siempre puede agregar una operación de corte, como en x.ljust(n)[:n].)

Hay otro método, str.zfill()que rellena una cadena numérica a la izquierda con
ceros. Entiende los signos más y menos:

>>>

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

7.1.4. Formateo de cadenas antiguo

El operador% (módulo) también se puede utilizar para formatear cadenas. Dado ,


las instancias de in se reemplazan con cero o más elementos de . Esta operación
se conoce comúnmente como interpolación de cadenas. Por
ejemplo:'string' % values%stringvalues

>>>

>>> import math


>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.

Puede encontrar más información en la sección Formato de cadena de estilo printf .

7.2. Leer y escribir archivos


open()devuelve un objeto de archivo , y se utiliza más comúnmente con dos
argumentos: .open(filename, mode)

>>>
>>> f = open('workfile', 'w')

El primer argumento es una cadena que contiene el nombre del archivo. El segundo
argumento es otra cadena que contiene algunos caracteres que describen la forma
en que se utilizará el archivo. el modo puede ser 'r'cuando el archivo solo se
leerá, 'w' solo para escribir (se borrará un archivo existente con el mismo nombre),
y 'a'abre el archivo para agregarlo; cualquier dato escrito en el archivo se agrega
automáticamente al final. 'r+'abre el archivo para lectura y escritura. El argumento
de modo es opcional; 'r'se asumirá si se omite.

Normalmente, los archivos se abren en modo texto , es decir, usted lee y escribe
cadenas desde y hacia el archivo, que están codificadas en una codificación
específica. Si no se especifica la codificación, el valor predeterminado depende de
la plataforma (consulte open()). 'b'adjunto al modo abre el archivo en modo binario :
ahora los datos se leen y escriben en forma de objetos bytes. Este modo debe
usarse para todos los archivos que no contienen texto.

En el modo de texto, el valor predeterminado al leer es convertir los finales de línea


específicos de la plataforma ( \nen Unix, \r\nen Windows) a solo \n. Al escribir en
modo de texto, el valor predeterminado es convertir las apariciones de los \nfinales
de línea de regreso a la plataforma específica. Esta modificación detrás de escena
de los datos de los archivos está bien para los archivos de texto, pero dañará los
datos binarios como esos en los archivos JPEGo EXE. Tenga mucho cuidado al
utilizar el modo binario al leer y escribir dichos archivos.

Es una buena práctica utilizar la withpalabra clave cuando se trata de objetos de


archivo. La ventaja es que el archivo se cierra correctamente después de que
finaliza su suite, incluso si se genera una excepción en algún momento. El
uso withtambién es mucho más corto que el equivalente de
escritura try: finallybloques:

>>>

>>> with open('workfile') as f:


... read_data = f.read()

>>> # We can check that the file has been automatically closed.
>>> f.closed
True
Si no está utilizando la withpalabra clave, debe llamar f.close()para cerrar el archivo
y liberar inmediatamente los recursos del sistema que utiliza. Si no cierra
explícitamente un archivo, el recolector de basura de Python eventualmente
destruirá el objeto y cerrará el archivo abierto por usted, pero el archivo puede
permanecer abierto por un tiempo. Otro riesgo es que diferentes implementaciones
de Python harán esta limpieza en diferentes momentos.

Después de cerrar un objeto de archivo, ya sea mediante una withdeclaración o


mediante una llamada f.close(), los intentos de utilizar el objeto de archivo fallarán
automáticamente.

>>>

>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

7.2.1. Métodos de objetos de archivo

El resto de los ejemplos de esta sección supondrá que fya se ha creado un objeto
de archivo llamado .

Para leer el contenido de un archivo, llame a f.read(size), que lee cierta cantidad de
datos y lo devuelve como una cadena (en modo texto) o un objeto de bytes (en
modo binario). el tamaño es un argumento numérico opcional. Cuando se omite
el tamaño o es negativo, se leerá y devolverá todo el contenido del archivo; es su
problema si el archivo es dos veces más grande que la memoria de su máquina. De
lo contrario, se leen y devuelven como máximo caracteres de tamaño (en modo de
texto) o bytes de tamaño (en modo binario). Si se ha llegado al final del
archivo, f.read()devolverá una cadena vacía ( '').

>>>

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''
f.readline()lee una sola línea del archivo; \nse deja un carácter de nueva línea ( ) al
final de la cadena y solo se omite en la última línea del archivo si el archivo no
termina en una nueva línea. Esto hace que el valor de retorno sea
inequívoco; si f.readline()devuelve una cadena vacía, se ha llegado al final del
archivo, mientras que una línea en blanco está representada por '\n'una cadena que
contiene solo una nueva línea.

>>>

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

Para leer líneas de un archivo, puede recorrer el objeto de archivo. Esto es eficiente
en memoria, rápido y conduce a un código simple:

>>>

>>> for line in f:


... print(line, end='')
...
This is the first line of the file.
Second line of the file

Si desea leer todas las líneas de un archivo en una lista, también puede
usar list(f)o f.readlines().

f.write(string)escribe el contenido de la cadena en el archivo, devolviendo el número


de caracteres escritos.

>>>

>>> f.write('This is a test\n')


15

Otros tipos de objetos deben convertirse, ya sea en una cadena (en modo texto) o
en un objeto de bytes (en modo binario), antes de escribirlos:

>>>
>>> value = ('the answer', 42)
>>> s = str(value) # convert the tuple to string
>>> f.write(s)
18

f.tell() devuelve un número entero que indica la posición actual del objeto de archivo
en el archivo representado como el número de bytes desde el principio del archivo
cuando está en modo binario y un número opaco cuando está en modo de texto.

Para cambiar la posición del objeto de archivo, use . La posición se calcula sumando
el desplazamiento a un punto de referencia; el punto de referencia es seleccionada
por el dónde argumento. Un valor de donde 0 mide desde el principio del archivo, 1
usa la posición actual del archivo y 2 usa el final del archivo como punto de
referencia. donde se puede omitir y el valor predeterminado es 0, utilizando el
comienzo del archivo como punto de referencia.f.seek(offset, whence)

>>>

>>> f = open('workfile', 'rb+')


>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5) # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'

En los archivos de texto (los que se abren sin una ben la cadena de modo), solo se
permiten búsquedas relativas al principio del archivo (la excepción es buscar hasta
el final del archivo con ) y los únicos valores de compensación válidos son los
devueltos por , o cero. Cualquier otro valor de compensación produce un
comportamiento indefinido.seek(0, 2)f.tell()

Los objetos de archivo tienen algunos métodos adicionales,


como isatty()y truncate()que se utilizan con menos frecuencia; consulte la Referencia
de la biblioteca para obtener una guía completa de los objetos de archivo.
7.2.2. Guardar datos estructurados con json

Las cadenas se pueden escribir y leer fácilmente en un archivo. Los números


requieren un poco más de esfuerzo, ya que el read()método solo devuelve cadenas,
que tendrán que pasarse a una función como int(), que toma una cadena como '123' y
devuelve su valor numérico 123. Cuando desee guardar tipos de datos más
complejos como listas anidadas y diccionarios, analizar y serializar a mano se
vuelve complicado.

En lugar de que los usuarios escriban y depuren constantemente el código para


guardar tipos de datos complicados en archivos, Python le permite usar el popular
formato de intercambio de datos llamado JSON (JavaScript Object Notation) . El
módulo estándar llamado jsonpuede tomar jerarquías de datos de Python y
convertirlas en representaciones de cadenas; este proceso se
llama serialización . La reconstrucción de los datos a partir de la representación de
cadena se denomina deserialización . Entre la serialización y deserialización, la
cadena que representa el objeto puede haber sido almacenada en un archivo o
datos, o enviada a través de una conexión de red a alguna máquina distante.

Nota

Las aplicaciones modernas suelen utilizar el formato JSON para permitir el


intercambio de datos. Muchos programadores ya están familiarizados con él, lo que
lo convierte en una buena opción para la interoperabilidad.

Si tiene un objeto x, puede ver su representación de cadena JSON con una simple
línea de código:

>>>

>>> import json


>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'

Otra variante de la dumps()función, llamada dump(), simplemente serializa el objeto


en un archivo de texto . Entonces, si fes un objeto de archivo de texto abierto para
escritura, podemos hacer esto:
json.dump(x, f)

Para decodificar el objeto nuevamente, si fes un objeto de archivo de texto que se


ha abierto para lectura:

x = json.load(f)

Esta sencilla técnica de serialización puede manejar listas y diccionarios, pero


serializar instancias de clases arbitrarias en JSON requiere un poco de esfuerzo
adicional. La referencia del jsonmódulo contiene una explicación de esto.

Ver también

pickle - el módulo de pepinillos

A diferencia de JSON , pickle es un protocolo que permite la serialización de objetos


Python arbitrariamente complejos. Como tal, es específico de Python y no se puede
utilizar para comunicarse con aplicaciones escritas en otros lenguajes. También es
inseguro de forma predeterminada: la deserialización de datos pickle que provienen
de una fuente no confiable puede ejecutar código arbitrario, si los datos fueron
creados por un atacante experto.

8. Errores y excepciones
Hasta ahora, los mensajes de error no se han mencionado más que, pero si ha
probado los ejemplos, probablemente haya visto algunos. Hay (al menos) dos tipos
de errores distinguibles: errores de sintaxis y excepciones .

8.1. Errores de sintaxis


Los errores de sintaxis, también conocidos como errores de análisis, son quizás el
tipo de queja más común que recibe mientras aún está aprendiendo Python:

>>>

>>> while True print('Hello world')


File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax

El analizador repite la línea infractora y muestra una pequeña 'flecha' que apunta al
punto más temprano de la línea donde se detectó el error. El error es causado por
(o al menos detectado en) el token que precede a la flecha: en el ejemplo, el error
se detecta en la función print(), ya que ':'faltan dos puntos ( ) antes. El nombre
del archivo y el número de línea se imprimen para que sepa dónde buscar en caso
de que la entrada provenga de un script.

8.2. Excepciones
Incluso si una declaración o expresión es sintácticamente correcta, puede causar
un error cuando se intenta ejecutarla. Los errores detectados durante la ejecución
se denominan excepciones y no son incondicionalmente fatales: pronto aprenderá
a manejarlos en los programas Python. Sin embargo, la mayoría de las excepciones
no son manejadas por programas y dan como resultado mensajes de error como se
muestra aquí:

>>>

>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

La última línea del mensaje de error indica lo que sucedió. Las excepciones vienen
en diferentes tipos, y el tipo se imprime como parte del mensaje: los tipos en el
ejemplo son ZeroDivisionError, NameErrory TypeError. La cadena impresa
como tipo de excepción es el nombre de la excepción incorporada que ocurrió. Esto
es cierto para todas las excepciones integradas, pero no es necesario que sea cierto
para las excepciones definidas por el usuario (aunque es una convención útil). Los
nombres de excepción estándar son identificadores integrados (no palabras clave
reservadas).

El resto de la línea proporciona detalles según el tipo de excepción y su causa.

La parte anterior del mensaje de error muestra el contexto donde ocurrió la


excepción, en forma de seguimiento de pila. En general, contiene un seguimiento
de pila que enumera las líneas de origen; sin embargo, no mostrará las líneas leídas
desde la entrada estándar.

Excepciones integradas enumera las excepciones integradas y sus significados.

8.3. Manejo de excepciones


Es posible escribir programas que manejen excepciones seleccionadas. Mire el
siguiente ejemplo, que le pide al usuario que ingrese hasta que se ingrese un entero
válido, pero le permite al usuario interrumpir el programa (usando Control-Co lo que
sea que admita el sistema operativo); tenga en cuenta que una interrupción
generada por el usuario se indica al generar la KeyboardInterruptexcepción.

>>>

>>> while True:


... try:
... x = int(input("Please enter a number: "))
... break
... except ValueError:
... print("Oops! That was no valid number. Try
again...")
...

La trydeclaración funciona de la siguiente manera.

 Primero, se ejecuta la cláusula try (las declaraciones entre las palabras


clave tryy except).
 Si no se produce ninguna excepción, se omite la cláusula
except y tryfinaliza la ejecución de la instrucción.
 Si se produce una excepción durante la ejecución de la cláusula try, se salta
el resto de la cláusula. Luego, si su tipo coincide con la excepción nombrada
después de la exceptpalabra clave, se ejecuta la cláusula except y luego la
ejecución continúa después de la trydeclaración.
 Si ocurre una excepción que no coincide con la excepción nombrada en la
cláusula except, se pasa a las trydeclaraciones externas ; si no se encuentra
ningún controlador, es una excepción no controlada y la ejecución se detiene
con un mensaje como se muestra arriba.

Una trydeclaración puede tener más de una cláusula except, para especificar
controladores para diferentes excepciones. Como máximo se ejecutará un
controlador. Los manejadores solo manejan excepciones que ocurren en la cláusula
try correspondiente, no en otros manejadores de la misma tryinstrucción. Una
cláusula except puede nombrar múltiples excepciones como una tupla entre
paréntesis, por ejemplo:

... except (RuntimeError, TypeError, NameError):


... pass

Una clase en una exceptcláusula es compatible con una excepción si es la misma


clase o una clase base de la misma (pero no al revés: una cláusula except que
enumera una clase derivada no es compatible con una clase base). Por ejemplo, el
siguiente código imprimirá B, C, D en ese orden:

class B(Exception):
pass

class C(B):
pass

class D(C):
pass

for cls in [B, C, D]:


try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
Tenga en cuenta que si se invirtieran las cláusulas except (con la primera), se habría
impreso B, B, B: se activará la primera cláusula except coincidente.except B

La última cláusula except puede omitir el (los) nombre (s) de excepción, para que
sirva como comodín. Use esto con extrema precaución, ya que es fácil enmascarar
un error de programación real de esta manera. También se puede usar para imprimir
un mensaje de error y luego volver a generar la excepción (permitiendo que una
persona que llama también maneje la excepción):

import sys

try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise

La instrucción try… excepttiene una cláusula else opcional , que, cuando está
presente, debe seguir todas las cláusulas excepto. Es útil para el código que debe
ejecutarse si la cláusula try no genera una excepción. Por ejemplo:

for arg in sys.argv[1:]:


try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()

El uso de la elsecláusula es mejor que agregar código adicional a la trycláusula


porque evita atrapar accidentalmente una excepción que no fue provocada por el
código protegido por la declaración try… except.
Cuando ocurre una excepción, puede tener un valor asociado, también conocido
como argumento de la excepción . La presencia y el tipo de argumento dependen
del tipo de excepción.

La cláusula except puede especificar una variable después del nombre de la


excepción. La variable está vinculada a una instancia de excepción con los
argumentos almacenados en instance.args. Por conveniencia, la instancia de
excepción define de __str__()modo que los argumentos se puedan imprimir
directamente sin tener que hacer referencia .args. También se puede crear una
instancia de una excepción antes de generarla y agregarle los atributos que desee.

>>>

>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst)) # the exception instance
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed
directly,
... # but may be overridden in
exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

Si una excepción tiene argumentos, se imprimen como la última parte ('detalle') del
mensaje para las excepciones no controladas.

Los controladores de excepciones no solo manejan las excepciones si ocurren


inmediatamente en la cláusula try, sino también si ocurren dentro de funciones que
se llaman (incluso indirectamente) en la cláusula try. Por ejemplo:

>>>

>>> def this_fails():


... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: division by zero

8.4. Generación de excepciones


La raisedeclaración permite al programador forzar la ocurrencia de una excepción
específica. Por ejemplo:

>>>

>>> raise NameError('HiThere')


Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere

El único argumento de raiseindica la excepción que debe plantearse. Debe ser


una instancia de excepción o una clase de excepción (una clase que se deriva
de Exception). Si se pasa una clase de excepción, se instanciará implícitamente
llamando a su constructor sin argumentos:

raise ValueError # shorthand for 'raise ValueError()'

Si necesita determinar si se generó una excepción pero no tiene la intención de


manejarla, una forma más simple de la raisedeclaración le permite volver a
generar la excepción:

>>>

>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: HiThere

8.5. Excepciones definidas por el usuario


Los programas pueden nombrar sus propias excepciones creando una nueva clase
de excepción (consulte Clases para obtener más información sobre las clases de
Python). Normalmente, las excepciones deben derivarse de la Exceptionclase, ya
sea directa o indirectamente.

Se pueden definir clases de excepción que hacen cualquier cosa que pueda hacer
cualquier otra clase, pero generalmente se mantienen simples, a menudo solo
ofrecen una serie de atributos que permiten que los controladores extraigan
información sobre el error para la excepción. Al crear un módulo que puede generar
varios errores distintos, una práctica común es crear una clase base para las
excepciones definidas por ese módulo y una subclase para crear clases de
excepción específicas para diferentes condiciones de error:

class Error(Exception):
"""Base class for exceptions in this module."""
pass

class InputError(Error):
"""Exception raised for errors in the input.

Attributes:
expression -- input expression in which the error
occurred
message -- explanation of the error
"""

def __init__(self, expression, message):


self.expression = expression
self.message = message

class TransitionError(Error):
"""Raised when an operation attempts a state transition
that's not
allowed.

Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is
not allowed
"""

def __init__(self, previous, next, message):


self.previous = previous
self.next = next
self.message = message

La mayoría de las excepciones se definen con nombres que terminan en "Error",


similar al nombre de las excepciones estándar.

Muchos módulos estándar definen sus propias excepciones para informar errores
que pueden ocurrir en las funciones que definen. Se presenta más información
sobre las clases en el capítulo Clases .

8.6. Definición de acciones de limpieza


La trydeclaración tiene otra cláusula opcional que está destinada a definir acciones
de limpieza que deben ejecutarse en todas las circunstancias. Por ejemplo:

>>>

>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in <module>

Si hay una finallycláusula presente, la finally cláusula se ejecutará como la


última tarea antes de try que se complete la declaración. La finallycláusula se
ejecuta tanto si la trydeclaración produce una excepción como si no . Los
siguientes puntos discuten casos más complejos cuando ocurre una excepción:

 Si ocurre una excepción durante la ejecución de la try cláusula, la excepción


puede ser manejada por una except cláusula. Si la excepción no es
manejada por una except cláusula, la excepción se vuelve a generar
después de finally que se haya ejecutado la cláusula.
 Podría producirse una excepción durante la ejecución de
una cláusula except o else. Nuevamente, la excepción se vuelve a generar
después de finallyque se haya ejecutado la cláusula.
 Si la tryinstrucción llega a una instrucción break, continueo return,
la finallycláusula se ejecutará justo antes de la ejecución de la
instrucción break, continueo return.
 Si una finallycláusula incluye una return declaración, el valor devuelto
será el de la declaración de la finallycláusula return, no el valor de
la declaración de la trycláusula return.

Por ejemplo:

>>>

>>> def bool_return():


... try:
... return True
... finally:
... return False
...
>>> bool_return()
False

Un ejemplo más complicado:

>>>

>>> def divide(x, y):


... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Como puede ver, la finallycláusula se ejecuta en cualquier


caso. El TypeErroraumento al dividir dos cadenas no se maneja con
la exceptcláusula y, por lo tanto, se vuelve a generar después de finally que se
haya ejecutado la cláusula.

En aplicaciones del mundo real, la finallycláusula es útil para liberar recursos


externos (como archivos o conexiones de red), independientemente de si el uso del
recurso fue exitoso.

8.7. Acciones de limpieza predefinidas


Algunos objetos definen acciones de limpieza estándar que se realizarán cuando el
objeto ya no sea necesario, independientemente de si la operación que utiliza el
objeto tuvo éxito o no. Mire el siguiente ejemplo, que intenta abrir un archivo e
imprimir su contenido en la pantalla.

for line in open("myfile.txt"):


print(line, end="")

El problema con este código es que deja el archivo abierto durante un período de
tiempo indeterminado después de que esta parte del código ha terminado de
ejecutarse. Esto no es un problema en scripts simples, pero puede ser un problema
para aplicaciones más grandes. La withdeclaración permite que objetos como
archivos se utilicen de una manera que garantice que siempre se limpien de manera
rápida y correcta.

with open("myfile.txt") as f:
for line in f:
print(line, end="")
Después de que se ejecuta la instrucción, el archivo f siempre se cierra, incluso si
se encontró un problema al procesar las líneas. Los objetos que, como los archivos,
proporcionan acciones de limpieza predefinidas, lo indicarán en su documentación.

9. Clases
Las clases proporcionan un medio para agrupar datos y funciones. La creación de
una nueva clase crea un nuevo tipo de objeto, lo que
permite crear nuevas instancias de ese tipo. Cada instancia de clase puede tener
atributos adjuntos para mantener su estado. Las instancias de clase también
pueden tener métodos (definidos por su clase) para modificar su estado.

En comparación con otros lenguajes de programación, el mecanismo de clases de


Python agrega clases con un mínimo de nueva sintaxis y semántica. Es una mezcla
de los mecanismos de clase que se encuentran en C ++ y Modula-3. Las clases de
Python proporcionan todas las características estándar de la programación
orientada a objetos: el mecanismo de herencia de clases permite múltiples clases
base, una clase derivada puede anular cualquier método de su clase o clases base,
y un método puede llamar al método de una clase base con el mismo nombre . Los
objetos pueden contener cantidades y tipos de datos arbitrarios. Como ocurre con
los módulos, las clases participan de la naturaleza dinámica de Python: se crean en
tiempo de ejecución y se pueden modificar después de la creación.

En la terminología de C ++, normalmente los miembros de la clase (incluidos los


miembros de datos) son públicos (excepto ver más adelante Variables privadas ) y
todas las funciones de los miembros son virtuales . Como en Modula-3, no hay
atajos para hacer referencia a los miembros del objeto desde sus métodos: la
función del método se declara con un primer argumento explícito que representa el
objeto, que es proporcionado implícitamente por la llamada. Como en Smalltalk, las
clases en sí mismas son objetos. Esto proporciona semántica para importar y
cambiar el nombre. A diferencia de C ++ y Modula-3, los tipos integrados se pueden
usar como clases base para que el usuario los extienda. Además, como en C ++, la
mayoría de los operadores integrados con sintaxis especial (operadores aritméticos,
subíndices, etc.) se pueden redefinir para instancias de clases.

(Al carecer de terminología universalmente aceptada para hablar de clases, haré


uso ocasional de términos de Smalltalk y C ++. Usaría términos de Modula-3, ya
que su semántica orientada a objetos es más cercana a la de Python que a la de C
++, pero espero que pocos lectores he oído hablar de él.)

9.1. Unas palabras sobre nombres y objetos


Los objetos tienen individualidad y varios nombres (en varios ámbitos) se pueden
vincular al mismo objeto. Esto se conoce como aliasing en otros idiomas. Por lo
general, esto no se aprecia a primera vista en Python y se puede ignorar con
seguridad cuando se trata de tipos básicos inmutables (números, cadenas,
tuplas). Sin embargo, la creación de alias tiene un efecto posiblemente sorprendente
en la semántica del código Python que involucra objetos mutables como listas,
diccionarios y la mayoría de los otros tipos. Esto generalmente se usa en beneficio
del programa, ya que los alias se comportan como punteros en algunos
aspectos. Por ejemplo, pasar un objeto es barato ya que la implementación sólo
pasa un puntero; y si una función modifica un objeto pasado como argumento, la
persona que llama verá el cambio; esto elimina la necesidad de dos mecanismos
de paso de argumentos diferentes como en Pascal.

9.2. Ámbitos y espacios de nombres de Python


Antes de presentar las clases, primero tengo que contarte algo sobre las reglas de
alcance de Python. Las definiciones de clase juegan algunos trucos ingeniosos con
los espacios de nombres, y necesita saber cómo funcionan los ámbitos y los
espacios de nombres para comprender completamente lo que está sucediendo. Por
cierto, el conocimiento sobre este tema es útil para cualquier programador avanzado
de Python.

Comencemos con algunas definiciones.

Un espacio de nombres es un mapeo de nombres a objetos. La mayoría de los


espacios de nombres se implementan actualmente como diccionarios de Python,
pero eso normalmente no se nota de ninguna manera (excepto por el rendimiento)
y puede cambiar en el futuro. Ejemplos de espacios de nombres son: el conjunto de
nombres integrados (que contienen funciones como abs(), y nombres de
excepción integrados); los nombres globales en un módulo; y los nombres locales
en una invocación de función. En cierto sentido, el conjunto de atributos de un objeto
también forma un espacio de nombres. Lo importante que debe saber sobre los
espacios de nombres es que no existe absolutamente ninguna relación entre los
nombres en diferentes espacios de nombres; por ejemplo, dos módulos diferentes
pueden definir una función maximizesin confusión: los usuarios de los módulos
deben anteponerlo con el nombre del módulo.

Por cierto, utilizo la palabra atributo para cualquier nombre que siga a un punto; por
ejemplo, en la expresión z.real, reales un atributo del objeto z. Estrictamente
hablando, las referencias a nombres en módulos son referencias a atributos: en la
expresión modname.funcname, modnamees un objeto de módulo y funcnamees un
atributo del mismo. En este caso, resulta que hay un mapeo sencillo entre los
atributos del módulo y los nombres globales definidos en el módulo: ¡comparten el
mismo espacio de nombres! 1

Los atributos pueden ser de solo lectura o de escritura. En el último caso, es posible
la asignación a atributos. Los atributos del módulo se pueden escribir: puede
escribir . Los atributos de escritura también se pueden eliminar con
la declaración. Por ejemplo, eliminará el atributo del objeto nombrado
por .modname.the_answer = 42deldel modname.the_answerthe_answermo
dname

Los espacios de nombres se crean en diferentes momentos y tienen diferentes


vidas. El espacio de nombres que contiene los nombres integrados se crea cuando
se inicia el intérprete de Python y nunca se elimina. El espacio de nombres global
para un módulo se crea cuando se lee la definición del módulo; normalmente, los
espacios de nombres de los módulos también duran hasta que el intérprete se
cierra. Las declaraciones ejecutadas por la invocación de nivel superior del
intérprete, ya sea leídas desde un archivo de script o de forma interactiva, se
consideran parte de un módulo llamado __main__, por lo que tienen su propio
espacio de nombres global. (Los nombres integrados en realidad también viven en
un módulo; esto se llama builtins).

El espacio de nombres local para una función se crea cuando se llama a la función
y se elimina cuando la función devuelve o genera una excepción que no se maneja
dentro de la función. (De hecho, olvidar sería una mejor manera de describir lo que
realmente sucede). Por supuesto, las invocaciones recursivas tienen cada una su
propio espacio de nombres local.

Un alcance es una región textual de un programa Python donde se puede acceder


directamente a un espacio de nombres. "Directamente accesible" aquí significa que
una referencia no calificada a un nombre intenta encontrar el nombre en el espacio
de nombres.

Aunque los alcances se determinan de forma estática, se utilizan de forma


dinámica. En cualquier momento durante la ejecución, hay 3 o 4 ámbitos anidados
cuyos espacios de nombres son directamente accesibles:

 el ámbito más interno, que se busca primero, contiene los nombres locales
 los alcances de cualquier función adjunta, que se buscan comenzando con
el alcance adjunto más cercano, contienen nombres no locales, pero también
no globales
 el penúltimo alcance contiene los nombres globales del módulo actual
 el alcance más externo (buscado en último lugar) es el espacio de nombres
que contiene los nombres integrados

Si un nombre se declara global, todas las referencias y asignaciones van


directamente al ámbito intermedio que contiene los nombres globales del
módulo. Para volver a vincular las variables que se encuentran fuera del ámbito más
interno, nonlocalse puede utilizar la declaración; si no se declara no local, esas
variables son de solo lectura (un intento de escribir en dicha variable simplemente
creará una nueva variable local en el ámbito más interno, dejando sin cambios la
variable externa con el mismo nombre).

Por lo general, el ámbito local hace referencia a los nombres locales de la función
actual (textualmente). Fuera de las funciones, el ámbito local hace referencia al
mismo espacio de nombres que el ámbito global: el espacio de nombres del
módulo. Las definiciones de clase colocan otro espacio de nombres en el ámbito
local.

Es importante darse cuenta de que los alcances se determinan textualmente: el


alcance global de una función definida en un módulo es el espacio de nombres de
ese módulo, sin importar desde dónde o por qué alias se llame a la función. Por otro
lado, la búsqueda real de nombres se realiza dinámicamente, en tiempo de
ejecución; sin embargo, la definición del lenguaje está evolucionando hacia la
resolución de nombres estática, en el momento de "compilación", ¡así que no confíe
en la resolución dinámica de nombres! (De hecho, las variables locales ya están
determinadas estáticamente).
Una peculiaridad especial de Python es que, si no hay
ninguna declaración globalo nonlocalen efecto, las asignaciones a los nombres
siempre van al ámbito más interno. Las asignaciones no copian datos, solo unen
nombres a objetos. Lo mismo es cierto para las eliminaciones: la declaración elimina
el enlace del espacio de nombres al que hace referencia el ámbito local. De hecho,
todas las operaciones que introducen nuevos nombres utilizan el ámbito local: en
particular, las declaraciones y definiciones de funciones vinculan el nombre del
módulo o función en el ámbito local.del xximport

La globaldeclaración se puede utilizar para indicar que determinadas variables


viven en el ámbito global y deben recuperarse allí; la nonlocaldeclaración indica
que las variables particulares viven en un ámbito circundante y deben rebotarse allí.

9.2.1. Ejemplo de ámbitos y espacios de nombres

Este es un ejemplo que demuestra cómo hacer referencia a los diferentes ámbitos
y espacios de nombres, y cómo globaly nonlocalafectó a la unión variable:

def scope_test():
def do_local():
spam = "local spam"

def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"

def do_global():
global spam
spam = "global spam"

spam = "test spam"


do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

La salida del código de ejemplo es:


After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

Tenga en cuenta cómo la asignación local (que es la predeterminada) no cambió la


vinculación de spam de scope_test . La asignación cambió la vinculación
de spam de scope_test , y la asignación cambió la vinculación a nivel de
módulo.nonlocalglobal

También puede ver que no había ningún enlace previo para el spam antes de
la globalasignación.

9.3. Un primer vistazo a las clases


Las clases introducen un poco de sintaxis nueva, tres nuevos tipos de objetos y algo
de semántica nueva.

9.3.1. Sintaxis de definición de clase

La forma más simple de definición de clase se ve así:

class ClassName:
<statement-1>
.
.
.
<statement-N>

Las definiciones de clases, como las definiciones de funciones ( defdeclaraciones),


deben ejecutarse antes de que tengan algún efecto. (Posiblemente podría colocar
una definición de clase en una rama de una ifdeclaración o dentro de una función).

En la práctica, las declaraciones dentro de una definición de clase generalmente


serán definiciones de funciones, pero se permiten otras declaraciones y, a veces,
son útiles; volveremos a esto más adelante. Las definiciones de funciones dentro
de una clase normalmente tienen una forma peculiar de lista de argumentos, dictada
por las convenciones de llamadas para métodos; nuevamente, esto se explica más
adelante.
Cuando se ingresa una definición de clase, se crea un nuevo espacio de nombres
y se usa como ámbito local; por lo tanto, todas las asignaciones a las variables
locales entran en este nuevo espacio de nombres. En particular, las definiciones de
funciones unen aquí el nombre de la nueva función.

Cuando una definición de clase se deja normalmente (al final), se crea un objeto de
clase . Esto es básicamente un envoltorio alrededor del contenido del espacio de
nombres creado por la definición de clase; aprenderemos más sobre los objetos de
clase en la siguiente sección. El ámbito local original (el que estaba en vigor justo
antes de que se ingresara la definición de clase) se restablece, y el objeto de clase
está vinculado aquí al nombre de clase dado en el encabezado de definición de
clase ( ClassNameen el ejemplo).

9.3.2. Objetos de clase

Los objetos de clase admiten dos tipos de operaciones: referencias de atributos y


creación de instancias.

Referencias a atributos utilizan la sintaxis estándar que se utiliza para todas las
referencias a atributos en Python: obj.name. Los nombres de atributo válidos son
todos los nombres que estaban en el espacio de nombres de la clase cuando se
creó el objeto de clase. Entonces, si la definición de la clase se ve así:

class MyClass:
"""A simple example class"""
i = 12345

def f(self):
return 'hello world'

entonces MyClass.iy MyClass.fson referencias de atributo válidas, que


devuelven un entero y un objeto de función, respectivamente. También se pueden
asignar atributos de clase, por lo que puede cambiar el valor de MyClass.ipor
asignación. __doc__es también un atributo válido, devolviendo la cadena de
documentación perteneciente a la clase: ."A simple example class"

La instanciación de clases utiliza la notación de funciones. Simplemente imagina


que el objeto de la clase es una función sin parámetros que devuelve una nueva
instancia de la clase. Por ejemplo (asumiendo la clase anterior):
x = MyClass()

crea una nueva instancia de la clase y asigna este objeto a la variable local x.

La operación de instanciación ("llamar" a un objeto de clase) crea un objeto vacío. A


muchas clases les gusta crear objetos con instancias personalizadas para un estado
inicial específico. Por lo tanto, una clase puede definir un método especial
llamado __init__()así:

def __init__(self):
self.data = []

Cuando una clase define un __init__()método, la instanciación de clase invoca


automáticamente la instancia de clase __init__()recién creada. Entonces, en
este ejemplo, una nueva instancia inicializada se puede obtener mediante:

x = MyClass()

Por supuesto, el __init__()método puede tener argumentos para una mayor


flexibilidad. En ese caso, se pasan los argumentos dados al operador de
instanciación de clase __init__(). Por ejemplo,

>>>

>>> class Complex:


... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

9.3.3. Objetos de instancia

Ahora, ¿qué podemos hacer con los objetos de instancia? Las únicas operaciones
que entienden los objetos de instancia son las referencias a atributos. Hay dos tipos
de nombres de atributos válidos: atributos de datos y métodos.

los atributos de datos corresponden a “variables de instancia” en Smalltalk y a


“miembros de datos” en C ++. No es necesario declarar los atributos de los
datos; como las variables locales, surgen cuando se asignan por primera vez. Por
ejemplo, si xes la instancia de MyClasscreado anteriormente, el siguiente
fragmento de código imprimirá el valor 16, sin dejar rastro:

x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print(x.counter)
del x.counter

El otro tipo de referencia de atributo de instancia es un método . Un método es una


función que "pertenece a" un objeto. (En Python, el término método no es exclusivo
de las instancias de clase: otros tipos de objetos también pueden tener métodos.
Por ejemplo, los objetos de lista tienen métodos llamados agregar, insertar, eliminar,
ordenar, etc. Sin embargo, en la siguiente discusión, Usaremos el término método
exclusivamente para referirnos a métodos de objetos de instancia de clase, a menos
que se indique explícitamente lo contrario).

Los nombres de método válidos de un objeto de instancia dependen de su clase. Por


definición, todos los atributos de una clase que son objetos de función definen los
métodos correspondientes de sus instancias. Entonces, en nuestro ejemplo, x.fes
una referencia de método válida, ya que MyClass.fes una función, pero x.ino lo
es, ya MyClass.ique no lo es. Pero x.fno es lo mismo que MyClass.f: es
un objeto de método , no un objeto de función.

9.3.4. Objetos de método

Por lo general, se llama a un método justo después de enlazarlo:

x.f()

En el MyClassejemplo, esto devolverá la cadena . Sin embargo, no es necesario


llamar a un método de inmediato: es un objeto de método y se puede almacenar y
llamar en un momento posterior. Por ejemplo:'hello world'x.f

xf = x.f
while True:
print(xf())
seguirá imprimiendo hasta el final de los tiempos.hello world

¿Qué sucede exactamente cuando se llama a un método? Es posible que haya


notado que x.f()se llamó sin un argumento anterior, aunque la definición de
función para f()especificó un argumento. ¿Qué pasó con la
discusión? Seguramente Python genera una excepción cuando se llama a una
función que requiere un argumento sin ningún argumento, incluso si el argumento
no se usa realmente ...

En realidad, es posible que haya adivinado la respuesta: lo especial de los métodos


es que el objeto de instancia se pasa como el primer argumento de la función. En
nuestro ejemplo, la llamada x.f()es exactamente equivalente
a MyClass.f(x). En general, llamar a un método con una lista de n argumentos
equivale a llamar a la función correspondiente con una lista de argumentos que se
crea insertando el objeto de instancia del método antes del primer argumento.

Si aún no comprende cómo funcionan los métodos, una mirada a la implementación


tal vez pueda aclarar las cosas. Cuando se hace referencia a un atributo que no es
de datos de una instancia, se busca en la clase de la instancia. Si el nombre denota
un atributo de clase válido que es un objeto de función, se crea un objeto de método
empaquetando (apuntando a) el objeto de instancia y el objeto de función que se
acaban de encontrar juntos en un objeto abstracto: este es el objeto de
método. Cuando se llama al objeto de método con una lista de argumentos, se
construye una nueva lista de argumentos a partir del objeto de instancia y la lista de
argumentos, y se llama al objeto de función con esta nueva lista de argumentos.

9.3.5. Variables de clase e instancia

En términos generales, las variables de instancia son para datos únicos para cada
instancia y las variables de clase son para atributos y métodos compartidos por
todas las instancias de la clase:

class Dog:

kind = 'canine' # class variable shared by all


instances

def __init__(self, name):


self.name = name # instance variable unique to each
instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'canine'
>>> e.kind # shared by all dogs
'canine'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'

Como se discutió en Una palabra sobre nombres y objetos , los datos compartidos
pueden tener efectos posiblemente sorprendentes al
involucrar objetos mutables como listas y diccionarios. Por ejemplo, la lista
de trucos en el siguiente código no debe usarse como una variable de clase porque
todas las instancias de Dog compartirían una sola lista :

class Dog:

tricks = [] # mistaken use of a class variable

def __init__(self, name):


self.name = name

def add_trick(self, trick):


self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']

El diseño correcto de la clase debería usar una variable de instancia en su lugar:

class Dog:

def __init__(self, name):


self.name = name
self.tricks = [] # creates a new empty list for each
dog
def add_trick(self, trick):
self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

9.4. Comentarios aleatorios


Si el mismo nombre de atributo aparece tanto en una instancia como en una clase,
la búsqueda de atributos prioriza la instancia:

>>>

>>> class Warehouse:


purpose = 'storage'
region = 'west'

>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
storage east

Los atributos de datos pueden ser referenciados tanto por métodos como por
usuarios comunes ("clientes") de un objeto. En otras palabras, las clases no se
pueden utilizar para implementar tipos de datos abstractos puros. De hecho, nada
en Python hace posible imponer la ocultación de datos; todo se basa en una
convención. (Por otro lado, la implementación de Python, escrita en C, puede ocultar
completamente los detalles de la implementación y controlar el acceso a un objeto
si es necesario; esto puede ser utilizado por extensiones de Python escritas en C.)

Los clientes deben usar los atributos de los datos con cuidado; los clientes pueden
estropear las invariantes mantenidas por los métodos estampando sus atributos de
datos. Tenga en cuenta que los clientes pueden agregar atributos de datos propios
a un objeto de instancia sin afectar la validez de los métodos, siempre y cuando se
eviten los conflictos de nombres; nuevamente, una convención de nomenclatura
puede ahorrar muchos dolores de cabeza aquí.

No hay forma abreviada para hacer referencia a atributos de datos (¡u otros
métodos!) Desde dentro de los métodos. Encuentro que esto en realidad aumenta
la legibilidad de los métodos: no hay posibilidad de confundir las variables locales y
las variables de instancia al revisar un método.

A menudo, se llama al primer argumento de un método self. Esto no es más que


una convención: el nombre no selftiene ningún significado especial para
Python. Sin embargo, tenga en cuenta que al no seguir la convención, su código
puede ser menos legible para otros programadores de Python, y también es
concebible que se pueda escribir un programa de navegador de clase que se base
en dicha convención.

Cualquier objeto de función que sea un atributo de clase define un método para
instancias de esa clase. No es necesario que la definición de la función esté incluida
textualmente en la definición de la clase: también está bien asignar un objeto de
función a una variable local en la clase. Por ejemplo:

# Function defined outside the class


def f1(self, x, y):
return min(x, x+y)

class C:
f = f1

def g(self):
return 'hello world'

h = g

Ahora f, gy hson todos los atributos de la clase Cque se refieren a objetos de


función, y en consecuencia son todos los métodos de casos de C- hsiendo
exactamente equivalente a g. Tenga en cuenta que esta práctica generalmente solo
sirve para confundir al lector de un programa.

Los métodos pueden llamar a otros métodos utilizando atributos de método


del self argumento:
class Bag:
def __init__(self):
self.data = []

def add(self, x):


self.data.append(x)

def addtwice(self, x):


self.add(x)
self.add(x)

Los métodos pueden hacer referencia a nombres globales de la misma forma que
las funciones ordinarias. El alcance global asociado con un método es el módulo
que contiene su definición. (Una clase nunca se usa como un alcance global). Si
bien uno rara vez encuentra una buena razón para usar datos globales en un
método, hay muchos usos legítimos del alcance global: por un lado, las funciones y
módulos importados al alcance global pueden ser utilizado por métodos, así como
funciones y clases definidas en él. Por lo general, la clase que contiene el método
está definida en este ámbito global, y en la siguiente sección encontraremos algunas
buenas razones por las que un método querría hacer referencia a su propia clase.

Cada valor es un objeto y, por lo tanto, tiene una clase (también llamada su tipo ). Se
almacena como object.__class__.

9.5. Herencia
Por supuesto, una característica del lenguaje no sería digna del nombre "clase" sin
la herencia de soporte. La sintaxis de una definición de clase derivada se ve así:

class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>

El nombre BaseClassNamedebe definirse en un ámbito que contenga la definición


de clase derivada. En lugar de un nombre de clase base, también se permiten otras
expresiones arbitrarias. Esto puede resultar útil, por ejemplo, cuando la clase base
está definida en otro módulo:
class DerivedClassName(modname.BaseClassName):

La ejecución de una definición de clase derivada procede de la misma forma que


para una clase base. Cuando se construye el objeto de clase, se recuerda la clase
base. Esto se usa para resolver referencias de atributos: si un atributo solicitado no
se encuentra en la clase, la búsqueda procede a buscar en la clase base. Esta regla
se aplica de forma recursiva si la clase base en sí se deriva de alguna otra clase.

No hay nada especial en la creación de instancias de clases


derivadas: DerivedClassName()crea una nueva instancia de la clase. Las
referencias a métodos se resuelven de la siguiente manera: se busca el atributo de
clase correspondiente, descendiendo por la cadena de clases base si es necesario,
y la referencia al método es válida si esto produce un objeto de función.

Las clases derivadas pueden anular los métodos de sus clases base. Debido a que
los métodos no tienen privilegios especiales cuando llaman a otros métodos del
mismo objeto, un método de una clase base que llama a otro método definido en la
misma clase base puede terminar llamando a un método de una clase derivada que
lo anula. (Para programadores de C ++: todos los métodos en Python son
efectivos virtual).

De hecho, un método primordial en una clase derivada puede querer extender en


lugar de simplemente reemplazar el método de la clase base del mismo
nombre. Hay una forma sencilla de llamar directamente al método de la clase base:
simplemente llame . En ocasiones, esto también es útil para los clientes. (Tenga en
cuenta que esto solo funciona si se puede acceder a la clase base como en el ámbito
global).BaseClassName.methodname(self, arguments)BaseClassName

Python tiene dos funciones integradas que funcionan con herencia:

 Úselo isinstance()para verificar el tipo de una instancia: será solo si es o


alguna clase
derivada .isinstance(obj, int)Trueobj.__class__intint
 Se usa issubclass()para verificar la herencia de clases: es ya que es una
subclase de . Sin embargo, es ya no es una subclase
de .issubclass(bool, int)Trueboolintissubclass(float, int)F
alsefloatint
9.5.1. Herencia múltiple

Python también admite una forma de herencia múltiple. Una definición de clase con
varias clases base se ve así:

class DerivedClassName(Base1, Base2, Base3):


<statement-1>
.
.
.
<statement-N>

Para la mayoría de los propósitos, en los casos más simples, puede pensar en la
búsqueda de atributos heredados de una clase principal como profundidad primero,
de izquierda a derecha, sin buscar dos veces en la misma clase donde hay una
superposición en la jerarquía. Por lo tanto, si un atributo no se encuentra
en DerivedClassName, se busca en Base1, luego (recursivamente) en las clases
base de Base1, y si no se encontró allí, se buscó en Base2, y así sucesivamente.

De hecho, es un poco más complejo que eso; el orden de resolución del método
cambia dinámicamente para admitir llamadas cooperativas a super(). Este
enfoque se conoce en algunos otros lenguajes de herencia múltiple como call-next-
method y es más poderoso que la super llamada que se encuentra en los lenguajes
de herencia única.

El orden dinámico es necesario porque todos los casos de herencia múltiple exhiben
una o más relaciones de diamante (donde se puede acceder al menos a una de las
clases principales a través de múltiples rutas desde la clase más baja). Por ejemplo,
todas las clases heredan object, por lo que cualquier caso de herencia múltiple
proporciona más de una ruta para llegar object. Para evitar que se acceda a las
clases base más de una vez, el algoritmo dinámico linealiza el orden de búsqueda
de una manera que conserva el orden de izquierda a derecha especificado en cada
clase, que llama a cada padre solo una vez, y eso es monótono (lo que significa que
una clase puede ser subclasificada sin afectar el orden de precedencia de sus
padres). En conjunto, estas propiedades permiten diseñar clases confiables y
extensibles con herencia múltiple. Para obtener más detalles,
consulte https://www.python.org/download/releases/2.3/mro/ .
9.6. Variables privadas
Las variables de instancia "privadas" a las que no se puede acceder excepto desde
el interior de un objeto no existen en Python. Sin embargo, existe una convención
que es seguida por la mayoría del código Python: un nombre con un prefijo de
subrayado (por ejemplo _spam) debe tratarse como una parte no pública de la API
(ya sea una función, un método o un miembro de datos). Debe considerarse un
detalle de implementación y sujeto a cambios sin previo aviso.

Dado que existe un caso de uso válido para miembros de clase privada (es decir,
para evitar conflictos de nombres con nombres definidos por subclases), existe un
soporte limitado para dicho mecanismo, llamado manipulación de
nombres . Cualquier identificador del formulario __spam(al menos dos guiones bajos
iniciales, como máximo un guión bajo final) se reemplaza textualmente
con _classname__spam, donde classnamees el nombre de la clase actual con
guiones bajos iniciales eliminados. Esta alteración se realiza sin tener en cuenta la
posición sintáctica del identificador, siempre que ocurra dentro de la definición de
una clase.

La alteración de nombres es útil para permitir que las subclases anulen los métodos
sin interrumpir las llamadas a métodos intraclase. Por ejemplo:

class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)

def update(self, iterable):


for item in iterable:
self.items_list.append(item)

__update = update # private copy of original update()


method

class MappingSubclass(Mapping):

def update(self, keys, values):


# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
El ejemplo anterior funcionaría incluso si MappingSubclassse introdujera
un __updateidentificador, ya que se reemplaza por _Mapping__updateen
la Mappingclase y _MappingSubclass__updateen la MappingSubclass clase
respectivamente.

Tenga en cuenta que las reglas de manipulación están diseñadas principalmente


para evitar accidentes; todavía es posible acceder o modificar una variable que se
considera privada. Esto incluso puede resultar útil en circunstancias especiales,
como en el depurador.

Observe que el código pasado exec()o eval()no considera que el nombre de


clase de la clase que invoca sea la clase actual; esto es similar al efecto de
la globaldeclaración, cuyo efecto está igualmente restringido al código que se
compila en bytes. La misma restricción se aplica
a getattr(), setattr()y delattr(), al igual que cuando se hace
referencia __dict__directamente.

9,7. Probabilidades y finales


A veces es útil tener un tipo de datos similar al "registro" de Pascal o la "estructura"
de C, agrupando algunos elementos de datos con nombre. Una definición de clase
vacía funcionará bien:

class Employee:
pass

john = Employee() # Create an empty employee record

# Fill the fields of the record


john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

A un fragmento de código Python que espera un tipo de datos abstracto particular,


a menudo se le puede pasar una clase que emula los métodos de ese tipo de
datos. Por ejemplo, si tiene una función que formatea algunos datos de un objeto
de archivo, puede definir una clase con métodos read()y readline()obtener los
datos de un búfer de cadena y pasarlos como un argumento.
Los objetos de método de instancia también tienen atributos: m.__self__es el
objeto de instancia con el método m()y m.__func__es el objeto de función
correspondiente al método.

9,8. Iteradores
A estas alturas, probablemente haya notado que la mayoría de los objetos
contenedores se pueden recorrer usando una fordeclaración:

for element in [1, 2, 3]:


print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open("myfile.txt"):
print(line, end='')

Este estilo de acceso es claro, conciso y conveniente. El uso de iteradores impregna


y unifica Python. Detrás de escena, la fordeclaración invoca iter()el objeto
contenedor. La función devuelve un objeto iterador que define el
método __next__()que accede a los elementos del contenedor de uno en
uno. Cuando no hay más elementos, __next__()genera
una StopIterationexcepción que le dice al forciclo que termine. Puede llamar
al __next__()método usando la next()función incorporada; este ejemplo
muestra cómo funciona todo:

>>>

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
next(it)
StopIteration

Habiendo visto la mecánica detrás del protocolo de iterador, es fácil agregar


comportamiento de iterador a sus clases. Defina un __iter__()método que
devuelva un objeto con un __next__()método. Si la clase define __next__(),
entonces __iter__()puede devolver self:

class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)

def __iter__(self):
return self

def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
>>>

>>> rev = Reverse('spam')


>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
... print(char)
...
m
a
p
s

9,9. Generadores
Los generadores son una herramienta simple y poderosa para crear
iteradores. Están escritas como funciones regulares, pero usan layielddeclaración
siempre que quieren devolver datos. Cada vez quenext()se invoca, el generador
se reanuda donde lo dejó (recuerda todos los valores de datos y qué instrucción se
ejecutó por última vez). Un ejemplo muestra que los generadores pueden ser
trivialmente fáciles de crear:

def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>>

>>> for char in reverse('golf'):


... print(char)
...
f
l
o
g

Todo lo que se puede hacer con generadores también se puede hacer con
iteradores basados en clases como se describe en la sección anterior. Lo que hace
que los generadores sean tan compactos es que
los métodos __iter__()y __next__()se crean automáticamente.

Otra característica clave es que las variables locales y el estado de ejecución se


guardan automáticamente entre llamadas. Esto hizo que la función fuera más fácil
de escribir y mucho más clara que un enfoque que usa variables de instancia
como self.index y self.data.

Además de la creación automática de métodos y el estado del programa de


guardado, cuando los generadores terminan, se activan
automáticamente StopIteration. En combinación, estas características facilitan
la creación de iteradores sin más esfuerzo que escribir una función regular.

9.10. Expresiones generadoras


Algunos generadores simples pueden codificarse sucintamente como expresiones
usando una sintaxis similar a las listas por comprensión, pero con paréntesis en
lugar de corchetes. Estas expresiones están diseñadas para situaciones en las que
el generador se usa de inmediato mediante una función envolvente. Las
expresiones generadoras son más compactas pero menos versátiles que las
definiciones generadoras completas y tienden a ser más amigables con la memoria
que las listas por comprensión equivalentes.
Ejemplos:

>>>

>>> sum(i*i for i in range(10)) # sum of squares


285

>>> xvec = [10, 20, 30]


>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
260

>>> unique_words = set(word for line in page for word in


line.split())

>>> valedictorian = max((student.gpa, student.name) for student


in graduates)

>>> data = 'golf'


>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

Notas al pie

Excepto por una cosa. Los objetos de módulo tienen un atributo secreto de
sólo lectura llamado __dict__que devuelve el diccionario utilizado para
implementar el espacio de nombres del módulo; el nombre __dict__es un
atributo pero no un nombre global. Obviamente, usar esto viola la abstracción
de la implementación del espacio de nombres y debería restringirse a cosas
como depuradores post-mortem.

10. Breve recorrido por la biblioteca


estándar
10.1. Interfaz del sistema operativo
El osmódulo proporciona decenas de funciones para interactuar con el sistema
operativo:
>>>

>>> import os
>>> os.getcwd() # Return the current working directory
'C:\\Python38'
>>> os.chdir('/server/accesslogs') # Change current working
directory
>>> os.system('mkdir today') # Run the command mkdir in the
system shell
0

Asegúrese de usar el estilo en lugar de . Esto evitará que se ensombrezca la función


incorporada que opera de manera muy
diferente.import osfrom os import *os.open()open()

Las funciones incorporadas dir()y help()son útiles como ayudas interactivas


para trabajar con módulos grandes como os:

>>>

>>> import os
>>> dir(os)
<returns a list of all module functions>
>>> help(os)
<returns an extensive manual page created from the module's
docstrings>

Para las tareas diarias de administración de archivos y directorios, el shutilmódulo


proporciona una interfaz de nivel superior que es más fácil de usar:

>>>

>>> import shutil


>>> shutil.copyfile('data.db', 'archive.db')
'archive.db'
>>> shutil.move('/build/executables', 'installdir')
'installdir'

10.2. Comodines de archivo


El globmódulo proporciona una función para hacer listas de archivos a partir de
búsquedas con comodines de directorio:
>>>

>>> import glob


>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']

10.3. Argumentos de la línea de comandos


Los scripts de utilidades comunes a menudo necesitan procesar argumentos de
línea de comando. Estos argumentos se almacenan en el atributo argvsys del
módulo como una lista. Por ejemplo, la siguiente salida resulta de la ejecución en la
línea de comando:python demo.py one two three

>>>

>>> import sys


>>> print(sys.argv)
['demo.py', 'one', 'two', 'three']

El argparsemódulo proporciona un mecanismo más sofisticado para procesar


argumentos de línea de comandos. El siguiente script extrae uno o más nombres
de archivo y un número opcional de líneas que se mostrarán:

import argparse

parser = argparse.ArgumentParser(prog = 'top',


description = 'Show top lines from each file')
parser.add_argument('filenames', nargs='+')
parser.add_argument('-l', '--lines', type=int, default=10)
args = parser.parse_args()
print(args)

Cuando se ejecuta en la línea de comando con , el script


establece en y en .python top.py --
lines=5 alpha.txt beta.txtargs.lines5args.filenames['alpha.txt',
'beta.txt']
10.4. Redirección de salida de error y
terminación del programa
El sysmódulo también tiene atributos para stdin , stdout y stderr . Este último es útil
para emitir advertencias y mensajes de error para hacerlos visibles incluso
cuando se ha redirigido stdout :

>>>

>>> sys.stderr.write('Warning, log file not found starting a new


one\n')
Warning, log file not found starting a new one

La forma más directa de terminar un script es usar sys.exit().

10.5. Coincidencia de patrones de cadena


El remódulo proporciona herramientas de expresión regular para el procesamiento
avanzado de cadenas. Para combinaciones y manipulaciones complejas, las
expresiones regulares ofrecen soluciones sucintas y optimizadas:

>>>

>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
'cat in the hat'

Cuando solo se necesitan capacidades simples, se prefieren los métodos de cadena


porque son más fáciles de leer y depurar:

>>>

>>> 'tea for too'.replace('too', 'two')


'tea for two'
10.6. Matemáticas
El mathmódulo da acceso a las funciones de la biblioteca C subyacente para
matemáticas de punto flotante:

>>>

>>> import math


>>> math.cos(math.pi / 4)
0.70710678118654757
>>> math.log(1024, 2)
10.0

El randommódulo proporciona herramientas para realizar selecciones aleatorias:

>>>

>>> import random


>>> random.choice(['apple', 'pear', 'banana'])
'apple'
>>> random.sample(range(100), 10) # sampling without
replacement
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random() # random float
0.17970987693706186
>>> random.randrange(6) # random integer chosen from range(6)
4

El statisticsmódulo calcula propiedades estadísticas básicas (la media,


mediana, varianza, etc.) de datos numéricos:

>>>

>>> import statistics


>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> statistics.mean(data)
1.6071428571428572
>>> statistics.median(data)
1.25
>>> statistics.variance(data)
1.3720238095238095
El proyecto SciPy < https://scipy.org > tiene muchos otros módulos para cálculos
numéricos.

10,7. Acceso a Internet


Hay varios módulos para acceder a Internet y procesar protocolos de Internet. Dos
de los más simples son urllib.requestpara recuperar datos de URL
y smtplibpara enviar correo:

>>>

>>> from urllib.request import urlopen


>>> with urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl')
as response:
... for line in response:
... line = line.decode('utf-8') # Decoding the binary
data to text.
... if 'EST' in line or 'EDT' in line: # look for
Eastern Time
... print(line)

<BR>Nov. 25, 09:43:32 PM EST

>>> import smtplib


>>> server = smtplib.SMTP('localhost')
>>> server.sendmail('[email protected]',
'[email protected]',
... """To: [email protected]
... From: [email protected]
...
... Beware the Ides of March.
... """)
>>> server.quit()

(Tenga en cuenta que el segundo ejemplo necesita un servidor de correo que se


ejecute en localhost).

10,8. Fechas y horas


El datetimemódulo proporciona clases para manipular fechas y horas de forma
sencilla y compleja. Si bien se admite la aritmética de fecha y hora, el enfoque de la
implementación está en la extracción eficiente de miembros para el formato y la
manipulación de salida. El módulo también admite objetos que son conscientes de
la zona horaria.

>>>

>>> # dates are easily constructed and formatted


>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2003, 12, 2)
>>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of
%B.")
'12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'

>>> # dates support calendar arithmetic


>>> birthday = date(1964, 7, 31)
>>> age = now - birthday
>>> age.days
14368

10,9. Compresión de datos


Formatos de archivo de datos común y de compresión están directamente
soportados por módulos que incluyen: zlib, gzip, bz2, lzma, zipfiley tarfile.

>>>

>>> import zlib


>>> s = b'witch which has which witches wrist watch'
>>> len(s)
41
>>> t = zlib.compress(s)
>>> len(t)
37
>>> zlib.decompress(t)
b'witch which has which witches wrist watch'
>>> zlib.crc32(s)
226805979

10.10. Medición del desempeño


Algunos usuarios de Python desarrollan un profundo interés en conocer el
rendimiento relativo de diferentes enfoques para el mismo problema. Python
proporciona una herramienta de medición que responde a esas preguntas de
inmediato.

Por ejemplo, puede resultar tentador utilizar la función de empaquetado y


desempaquetado de tuplas en lugar del enfoque tradicional para intercambiar
argumentos. El timeit módulo demuestra rápidamente una ventaja de rendimiento
modesta:

>>>

>>> from timeit import Timer


>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.57535828626024577
>>> Timer('a,b = b,a', 'a=1; b=2').timeit()
0.54962537085770791

En contraste con timeitel fino nivel de granularidad,


los módulos profiley pstatsproporcionan herramientas para identificar
secciones críticas en el tiempo en bloques de código más grandes.

10.11. Control de calidad


Un enfoque para desarrollar software de alta calidad es escribir pruebas para cada
función a medida que se desarrolla y ejecutar esas pruebas con frecuencia durante
el proceso de desarrollo.

El doctestmódulo proporciona una herramienta para escanear un módulo y validar


pruebas integradas en las cadenas de documentación de un programa. La
construcción de la prueba es tan simple como cortar y pegar una llamada típica junto
con sus resultados en la cadena de documentos. Esto mejora la documentación al
proporcionar al usuario un ejemplo y permite que el módulo doctest se asegure de
que el código se mantenga fiel a la documentación:

def average(values):
"""Computes the arithmetic mean of a list of numbers.

>>> print(average([20, 30, 70]))


40.0
"""
return sum(values) / len(values)
import doctest
doctest.testmod() # automatically validate the embedded tests

El unittestmódulo no es tan sencillo como el doctestmódulo, pero permite


mantener un conjunto más completo de pruebas en un archivo separado:

import unittest

class TestStatisticalFunctions(unittest.TestCase):

def test_average(self):
self.assertEqual(average([20, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
with self.assertRaises(ZeroDivisionError):
average([])
with self.assertRaises(TypeError):
average(20, 30, 70)

unittest.main() # Calling from the command line invokes all


tests

10.12. Pilas incluidas


Python tiene una filosofía de "baterías incluidas". Esto se ve mejor a través de las
capacidades sofisticadas y robustas de sus paquetes más grandes. Por ejemplo:

 Los módulos xmlrpc.clienty xmlrpc.serverhacen que la


implementación de llamadas a procedimientos remotos sea una tarea casi
trivial. A pesar de los nombres de los módulos, no se necesita ningún
conocimiento directo o manejo de XML.
 El emailpaquete es una biblioteca para administrar mensajes de correo
electrónico, incluidos MIME y otrosDocumentos de mensajes basados
en RFC 2822 . A diferenciasmtplib, y poplibque en realidad enviar y recibir
mensajes, el paquete de correo electrónico tiene un completo conjunto de
herramientas para la construcción o la decodificación de estructuras
complejas de mensajes (incluidos los archivos adjuntos) y para la aplicación
de codificación de Internet y protocolos de cabecera.
 El jsonpaquete proporciona un soporte sólido para analizar este popular
formato de intercambio de datos. El csvmódulo admite la lectura y escritura
directa de archivos en formato de valores separados por comas,
comúnmente compatible con bases de datos y hojas de cálculo. El
procesamiento de XML es compatible con
los paquetes xml.etree.ElementTree, xml.domy xml.sax. Juntos, estos
módulos y paquetes simplifican enormemente el intercambio de datos entre
las aplicaciones de Python y otras herramientas.
 El sqlite3módulo es un contenedor para la biblioteca de la base de datos
SQLite, que proporciona una base de datos persistente que se puede
actualizar y acceder utilizando una sintaxis SQL ligeramente no estándar.
 La internacionalización es apoyada por un número de módulos que
incluyen gettext, localey el codecspaquete.

11. Breve recorrido por la biblioteca


estándar - Parte II
Este segundo recorrido cubre módulos más avanzados que apoyan las necesidades
de programación profesional. Estos módulos rara vez ocurren en pequeños scripts.

11.1. Formato de salida


El reprlibmódulo proporciona una versión repr()personalizada para pantallas
abreviadas de contenedores grandes o profundamente anidados:

>>>

>>> import reprlib


>>> reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'a', 'c', 'd', 'e', 'f', 'g', ...}"

El pprintmódulo ofrece un control más sofisticado sobre la impresión de objetos


integrados y definidos por el usuario de una manera que sea legible por el
intérprete. Cuando el resultado es más largo que una línea, la "impresora bonita"
agrega saltos de línea y sangría para revelar más claramente la estructura de los
datos:

>>>

>>> import pprint


>>> t = [[[['black', 'cyan'], 'white', ['green', 'red']],
[['magenta',
... 'yellow'], 'blue']]]
...
>>> pprint.pprint(t, width=30)
[[[['black', 'cyan'],
'white',
['green', 'red']],
[['magenta', 'yellow'],
'blue']]]

El textwrapmódulo formatea párrafos de texto para que se ajusten a un ancho de


pantalla determinado:

>>>

>>> import textwrap


>>> doc = """The wrap() method is just like fill() except that
it returns
... a list of strings instead of one big string with newlines to
separate
... the wrapped lines."""
...
>>> print(textwrap.fill(doc, width=40))
The wrap() method is just like fill()
except that it returns a list of strings
instead of one big string with newlines
to separate the wrapped lines.

El localemódulo accede a una base de datos de formatos de datos específicos de


la cultura. El atributo de agrupación de la función de formato de la configuración
regional proporciona una forma directa de formatear números con separadores de
grupo:

>>>

>>> import locale


>>> locale.setlocale(locale.LC_ALL, 'English_United
States.1252')
'English_United States.1252'
>>> conv = locale.localeconv() # get a mapping of
conventions
>>> x = 1234567.8
>>> locale.format("%d", x, grouping=True)
'1,234,567'
>>> locale.format_string("%s%.*f", (conv['currency_symbol'],
... conv['frac_digits'], x), grouping=True)
'$1,234,567.80'

11.2. Plantillas
El stringmódulo incluye una Templateclase versátil con una sintaxis simplificada
adecuada para que los usuarios finales la editen. Esto permite a los usuarios
personalizar sus aplicaciones sin tener que modificar la aplicación.

El formato utiliza nombres de marcadores de posición formados por $identificadores


de Python válidos (caracteres alfanuméricos y guiones bajos). Rodear el marcador
de posición con llaves permite que sea seguido por más letras alfanuméricas sin
espacios intermedios. La escritura $$crea un solo escape $:

>>>

>>> from string import Template


>>> t = Template('${village}folk send $$10 to $cause.')
>>> t.substitute(village='Nottingham', cause='the ditch fund')
'Nottinghamfolk send $10 to the ditch fund.'

El substitute()método genera un KeyErrorcuando no se proporciona un


marcador de posición en un diccionario o un argumento de palabra clave. Para
aplicaciones de estilo de combinación de correo, los datos proporcionados por el
usuario pueden estar incompletos y el safe_substitute()método puede ser más
apropiado; dejará los marcadores de posición sin cambios si faltan datos:

>>>

>>> t = Template('Return the $item to $owner.')


>>> d = dict(item='unladen swallow')
>>> t.substitute(d)
Traceback (most recent call last):
...
KeyError: 'owner'
>>> t.safe_substitute(d)
'Return the unladen swallow to $owner.'

Las subclases de plantilla pueden especificar un delimitador personalizado. Por


ejemplo, una utilidad de cambio de nombre por lotes para un navegador de fotos
puede optar por utilizar signos de porcentaje para marcadores de posición, como la
fecha actual, el número de secuencia de la imagen o el formato de archivo:
>>>

>>> import time, os.path


>>> photofiles = ['img_1074.jpg', 'img_1076.jpg',
'img_1077.jpg']
>>> class BatchRename(Template):
... delimiter = '%'
>>> fmt = input('Enter rename style (%d-date %n-seqnum %f-
format): ')
Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f

>>> t = BatchRename(fmt)
>>> date = time.strftime('%d%b%y')
>>> for i, filename in enumerate(photofiles):
... base, ext = os.path.splitext(filename)
... newname = t.substitute(d=date, n=i, f=ext)
... print('{0} --> {1}'.format(filename, newname))

img_1074.jpg --> Ashley_0.jpg


img_1076.jpg --> Ashley_1.jpg
img_1077.jpg --> Ashley_2.jpg

Otra aplicación para crear plantillas es separar la lógica del programa de los detalles
de múltiples formatos de salida. Esto hace posible la sustitución de plantillas
personalizadas por archivos XML, informes de texto sin formato e informes web
HTML.

11.3. Trabajar con diseños de registros de datos


binarios
El structmódulo proporciona pack()y unpack()funciones para trabajar con
formatos de registro binario de longitud variable. El siguiente ejemplo muestra cómo
recorrer la información del encabezado en un archivo ZIP sin usar
el zipfilemódulo. Empaquetar códigos "H"y "I"representar números sin signo
de dos y cuatro bytes respectivamente. El "<"indica que son de tamaño estándar y
están en orden de bytes little-endian:

import struct

with open('myfile.zip', 'rb') as f:


data = f.read()
start = 0
for i in range(3): # show the first 3 file
headers
start += 14
fields = struct.unpack('<IIIHH', data[start:start+16])
crc32, comp_size, uncomp_size, filenamesize, extra_size =
fields

start += 16
filename = data[start:start+filenamesize]
start += filenamesize
extra = data[start:start+extra_size]
print(filename, hex(crc32), comp_size, uncomp_size)

start += extra_size + comp_size # skip to the next


header

11.4. Multi-hilo
El enhebrado es una técnica para desacoplar tareas que no dependen
secuencialmente. Los subprocesos se pueden utilizar para mejorar la capacidad de
respuesta de las aplicaciones que aceptan la entrada del usuario mientras otras
tareas se ejecutan en segundo plano. Un caso de uso relacionado es ejecutar E / S
en paralelo con cálculos en otro hilo.

El siguiente código muestra cómo el threadingmódulo de alto nivel puede ejecutar


tareas en segundo plano mientras el programa principal continúa ejecutándose:

import threading, zipfile

class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile

def run(self):
f = zipfile.ZipFile(self.outfile, 'w',
zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print('Finished background zip of:', self.infile)

background = AsyncZip('mydata.txt', 'myarchive.zip')


background.start()
print('The main program continues to run in foreground.')

background.join() # Wait for the background task to finish


print('Main program waited until background was done.')

El principal desafío de las aplicaciones multiproceso es coordinar los subprocesos


que comparten datos u otros recursos. Con ese fin, el módulo de subprocesamiento
proporciona una serie de primitivas de sincronización que incluyen bloqueos,
eventos, variables de condición y semáforos.

Si bien esas herramientas son poderosas, los errores de diseño menores pueden
provocar problemas que son difíciles de reproducir. Entonces, el enfoque preferido
para la coordinación de tareas es concentrar todo el acceso a un recurso en un solo
hilo y luego usar el queuemódulo para alimentar ese hilo con solicitudes de otros
hilos. Las aplicaciones que utilizan Queueobjetos para la comunicación y
coordinación entre subprocesos son más fáciles de diseñar, más legibles y más
fiables.

11,5. Registro
El loggingmódulo ofrece un sistema de registro flexible y con todas las
funciones. En su forma más simple, los mensajes de registro se envían a un archivo
oa sys.stderr:

import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found',
'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')

Esto produce la siguiente salida:

WARNING:root:Warning:config file server.conf not found


ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down
De forma predeterminada, los mensajes informativos y de depuración se suprimen
y la salida se envía al error estándar. Otras opciones de salida incluyen el
enrutamiento de mensajes a través de correo electrónico, datagramas, sockets o un
servidor HTTP. Los nuevos filtros pueden seleccionar diferentes enrutamiento
basado en la prioridad del mensaje: DEBUG, INFO, WARNING, ERROR, y CRITICAL.

El sistema de registro se puede configurar directamente desde Python o se puede


cargar desde un archivo de configuración editable por el usuario para un registro
personalizado sin alterar la aplicación.

11.6. Referencias débiles


Python realiza una gestión automática de la memoria (recuento de referencias para
la mayoría de los objetos y recolección de basura para eliminar ciclos). La memoria
se libera poco después de que se haya eliminado la última referencia.

Este enfoque funciona bien para la mayoría de las aplicaciones, pero en ocasiones
es necesario realizar un seguimiento de los objetos siempre que estén siendo
utilizados por otra persona. Desafortunadamente, solo rastrearlos crea una
referencia que los hace permanentes. El weakrefmódulo proporciona herramientas
para rastrear objetos sin crear una referencia. Cuando el objeto ya no es necesario,
se elimina automáticamente de una tabla de referencia débil y se activa una
devolución de llamada para los objetos de referencia débil. Las aplicaciones típicas
incluyen el almacenamiento en caché de objetos que son costosos de crear:

>>>

>>> import weakref, gc


>>> class A:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
...
>>> a = A(10) # create a reference
>>> d = weakref.WeakValueDictionary()
>>> d['primary'] = a # does not create a reference
>>> d['primary'] # fetch the object if it is
still alive
10
>>> del a # remove the one reference
>>> gc.collect() # run garbage collection right
away
0
>>> d['primary'] # entry was automatically
removed
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
d['primary'] # entry was automatically
removed
File "C:/python38/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'

11,7. Herramientas para trabajar con listas


Se pueden satisfacer muchas necesidades de estructura de datos con el tipo de
lista integrado. Sin embargo, a veces existe la necesidad de implementaciones
alternativas con diferentes compensaciones de desempeño.

El arraymódulo proporciona un array()objeto que es como una lista que


almacena solo datos homogéneos y los almacena de forma más compacta. El
siguiente ejemplo muestra una matriz de números almacenados como números
binarios sin signo de dos bytes (código de tipo "H") en lugar de los habituales 16
bytes por entrada para las listas regulares de objetos int de Python:

>>>

>>> from array import array


>>> a = array('H', [4000, 10, 700, 22222])
>>> sum(a)
26932
>>> a[1:3]
array('H', [10, 700])

El collectionsmódulo proporciona un deque()objeto que es como una lista con


anexos y estallidos más rápidos desde el lado izquierdo pero búsquedas más lentas
en el medio. Estos objetos son adecuados para implementar colas y búsquedas de
primer árbol en amplitud:

>>>

>>> from collections import deque


>>> d = deque(["task1", "task2", "task3"])
>>> d.append("task4")
>>> print("Handling", d.popleft())
Handling task1
unsearched = deque([starting_node])
def breadth_first_search(unsearched):
node = unsearched.popleft()
for m in gen_moves(node):
if is_goal(m):
return m
unsearched.append(m)

Además de las implementaciones de listas alternativas, la biblioteca también ofrece


otras herramientas como el bisectmódulo con funciones para manipular listas
ordenadas:

>>>

>>> import bisect


>>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500,
'python')]
>>> bisect.insort(scores, (300, 'ruby'))
>>> scores
[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500,
'python')]

El heapqmódulo proporciona funciones para implementar montones basados en


listas regulares. La entrada de menor valor siempre se mantiene en la posición
cero. Esto es útil para aplicaciones que acceden repetidamente al elemento más
pequeño pero no quieren ejecutar una clasificación de lista completa:

>>>

>>> from heapq import heapify, heappop, heappush


>>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
>>> heapify(data) # rearrange the list into
heap order
>>> heappush(data, -5) # add a new entry
>>> [heappop(data) for i in range(3)] # fetch the three
smallest entries
[-5, 0, 1]
11,8. Aritmética de coma flotante decimal
El decimalmódulo ofrece un Decimaltipo de datos para aritmética de coma flotante
decimal. En comparación con la float implementación incorporada del punto
flotante binario, la clase es especialmente útil para

 aplicaciones financieras y otros usos que requieren una representación


decimal exacta,
 control sobre la precisión,
 control sobre el redondeo para cumplir con los requisitos legales o
reglamentarios,
 seguimiento de lugares decimales significativos, o
 aplicaciones en las que el usuario espera que los resultados coincidan con
los cálculos hechos a mano.

Por ejemplo, el cálculo de un impuesto del 5% sobre un cargo telefónico de 70


centavos da resultados diferentes en coma flotante decimal y coma flotante
binaria. La diferencia se vuelve significativa si los resultados se redondean al
centavo más cercano:

>>>

>>> from decimal import *


>>> round(Decimal('0.70') * Decimal('1.05'), 2)
Decimal('0.74')
>>> round(.70 * 1.05, 2)
0.73

El Decimalresultado mantiene un cero final, lo que infiere automáticamente un


significado de cuatro lugares a partir de multiplicandos con un significado de dos
lugares. Decimal reproduce las matemáticas a mano y evita problemas que pueden
surgir cuando el punto flotante binario no puede representar exactamente
cantidades decimales.

La representación exacta permite a la Decimalclase realizar cálculos de módulo y


pruebas de igualdad que no son adecuadas para coma flotante binaria:

>>>

>>> Decimal('1.00') % Decimal('.10')


Decimal('0.00')
>>> 1.00 % 0.10
0.09999999999999995

>>> sum([Decimal('0.1')]*10) == Decimal('1.0')


True
>>> sum([0.1]*10) == 1.0
False

El decimalmódulo proporciona aritmética con tanta precisión como sea necesario:

>>>

>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')

12. Paquetes y entornos virtuales


12.1. Introducción
Las aplicaciones de Python suelen utilizar paquetes y módulos que no forman parte
de la biblioteca estándar. Las aplicaciones a veces necesitarán una versión
específica de una biblioteca, porque la aplicación puede requerir que se haya
corregido un error en particular o la aplicación puede escribirse usando una versión
obsoleta de la interfaz de la biblioteca.

Esto significa que puede que no sea posible que una instalación de Python cumpla
con los requisitos de cada aplicación. Si la aplicación A necesita la versión 1.0 de
un módulo en particular, pero la aplicación B necesita la versión 2.0, entonces los
requisitos están en conflicto y la instalación de la versión 1.0 o 2.0 dejará una
aplicación incapaz de ejecutarse.

La solución para este problema es crear un entorno virtual , un árbol de directorios


autónomo que contiene una instalación de Python para una versión particular de
Python, además de varios paquetes adicionales.

Entonces, diferentes aplicaciones pueden utilizar diferentes entornos virtuales. Para


resolver el ejemplo anterior de requisitos en conflicto, la aplicación A puede tener
su propio entorno virtual con la versión 1.0 instalada, mientras que la aplicación B
tiene otro entorno virtual con la versión 2.0. Si la aplicación B requiere que se
actualice una biblioteca a la versión 3.0, esto no afectará el entorno de la aplicación
A.

12.2. Creación de entornos virtuales


El módulo utilizado para crear y gestionar entornos virtuales se
llama venv. venvnormalmente instalará la versión más reciente de Python que tenga
disponible. Si tiene varias versiones de Python en su sistema, puede seleccionar
una versión específica de Python ejecutando python3o la versión que desee.

Para crear un entorno virtual, decida el directorio donde desea colocarlo y ejecute
el venvmódulo como un script con la ruta del directorio:

python3 -m venv tutorial-env

Esto creará el tutorial-envdirectorio si no existe, y también creará directorios dentro


de él que contienen una copia del intérprete de Python, la biblioteca estándar y
varios archivos de soporte.

Una ubicación de directorio común para un entorno virtual es .venv. Este nombre
mantiene el directorio normalmente oculto en su shell y, por lo tanto, fuera del
camino, mientras le da un nombre que explica por qué existe el directorio. También
evita conflictos con .envlos archivos de definición de variables de entorno que
admiten algunas herramientas.

Una vez que haya creado un entorno virtual, puede activarlo.

En Windows, ejecute:

tutorial-env\Scripts\activate.bat

En Unix o MacOS, ejecute:

source tutorial-env/bin/activate

(Este script está escrito para el shell bash. Si usa csh o fish shells,
hay scripts alternativos activate.cshy activate.fishque debe usar en su lugar).
La activación del entorno virtual cambiará el indicador de su shell para mostrar qué
entorno virtual está utilizando y modificará el entorno para que la ejecución pythonle
proporcione esa versión e instalación en particular de Python. Por ejemplo:

$ source ~/envs/tutorial-env/bin/activate
(tutorial-env) $ python
Python 3.5.1 (default, May 6 2016, 10:59:36)
...
>>> import sys
>>> sys.path
['', '/usr/local/lib/python35.zip', ...,
'~/envs/tutorial-env/lib/python3.5/site-packages']
>>>

12.3. Administrar paquetes con pip


Puede instalar, actualizar y eliminar paquetes utilizando un programa
llamado pip . De forma predeterminada, pipse instalarán los paquetes del índice de
paquetes de Python, < https://pypi.org >. Puede explorar el índice de paquetes de
Python yendo a él en su navegador web, o puede usar pipla función de búsqueda
limitada:

(tutorial-env) $ pip search astronomy


skyfield - Elegant astronomy for Python
gary - Galactic astronomy and gravitational
dynamics.
novas - The United States Naval Observatory
NOVAS astronomy library
astroobs - Provides astronomy ephemeris to plan
telescope observations
PyAstronomy - A collection of astronomy related tools
for Python.
...

piptiene varios subcomandos: "buscar", "instalar", "desinstalar", "congelar", etc.


(Consulte la guía de instalación de módulos Python para obtener la documentación
completa pip).

Puede instalar la última versión de un paquete especificando el nombre de un


paquete:

(tutorial-env) $ pip install novas


Collecting novas
Downloading novas-3.1.1.3.tar.gz (136kB)
Installing collected packages: novas
Running setup.py install for novas
Successfully installed novas-3.1.1.3

También puede instalar una versión específica de un paquete dando el nombre del
paquete seguido de ==y el número de versión:

(tutorial-env) $ pip install requests==2.6.0


Collecting requests==2.6.0
Using cached requests-2.6.0-py2.py3-none-any.whl
Installing collected packages: requests
Successfully installed requests-2.6.0

Si vuelve a ejecutar este comando, pipnotará que la versión solicitada ya está


instalada y no hará nada. Puede proporcionar un número de versión diferente para
obtener esa versión, o puede ejecutar para actualizar el paquete a la última
versión:pip install --upgrade

(tutorial-env) $ pip install --upgrade requests


Collecting requests
Installing collected packages: requests
Found existing installation: requests 2.6.0
Uninstalling requests-2.6.0:
Successfully uninstalled requests-2.6.0
Successfully installed requests-2.7.0

pip uninstall seguido de uno o más nombres de paquetes eliminará los paquetes del
entorno virtual.

pip show mostrará información sobre un paquete en particular:

(tutorial-env) $ pip show requests


---
Metadata-Version: 2.0
Name: requests
Version: 2.7.0
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: [email protected]
License: Apache 2.0
Location: /Users/akuchling/envs/tutorial-env/lib/python3.4/site-
packages
Requires:

pip list mostrará todos los paquetes instalados en el entorno virtual:

(tutorial-env) $ pip list


novas (3.1.1.3)
numpy (1.9.2)
pip (7.0.3)
requests (2.7.0)
setuptools (16.0)

pip freezeproducirá una lista similar de los paquetes instalados, pero la salida usa el
formato esperado. Una convención común es poner esta lista en
un archivo:pip installrequirements.txt

(tutorial-env) $ pip freeze > requirements.txt


(tutorial-env) $ cat requirements.txt
novas==3.1.1.3
numpy==1.9.2
requests==2.7.0

A requirements.txtcontinuación, puede comprometerse con el control de versiones y


enviarse como parte de una aplicación. Luego, los usuarios pueden instalar todos
los paquetes necesarios con :install -r

(tutorial-env) $ pip install -r requirements.txt


Collecting novas==3.1.1.3 (from -r requirements.txt (line 1))
...
Collecting numpy==1.9.2 (from -r requirements.txt (line 2))
...
Collecting requests==2.7.0 (from -r requirements.txt (line 3))
...
Installing collected packages: novas, numpy, requests
Running setup.py install for novas
Successfully installed novas-3.1.1.3 numpy-1.9.2 requests-2.7.0

piptiene muchas más opciones. Consulte la guía Instalación de módulos de


Python para obtener la documentación completa de pip. Cuando haya escrito un
paquete y desee que esté disponible en el índice de paquetes de Python, consulte
la guía Distribución de módulos de Python .

13. ¿Y ahora qué?


La lectura de este tutorial probablemente haya reforzado su interés en el uso de
Python: debería estar ansioso por aplicar Python para resolver sus problemas del
mundo real. ¿Dónde deberías ir para aprender más?

Este tutorial es parte del conjunto de documentación de Python. Algunos otros


documentos del conjunto son:

 La biblioteca estándar de Python :

Debe examinar este manual, que ofrece material de referencia completo


(aunque conciso) sobre los tipos, funciones y módulos de la biblioteca
estándar. La distribución estándar de Python incluye mucho código
adicional. Hay módulos para leer buzones de correo Unix, recuperar
documentos a través de HTTP, generar números aleatorios, analizar
opciones de línea de comandos, escribir programas CGI, comprimir datos y
muchas otras tareas. Hojear la Referencia de la biblioteca le dará una idea
de lo que está disponible.
 Instalación de módulos de Python explica cómo instalar módulos adicionales
escritos por otros usuarios de Python.
 Referencia del lenguaje Python : una explicación detallada de la sintaxis y la
semántica de Python. Es una lectura pesada, pero es útil como una guía
completa del idioma en sí.

Más recursos de Python:

 https://www.python.org : el principal sitio web de Python. Contiene código,


documentación y referencias a páginas de la Web relacionadas con
Python. Este sitio web se refleja en varios lugares del mundo, como Europa,
Japón y Australia; un espejo puede ser más rápido que el sitio principal,
dependiendo de su ubicación geográfica.
 https://docs.python.org : acceso rápido a la documentación de Python.
 https://pypi.org : El índice de paquetes de Python, anteriormente también
apodado Cheese Shop 1 , es un índice de módulos de Python creados por
usuarios que están disponibles para descargar. Una vez que comience a
publicar el código, puede registrarlo aquí para que otros puedan encontrarlo.
 https://code.activestate.com/recipes/langs/python/ : El libro de cocina de
Python es una colección considerable de ejemplos de código, módulos más
grandes y scripts útiles. Las contribuciones particularmente notables se
recopilan en un libro también titulado Python Cookbook (O'Reilly &
Associates, ISBN 0-596-00797-3).
 http://www.pyvideo.org recopila enlaces a videos relacionados con Python de
conferencias y reuniones de grupos de usuarios.
 https://scipy.org : El proyecto Scientific Python incluye módulos para cálculos
y manipulaciones de matrices rápidas, además de una serie de paquetes
para cosas como álgebra lineal, transformadas de Fourier, solucionadores no
lineales, distribuciones de números aleatorios, análisis estadístico y
similares.

Para preguntas relacionadas con Python e informes de problemas, puede publicar


en el grupo de noticias comp.lang.python , o enviarlos a la lista de correo en python-
list @ python . org . El grupo de noticias y la lista de correo tienen una puerta de
enlace, por lo que los mensajes enviados a uno se reenviarán automáticamente al
otro. Hay cientos de publicaciones al día, haciendo (y respondiendo) preguntas,
sugiriendo nuevas funciones y anunciando nuevos módulos. Los archivos de la lista
de correo están disponibles en https://mail.python.org/pipermail/ .

Antes de publicar, asegúrese de consultar la lista de preguntas frecuentes (también


llamada FAQ). Las preguntas frecuentes responden a muchas de las preguntas que
surgen una y otra vez, y es posible que ya contengan la solución para su problema.

Notas al pie

“Cheese Shop” es un boceto de Monty Python: un cliente entra en una tienda


de quesos, pero cualquier queso que pide, el empleado dice que falta.

14. Edición de entrada interactiva y


sustitución de historial
Algunas versiones del intérprete de Python admiten la edición de la línea de entrada
actual y la sustitución del historial, de forma similar a las instalaciones que se
encuentran en el shell Korn y el shell GNU Bash. Esto se implementa utilizando
la biblioteca GNU Readline , que admite varios estilos de edición. Esta biblioteca
tiene su propia documentación que no duplicaremos aquí.

14.1. Finalización de pestañas y edición del


historial
La finalización de nombres de módulos y variables se habilita automáticamente al
inicio del intérprete de modo que la Tabtecla invoca la función de
finalización; examina los nombres de las declaraciones de Python, las variables
locales actuales y los nombres de los módulos disponibles. Para expresiones
punteadas como string.a, evaluará la expresión hasta la final '.'y luego sugerirá
terminaciones a partir de los atributos del objeto resultante. Tenga en cuenta que
esto puede ejecutar código definido por la aplicación si un objeto con
un __getattr__()método es parte de la expresión. La configuración
predeterminada también guarda su historial en un archivo
nombrado .python_historyen su directorio de usuario. El historial volverá a estar
disponible durante la próxima sesión de interpretación interactiva.

14.2. Alternativas al intérprete interactivo


Esta facilidad es un gran paso adelante en comparación con versiones anteriores
del intérprete; sin embargo, quedan algunos deseos: sería bueno si se sugiriera la
sangría adecuada en las líneas de continuación (el analizador sabe si se requiere
un token de sangría a continuación). El mecanismo de finalización puede utilizar la
tabla de símbolos del intérprete. También sería útil un comando para verificar (o
incluso sugerir) la coincidencia de paréntesis, comillas, etc.

Un intérprete interactivo mejorado alternativo que existe desde hace bastante


tiempo es IPython , que cuenta con finalización de pestañas, exploración de objetos
y gestión avanzada del historial. También se puede personalizar completamente e
integrar en otras aplicaciones. Otro entorno interactivo mejorado similar es bpython .

Un intérprete interactivo mejorado alternativo que existe desde hace bastante


tiempo es IPython , que cuenta con finalización de pestañas, exploración de objetos
y gestión avanzada del historial. También se puede personalizar completamente e
integrar en otras aplicaciones. Otro entorno interactivo mejorado similar es bpython .

15. Aritmética de punto flotante:


problemas y limitaciones
Los números de coma flotante se representan en el hardware de la computadora
como fracciones de base 2 (binarias). Por ejemplo, la fracción decimal

0.125

tiene valor 1/10 + 2/100 + 5/1000, y de la misma forma la fracción binaria

0.001

tiene valor 0/2 + 0/4 + 1/8. Estas dos fracciones tienen valores idénticos, la única
diferencia real es que la primera está escrita en notación fraccionaria de base 10 y
la segunda en base 2.

Desafortunadamente, la mayoría de las fracciones decimales no se pueden


representar exactamente como fracciones binarias. Una consecuencia es que, en
general, los números decimales de coma flotante que ingresa solo son aproximados
por los números binarios de coma flotante realmente almacenados en la máquina.

El problema es más fácil de entender al principio en base 10. Considera la fracción


1/3. Puede aproximar eso como una fracción de base 10:

0.3

o mejor,

0.33

o mejor,

0.333
y así. No importa cuántos dígitos esté dispuesto a escribir, el resultado nunca será
exactamente 1/3, pero será una aproximación cada vez mejor de 1/3.

De la misma manera, no importa cuántos dígitos de base 2 esté dispuesto a usar,


el valor decimal 0.1 no se puede representar exactamente como una fracción de
base 2. En base 2, 1/10 es la fracción que se repite infinitamente

0.0001100110011001100110011001100110011001100110011...

Deténgase en cualquier número finito de bits y obtendrá una aproximación. En la


mayoría de las máquinas actuales, los flotantes se aproximan usando una fracción
binaria con el numerador usando los primeros 53 bits comenzando con el bit más
significativo y con el denominador como una potencia de dos. En el caso de 1/10, la
fracción binaria es la que está cerca pero no exactamente igual al valor real de
1/10.3602879701896397 / 2 ** 55

Muchos usuarios no conocen la aproximación debido a la forma en que se muestran


los valores. Python solo imprime una aproximación decimal al valor decimal
verdadero de la aproximación binaria almacenada por la máquina. En la mayoría de
las máquinas, si Python imprimiera el verdadero valor decimal de la aproximación
binaria almacenada para 0.1, tendría que mostrar

>>>

>>> 0.1
0.1000000000000000055511151231257827021181583404541015625

Son más dígitos de los que la mayoría de las personas encuentran útiles, por lo que
Python mantiene la cantidad de dígitos manejable al mostrar un valor redondeado
en su lugar

>>>

>>> 1 / 10
0.1

Solo recuerde, aunque el resultado impreso parece el valor exacto de 1/10, el valor
real almacenado es la fracción binaria representable más cercana.
Curiosamente, hay muchos números decimales diferentes que comparten la misma
fracción binaria aproximada más cercana. Por ejemplo, los
números 0.1y 0.10000000000000001y 0.100000000000000005551115123125
7827021181583404541015625son todos aproximados por . Dado que todos estos
valores decimales comparten la misma aproximación, cualquiera de ellos podría
mostrarse conservando el
invariante .3602879701896397 / 2 ** 55eval(repr(x)) == x

Históricamente, el pitón pronta y construido en repr()función sería elegir el que


tenga 17 dígitos significativos, 0.10000000000000001. A partir de Python 3.1,
Python (en la mayoría de los sistemas) ahora puede elegir el más corto de estos y
simplemente mostrar 0.1.

Tenga en cuenta que esto está en la naturaleza misma del punto flotante binario:
esto no es un error en Python, y tampoco es un error en su código. Verá el mismo
tipo de cosas en todos los idiomas que admiten la aritmética de punto flotante de su
hardware (aunque es posible que algunos idiomas no muestren la diferencia de
forma predeterminada o en todos los modos de salida).

Para obtener un resultado más agradable, es posible que desee utilizar el formato
de cadena para producir un número limitado de dígitos significativos:

>>>

>>> format(math.pi, '.12g') # give 12 significant digits


'3.14159265359'

>>> format(math.pi, '.2f') # give 2 digits after the point


'3.14'

>>> repr(math.pi)
'3.141592653589793'

Es importante darse cuenta de que esto es, en un sentido real, una ilusión:
simplemente está redondeando la visualización del valor real de la máquina.

Una ilusión puede engendrar otra. Por ejemplo, dado que 0.1 no es exactamente
1/10, sumar tres valores de 0.1 puede no producir exactamente 0.3, ya sea:

>>>
>>> .1 + .1 + .1 == .3
False

Además, dado que 0.1 no puede acercarse más al valor exacto de 1/10 y 0.3 no
puede acercarse al valor exacto de 3/10, entonces el redondeo previo con
la round()función no puede ayudar:

>>>

>>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1)


False

Aunque los números no se pueden acercar más a sus valores exactos previstos,
la round()función puede ser útil para el post-redondeo de modo que los resultados
con valores inexactos sean comparables entre sí:

>>>

>>> round(.1 + .1 + .1, 10) == round(.3, 10)


True

La aritmética binaria de coma flotante tiene muchas sorpresas como esta. El


problema con "0.1" se explica en detalle a continuación, en la sección "Error de
representación". Consulte Los peligros del punto flotante para obtener una
descripción más completa de otras sorpresas comunes.

Como dice cerca del final, "no hay respuestas fáciles". Aún así, ¡no tenga demasiado
cuidado con el punto flotante! Los errores en las operaciones flotantes de Python se
heredan del hardware de punto flotante, y en la mayoría de las máquinas son del
orden de no más de 1 parte en 2 ** 53 por operación. Eso es más que adecuado
para la mayoría de las tareas, pero debe tener en cuenta que no es aritmética
decimal y que cada operación flotante puede sufrir un nuevo error de redondeo.

Si bien existen casos patológicos, para el uso más casual de la aritmética de punto
flotante, verá el resultado que espera al final si simplemente redondea la
visualización de sus resultados finales a la cantidad de dígitos decimales que
espera. str()normalmente es suficiente, y para un control más preciso, consulte
los str.format() especificadores de formato del método en Sintaxis de cadena
de formato .
Para los casos de uso que requieren una representación decimal exacta, intente
utilizar el decimalmódulo que implementa la aritmética decimal adecuada para
aplicaciones de contabilidad y aplicaciones de alta precisión.

Otra forma de aritmética exacta es compatible con el fractionsmódulo que


implementa la aritmética basada en números racionales (por lo que los números
como 1/3 se pueden representar exactamente).

Si es un gran usuario de operaciones de punto flotante, debería echar un vistazo al


paquete Numerical Python y muchos otros paquetes para operaciones matemáticas
y estadísticas proporcionados por el proyecto SciPy. Consulte < https://scipy.org >.

Python proporciona herramientas que pueden ayudar en las raras ocasiones en las
que realmente no quiere saber el valor exacto de un
flotador. El float.as_integer_ratio()método expresa el valor de un flotante
como una fracción:

>>>

>>> x = 3.14159
>>> x.as_integer_ratio()
(3537115888337719, 1125899906842624)

Dado que la relación es exacta, se puede utilizar para recrear sin pérdidas el valor
original:

>>>

>>> x == 3537115888337719 / 1125899906842624


True

El float.hex()método expresa un flotante en hexadecimal (base 16), dando


nuevamente el valor exacto almacenado por su computadora:

>>>

>>> x.hex()
'0x1.921f9f01b866ep+1'
Esta representación hexadecimal precisa se puede utilizar para reconstruir el valor
flotante exactamente:

>>>

>>> x == float.fromhex('0x1.921f9f01b866ep+1')
True

Dado que la representación es exacta, es útil para transferir valores de manera


confiable a diferentes versiones de Python (independencia de la plataforma) e
intercambiar datos con otros lenguajes que admiten el mismo formato (como Java
y C99).

Otra herramienta útil es la math.fsum()función que ayuda a mitigar la pérdida de


precisión durante la suma. Realiza un seguimiento de los "dígitos perdidos" a
medida que los valores se agregan a un total acumulado. Eso puede marcar una
diferencia en la precisión general para que los errores no se acumulen hasta el
punto en que afecten al total final:

>>>

>>> sum([0.1] * 10) == 1.0


False
>>> math.fsum([0.1] * 10) == 1.0
True

15.1. Error de representación


Esta sección explica el ejemplo "0.1" en detalle y muestra cómo puede realizar usted
mismo un análisis exacto de casos como este. Se asume una familiaridad básica
con la representación binaria de coma flotante.

El error de representación se refiere al hecho de que algunas (la mayoría, en


realidad) fracciones decimales no se pueden representar exactamente como
fracciones binarias (base 2). Esta es la razón principal por la que Python (o Perl, C,
C ++, Java, Fortran y muchos otros) a menudo no muestran el número decimal
exacto que espera.

¿Porqué es eso? 1/10 no es exactamente representable como una fracción


binaria. Casi todas las máquinas actuales (noviembre de 2000) utilizan aritmética
de punto flotante IEEE-754, y casi todas las plataformas asignan flotantes de Python
a IEEE-754 de “doble precisión”. 754 dobles contienen 53 bits de precisión, por lo
que en la entrada la computadora se esfuerza por convertir 0.1 a la fracción más
cercana que puede de la forma J / 2 ** N donde J es un número entero que contiene
exactamente 53 bits. Reescritura

1 / 10 ~= J / (2**N)

como

J ~= 2**N / 10

y recordando que J tiene exactamente 53 bits (es pero ), el mejor valor para N es
56:>= 2**52< 2**53

>>>

>>> 2**52 <= 2**56 // 10 < 2**53


True

Es decir, 56 es el único valor de N que deja a J con exactamente 53 bits. El mejor


valor posible para J es entonces ese cociente redondeado:

>>>

>>> q, r = divmod(2**56, 10)


>>> r
6

Dado que el resto es más de la mitad de 10, la mejor aproximación se obtiene


redondeando hacia arriba:

>>>

>>> q+1
7205759403792794

Por lo tanto, la mejor aproximación posible a 1/10 en 754 de doble precisión es:

7205759403792794 / 2 ** 56
Dividir el numerador y el denominador por dos reduce la fracción a:

3602879701896397 / 2 ** 55

Tenga en cuenta que, dado que redondeamos, esto es en realidad un poco más
grande que 1/10; si no lo hubiéramos redondeado, el cociente habría sido un poco
menor que 1/10. ¡Pero en ningún caso puede ser exactamente 1/10!

Entonces, la computadora nunca "ve" 1/10: lo que ve es la fracción exacta dada


anteriormente, la mejor aproximación doble 754 que puede obtener:

>>>

>>> 0.1 * 2 ** 55
3602879701896397.0

Si multiplicamos esa fracción por 10 ** 55, podemos ver el valor en 55 dígitos


decimales:

>>>

>>> 3602879701896397 * 10 ** 55 // 2 ** 55
1000000000000000055511151231257827021181583404541015625

lo que significa que el número exacto almacenado en la computadora es igual al


valor decimal
0.1000000000000000055511151231257827021181583404541015625. En lugar
de mostrar el valor decimal completo, muchos idiomas (incluidas las versiones
anteriores de Python), redondean el resultado a 17 dígitos significativos:

>>>

>>> format(0.1, '.17f')


'0.10000000000000001'

Los módulos fractionsy decimalfacilitan estos cálculos:

>>>

>>> from decimal import Decimal


>>> from fractions import Fraction

>>> Fraction.from_float(0.1)
Fraction(3602879701896397, 36028797018963968)

>>> (0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)

>>> Decimal.from_float(0.1)
Decimal('0.10000000000000000555111512312578270211815834045410156
25')

>>> format(Decimal.from_float(0.1), '.17')


'0.10000000000000001'

16. Apéndice
16.1. Modo interactivo
16.1.1. Manejo de errores

Cuando ocurre un error, el intérprete imprime un mensaje de error y un seguimiento


de la pila. En modo interactivo, luego regresa al indicador principal; cuando la
entrada proviene de un archivo, sale con un estado de salida distinto de cero
después de imprimir el seguimiento de la pila. (Las excepciones manejadas por
una exceptcláusula en una trydeclaración no son errores en este contexto). Algunos
errores son incondicionalmente fatales y causan una salida con una salida distinta
de cero; esto se aplica a inconsistencias internas y algunos casos de quedarse sin
memoria. Todos los mensajes de error se escriben en el flujo de errores estándar; la
salida normal de los comandos ejecutados se escribe en la salida estándar.

Al escribir el carácter de interrupción (normalmente Control-Co Delete) en el


indicador principal o secundario, se cancela la entrada y se vuelve al indicador
principal. 1 Escribir una interrupción mientras se ejecuta un comando genera
la KeyboardInterruptexcepción, que puede ser manejada por una try declaración.

16.1.2. Scripts Python ejecutables

En los sistemas Unix BSD'ish, los scripts de Python se pueden hacer directamente
ejecutables, como los scripts de shell, poniendo la línea

#!/usr/bin/env python3.5
(asumiendo que el intérprete está en el usuario PATH) al comienzo del script y dando
al archivo un modo ejecutable. El #! deben ser los dos primeros caracteres del
archivo. En algunas plataformas, esta primera línea debe terminar con un final de
línea estilo Unix ( '\n'), no un '\r\n' final de línea de Windows ( ). Tenga en cuenta
que el carácter de almohadilla, o almohadilla, '#' se usa para iniciar un comentario
en Python.

El script puede recibir un modo ejecutable, o permiso, usando el comando chmod .

$ chmod +x myscript.py

En los sistemas Windows, no existe la noción de un "modo ejecutable". El instalador


de Python asocia automáticamente .py archivos con python.exe para que un doble
clic en un archivo de Python lo ejecute como un script. La extensión también puede
ser .pyw, en ese caso, se suprime la ventana de la consola que normalmente
aparece.

16.1.3. El archivo de inicio interactivo

Cuando usa Python de forma interactiva, con frecuencia es útil ejecutar algunos
comandos estándar cada vez que se inicia el intérprete. Puede hacer esto
configurando una variable de entorno llamadaPYTHONSTARTUPal nombre de un
archivo que contiene sus comandos de inicio. Esto es similar a
la .profile característica de los shells de Unix.

Este archivo solo se lee en sesiones interactivas, no cuando Python lee comandos
de un script, y no cuando /dev/tty se proporciona como fuente explícita de comandos
(que de lo contrario se comporta como una sesión interactiva). Se ejecuta en el
mismo espacio de nombres donde se ejecutan los comandos interactivos, por lo que
los objetos que define o importa se pueden usar sin calificación en la sesión
interactiva. También puede cambiar las indicaciones sys.ps1y sys.ps2 en este archivo.

Si desea leer un archivo de inicio adicional desde el directorio actual, puede


programarlo en el archivo de inicio global usando un código como. Si desea utilizar
el archivo de inicio en un script, debe hacerlo explícitamente en el script:
if os.path.isfile('.pythonrc.py'): exec(open('.pythonrc.py').read())

import os
filename = os.environ.get('PYTHONSTARTUP')
if filename and os.path.isfile(filename):
with open(filename) as fobj:
startup_file = fobj.read()
exec(startup_file)

16.1.4. Los módulos de personalización

Python proporciona dos ganchos que le permiten


personalizarlo: sitecustomizey usercustomize. Para ver cómo funciona, primero debe
encontrar la ubicación de su directorio de paquetes de sitios de usuario. Inicie
Python y ejecute este código:

>>>

>>> import site


>>> site.getusersitepackages()
'/home/user/.local/lib/python3.5/site-packages'

Ahora puede crear un archivo con el nombre usercustomize.py en ese directorio y


poner lo que quiera en él. Afectará a todas las invocaciones de Python, a menos
que se inicie con la -s opción de deshabilitar la importación automática.

Sitecustomize funciona de la misma manera, pero normalmente lo crea un


administrador de la computadora en el directorio global site-packages y se importa
antes usercustomize. Consulte la documentación del site módulo para obtener más
detalles.

Notas al pie

Un problema con el paquete GNU Readline puede evitarlo.

39 - Biblioteca estándar de Python


Todos los conceptos que hemos visto hasta ahora los hemos resuelto
utilizando solo la sintaxis que nos provee Python y un conjunto de funciones
básicas que se incluyen automáticamente como por ejemplo son print, range,
len etc.
En Python se incluye una biblioteca extra de funciones, variables, clases etc.
que nos facilitan la resolución de problemas en una gran diversidad de áreas
como matemáticas, estadísticas, compresión de datos, internet, interfaces
visuales etc.
Veremos en este concepto como se importa un módulo de la biblioteca
estándar y como se accede a su funcionalidad.

Problema 1:
Confeccionar un programa que simule tirar dos dados y luego muestre los
valores que salieron. Imprimir un mensaje que ganó si la suma de los mismos
es igual a 7.
Para resolver este problema requerimos un algoritmo para que se genere un
valor aleatorio entre 1 y 6. Como la generación de valores aleatorios es un
tema muy frecuente la biblioteca estándar de Python incluye un módulo que
nos resuelve la generación de valores aleatorios.
Programa: ejercicio179.py
Ver video

import random

dado1=random.randint(1,6)
dado2=random.randint(1,6)
print("Primer dado:",dado1)
print("Segundo dado:",dado2)
suma=dado1+dado2
if suma==7:
print("Gano")
else:
print("Perdio")

Para importar un módulo de la biblioteca estándar de Python utilizamos la


palabra clave import seguida por el nombre del módulo que necesitamos
importar:
import random

Como dijimos la biblioteca estándar de Python se instala junto con Python.


Si disponemos un nombre de módulo inexistente aparecerá un error:

Traceback (most recent call last):


File "C:/programaspython/ejercicio179.py", line 1, in
import ran
ImportError: No module named 'ran'

Para acceder a todas las funciones contenidas en el módulo random es


necesario primero importar el módulo y luego dentro del algoritmo de nuestro
programa anteceder primero el nombre del módulo y seguidamente la función
que queremos acceder:

dado1=random.randint(1,6)

Si tratamos de acceder directamente al nombre de la función sin disponer el


nombre del módulo se produce un error:

Traceback (most recent call last):


File "C:/programaspython/ejercicio179.py", line 3, in
dado1=randint(1,6)
NameError: name 'randint' is not defined

Este error se produce porque la función randint no es una función integrada en


Python como print, range, len etc.
Entonces la sintaxis para acceder a la funcionalidad de un módulo requiere que
dispongamos primero el nombre del módulo y seguidamente el nombre de la
función.
Como podemos imaginar la función randint retorna un valor aleatorio
comprendido entre los dos valores indicados en los parámetros.
La ejecución del programa tiene una salida similar a esta:
Problema 2:
Desarrollar un programa que cargue una lista con 10 enteros.
Cargar los valores aleatorios con números enteros comprendidos entre 0 y
1000.
Mostrar la lista por pantalla.
Luego mezclar los elementos de la lista y volver a mostrarlo.
Programa: ejercicio180.py
Ver video

import random

def cargar():
lista=[]
for x in range(10):
lista.append(random.randint(0,1000))
return lista

def imprimir(lista):
print(lista)

def mezclar(lista):
random.shuffle(lista)

# bloque principal

lista=cargar()
print("Lista generada aleatoriamente")
imprimir(lista)
mezclar(lista)
print("La misma lista luego de mezclar")
imprimir(lista)
No hay ningún problema de llamar a las funciones de un módulo dentro de una
función propia siempre y cuando indiquemos el import respectivo:

import random

def cargar():
lista=[]
for x in range(10):
lista.append(random.randint(0,1000))
return lista

El módulo random cuenta con otra función llamada shuffle que le pasamos
como parámetro una lista y nos la devuelve con los elementos mezclados
(pensemos esto nos podría servir si estamos desarrollando un juego de naipes
y necesitamos mezclarlos):

def mezclar(lista):
random.shuffle(lista)

Desde el bloque principal procedemos a llamar a las funciones que hemos


codificado:

# bloque principal

lista=cargar()
print("Lista generada aleatoriamente")
imprimir(lista)
mezclar(lista)
print("La misma lista luego de mezclar")
imprimir(lista)

En la documentación oficial de Python podemos consultar todas las funciones


que nos provee el módulo random random.
Y en general podemos también consultar todos los módulos de la Biblioteca
estándar de Python.
Problemas propuestos
 Confeccionar un programa que genere un número aleatorio entre 1 y 100
y no se muestre.
El operador debe tratar de adivinar el número ingresado.
Cada vez que ingrese un número mostrar un mensaje "Gano" si es igual
al generado o "El número aleatorio es mayor" o "El número aleatorio es
menor".
Mostrar cuando gana el jugador cuantos intentos necesitó.

Ver video

 Confeccionar una programa con las siguientes funciones:


1) Generar una lista con 4 elementos enteros aleatorios comprendidos
entre 1 y 3. Agregar un quinto elemento con un 1.
2) Controlar que el primer elemento de la lista sea un 1, en el caso que
haya un 2 o 3 mezclar la lista y volver a controlar hasta que haya un 1.
3) Imprimir la lista.

El módulo Python3 de la semana

Fuera de la biblioteca estándar

Aunque la biblioteca estándar de Python es extensa, también hay un ecosistema


robusto de módulos proporcionados por desarrolladores terceros y disponible
del Índice de Paquetes Python. Este apéndice describe algunos de estos módulos,
y las situaciones en las que es posible que desees utilizarlos para complementar o
incluso reemplazar la biblioteca estándar.

Texto
El módulo string incluye una herramienta de plantilla muy básica. Muchos
marcos Web incluyen herramientas de plantilla más potentes,
pero Jinja2 y Mako son alternativas independientes populares. Ambos admiten
bucles y estructuras de control condicional, así como otras características para
combinar datos con una plantilla para producir texto de salida.

El módulo re incluye funciones para buscar y analizar texto usando patrones


formalmente descritos, llamados expresiones regulares. Sin embargo, no es la
única forma de analizar texto.
El paquete PLY admite la construcción de analizadores en el estilo de las
herramientas GNU lexx y yacc, comúnmente usadas para construir compiladores
de lenguaje. Porproporcionando entradas que describen los tokens válidos, una
gramática y acciones para tomar cuando se encuentran cada uno, es posible
construir compiladores completos e intérpretes funcionales, así como analizadores
de datos más sencillos.

PyParsing es otra herramienta para construir analizadores. Las entradas son


instancias de clases que se pueden unir entre sí utilizando operadores e
invocaciones de métodos llama para construir una gramática.

Finalmente, NLTK es un paquete para procesar texto en lenguaje natural –


idiomas humanos en lugar de lenguajes de computadora. Es compatible con el
análisis de oraciones en partes de un discurso, encontrando la forma raíz de las
palabras, y procesamiento semántico básico

Algoritmos
El módulo functools incluye algunas herramientas para crear decoradores,
funciones que envuelven otras funciones para cambiar la forma en que se
comportan. El paquete wrapt va más allá de functools.wrap() al asegurarse de
que un decorador se construya correctamente y funcione para todos los casos
extremos.

Fechas y horas
Los módulos time y datetime proporcionan funciones y clases para manipular
valores de fecha y hora. Ambos incluyen funciones para analizar cadenas para
convertirlas en representaciones internas. El paquete dateutil incluye un analizador
más flexible que hace más fácil construir aplicaciones robustas que son más
indulgentes con los diferentes formatos de entrada.

El módulo datetime incluye una clase consciente de la zona horaria


representando un tiempo específico en un día específico. Sin embargo, no incluye
una base de datos de zona horaria completa. El paquete pytz proporciona una tal
base de datos. Se distribuye por separado de la biblioteca estándar porque es
mantenido por otros autores y se actualiza con frecuencia cuando los valores de la
zona horaria y del horario de verano son cambiados por instituciones políticas que
los controlan.

Matemáticas
El módulo math contiene implementaciones rápidas de funciones matemáticas
avanzadas. NumPy amplía el conjunto de funciones admitidas para incluir
funciones de álgebra lineal y la transformación de Fourier. También incluye una
implementación rápida de matriz multidimensional, mejorando la versión
en array .

Persistencia e intercambio de datos


Los ejemplos en la sección sqlite3 ejecutan sentencias SQL directamente y
trabajan con estructuras de datos de bajo nivel. Para aplicaciones grandes, es a
menudo deseable mapear clases a tablas en la base de datos usando
un mapeador relacional de objetos u ORM. La biblioteca
ORM sqlalchemy proporciona interfaces para asociar clases con tablas, generar
consultas, y conectar a diferentes tipos de base de datos relacionales de grado de
producción.

El paquete lxml envuelve las bibliotecas libxml2 y libxslt para crear una alternativa
al analizador XML en xml.etree.ElementTree . Desarrolladores familiarizados con
el uso de esas bibliotecas de otros idiomas pueden encontrar lxml más fácil de
adoptar en Python.

El paquete defusedxml contiene correcciones para «Billion Laughs» y otras


vulnerabilidades de denegación de servicio de expansión de entidad en Bibliotecas
XML de Python y hace que trabajar con XML no confiable sea más seguro que
usar solamente la biblioteca estándar.

Criptografía
El equipo que está creando el paquete cryptography dice «Nuestro objetivo es ser
tu “biblioteca estándar criptográfica”». El paquete de criptografía expone interfaces
de alto nivel para facilitar la incorporación de funciones criptográficas a las
aplicaciones y el paquete se mantiene activamente con lanzamientos frecuentes
para abordar vulnerabilidades en las bibliotecas subyacentes tales como
OpenSSL.
Concurrencia con Procesos, Hilos y Corutinas
El bucle de evento integrado en asyncio es una implementación de referencia
basada en la interfaz abstracta definida por el módulo. Es posible reemplaz el
bucle de evento con una biblioteca como uvloop, que da un mejor rendimiento a
cambio de agregar dependencias de aplicación adicionales.

El paquete curio es otro paquete de concurrencia similar a asyncio pero con una
interfaz más pequeña que trata todo como una corutina y no admite devoluciones
de llamadas en la forma en que asyncio lo hace.

La biblioteca Twisted proporciona un marco extensible para programación en


Python con un enfoque especial en la programación de redes basadas en eventos
e integración multiprotocolo. Es madura, robusta y está bien documentada.

El Internet
El paquete requests es un reemplazo muy popular para urllib.request .
Proporciona una interfaz consistente para trabajar con recursos remotos
direccionables a través de HTTP, incluye soporte robusto de SSL, y puede usar
agrupación de conexiones para un mejor rendimiento en aplicaciones multihilo.
También proporciona funciones que lo hacen muy adecuado para acceder a las
interfaces REST, como el análisis integrado JSON.

El módulo html de Python incluye un analizador básico para datos HTML bien
formados. Sin embargo, los datos del mundo real rara vez están bien
estructurados, lo que hace problemático analizarlos. Las
bibliotecas BeautifulSoup y PyQuery son alternativas a html que son más
robustas frente a datos desordenados. Ambos definen interfaces para analizar,
modificar y construir HTML.

El paquete incorporado http.server incluye clases base para crear servidores


HTTP simples desde cero. No ofrece mucho soporte más allá de eso para la
construcción de aplicaciones basadas en Web. Los
paquetes Django y Pyramid son dos marcos de aplicaciones Web populares que
proporcionan más soporte para funciones avanzadas como análisis de solicitud,
enrutamiento de URL y manejo de cookies.
Muchas bibliotecas existentes no funcionan con asyncio porque no se integran
con el bucle de eventos. Un nuevo conjunto de bibliotecas como aiohttp se está
creando para llenar este vacío como parte del proyeco aio-libs.

Correo electrónico
La interfaz para imaplib es relativamente de bajo nivel, requiriendo que
comprender el protocolo IMAP para generar consultas y analizar los resultados. El
paquete imapclient proporciona una interfaz de nivel superior con la que es más
fácil trabajar para construir aplicaciones que necesitan manipular buzones IMAP.

Bloques de construcción de aplicaciones


Los dos módulos de biblioteca estándar para construir interfaces de línea de
comando, argparse y getopt , ambos separan la definición de argumentos de
línea de comando de su análisis sintáctico y procesamiento de valores.
Alternativamente, click (el «kit de construcción de interfaz de línea de comando»),
funciona definiendo funciones de procesamiento de comandos y luego asociando
definiciones de opciones y de símbolos de sistema con esos comandos usando
decoradores.

cliff («Marco de formulación de interfaz de línea de comandos») proporciona un


conjunto de clases base para definir comandos y un sistema de complemento para
extender aplicaciones con múltiples sub-comandos que pueden ser distribuidos en
paquetes separados. Utiliza argparse para construir el texto de ayuda y el
analizador de argumentos, por lo que el procesamiento de la línea de comando es
familiar.

El paquete docopt invierte el flujo típico preguntando al desarrollador para que


escriba el texto de ayuda de un programa, que luego analiza para entender los
sub-comandos, combinaciones válidas de opciones y sub-comandos

Para programas interactivos basados en terminales, prompt_toolkit incluye


características avanzadas como soporte de color, resaltado de sintaxis, edición de
entrada, soporte de mouse e historial de búsqueda. Se puede usar para crear
programas orientados a comandos con un bucle de solicitud como el
módulo cmd o aplicaciones de pantalla completa, como editores de texto.
Mientras que los archivos INI como los usados por configparser continúan
siendo populares para la configuración aplicaciones, el formato de
archivo YAML también es muy popular. YAML proporciona muchas de las
características de la estructura de datos de JSON en un formato que es más fácil
de leer para las personas. La biblioteca PyYAML proporciona acceso a un
analizador y un serializador de YAML.

Herramientas de desarrollo
El módulo venv de la biblioteca estándar es nuevo en Python 3. Para aislamiento
de aplicación similar en Python 2 y 3, usa virtualenv.

El paquete fixtures proporciona varios clases para el manejo de recursos de


pruebas hechas a medida para trabajar con el método addCleanup() de casos de
prueba del módulo unittest . Las clases de fixtures proporcionadas pueden
gestionar registradores, variables de entorno, archivos temporales y más de una
manera consistente y segura que asegure que cada caso de prueba esté
completamente aislado de otros en la suite.

El módulo distutils en la biblioteca estándar para empaquetar módulos de


Python para distribución y reutilización están en desuso. El reemplazo, setuptools,
se empaqueta por separado de la biblioteca estándar para facilitar la entrega de
nuevas versiones con frecuencia. La interfaz para setuptools incluye herramientas
para compilar la lista de archivos para incluir en un paquete. Hay extensiones para
construir automáticamente la lista del conjunto de archivos administrados por un
sistema de control de versiones. Por ejemplo, usando setuptools-git con código en
un repositorio git hace que todos los archivos rastreados se incluyan en el paquete
por defecto. Después de crear un paquete, la aplicación twine lo cargará al índice
del paquetes para ser compartido con otros desarrolladores.

Herramientas como tabnanny son buenas para encontrar errores comunes de


formateo en código Python. La Autoridad de calidad del código Python mantiene
una amplia gama de herramientas de análisis estático más avanzadas, incluyendo
herramientas que hacen cumplir las pautas de estilo, encuentran errores comunes
de programación, e incluso ayudan a evitar la complejidad excesiva.
Módulos, paquetes y la librería estándar de Python
Como ya hemos visto, podemos importar nuevos módulos de la librería extándar de
Python o de terceros con import <modulo>, pero hay varias maneras de hacerlo:
In [10]: import math # importa el módulo math
In [11]: import math as M # importa el módulo math llamándolo M
In [12]: from math import sin, cos, pi # importa las funciones sin, cos y
pi de math
In [13]: from math import * # importa todas las funciones de math

De manera similar podemos crear un módulo propio que puede usarse como un
programa independiente o importase como un módulo y poder reutilizar sus funciones:
#!/usr/bin/python3
#-*- coding: utf-8 -*-

"""Programa de calculo del cubo de un numero"""

__author__ = "Jorge"
__copyright__ = "Curso de Python"
__credits__ = ["Pepe", "José Luis", "Roberto"]
__license__ = "GPL"
__version__ = "1.0"
__email__ = "[email protected]"
__status__ = "Development"

def cubo(x):
"""Calcula el cubo de un numero"""
y = x**3
return y

if __name__ == "__main__":
x = int( input("Dame un numero: ") )
y = cubo(x)
print("El cubo de %.2f es %.2f" % (x, y))

Bien, ahora podemos usar este programa como un ejecutable como ya hemos hecho o
importarlo como un módulo y usar la función cubo(). La primera de comentario
multilínea, limitada por comillas triples, se asiga automáticamente a la variable
mágica doc como la documentación del módulo o programa y el resto de variables
especiales como información adicional. Igualmente la primera línea de def() es la
documentación de la función. La variable especial name es el nombre del módulo
cuando se usa como tal, que en este caso vale cubo, pero tiene valor «main» cuando se
ejecuta como un programa. De esta manera distinguimos cuando el código se está
ejecutando como un programa o está siendo llamado como un módulo.
import cubo
In [20]: cubo.__doc__
Out[20]: 'Programa de calculo del cubo de un numero'

In [21]: cubo.cubo(3)
Out[21]: 27

In [22]: cubo.cubo.__doc__
Out[22]: 'Calcula el cubo de un numero'

In [23]: cubo.__version__
Out[23]: '1.0'

Para poder importar un módulo nuestro, debe estar en el directorio donde lo estamos
llamando, o bien estar en una ruta incluida en el PATH de la librería o bien en la variable
PYTHONPATH.
$ echo $PYTHONPATH
:/home/japp/codigo/lib/:/usr/local/aspylib/:/usr/local/lib/python2.7/dist -
packages/

Alternativamente, se puede incluir el PATH en el programa ejecutable añadiéndolo a la


lista sys.path:
import sys
sys.path.append('/home/japp/mis_modulos/')

En Windows, funciona de forma idéntica pero usando las rutas de Windows:


sys.path.append('C:\mis_modulos')

Para modificar de forma temporal el PYTHONPATH en Windows haríamos:


C:\>set PATH=C:\Program Files\Python 3.6;%PATH%
C:\>set PYTHONPATH=%PYTHONPATH%;C:\mis_modulos
C:\>python

Si se quiere añadir permanentemente es algo más complicado. Desde el botón de inicio


hay que buscar Propiedades del sistema (System properties) -> Advanced system
settings y pinchar en el botón de variables de entorno, donde se pueden modificar la
variables de entorno del sistema (solo el administrador).

Estructura de un paquete de Python

Los paquetes de python son un espacio de nombres que contiene varios módulos o
paquetes, a veces relacionados entre ellos aunque no tiene porqué. Se crean en un
directorio que debe incluir obligatoriamente un fichero especial llamado __init__.py que
es el que indica que se trata de un paquete y luego pueden haber otros módulos e
incluso otros paquetes. La siguiente es la estructura típica de un paquete:
mi_paquete/
__init__.py
modulo1.py
modulo2.py
utiles/
__init__py
utiles1.py
config.py

El fichero __init__.py puede y suele estar vacío, aunque se puede usar para importar
modulos comunes entre paquetes.
import mi_paquete

from mi_paquete import utiles1

La librería estándar de Python

La instalación básica de Python viene con una muy completa librería de módulos para
todo tipo de tareas, incluyendo acceso a ficheros y directorios, compresión de ficheros,
ejecución recurrente (multihilo), email, html, xml, csv y un largo etcétera. Lo más
conveniente es consultar la documentación de la librería estándar para tener una idea
de todo lo disponible, pero podemos probar los más importantes.

Creación y administración de ficheros

La forma más directa y práctica de interactuar con el sistema, independientemente de la


plataforma, es empleando el módulo os, que básicamente es una interfaz para sistema
operativo del ordenador que ejecuta el programa.
import os

os.chdir("/home/japp/Documentos/")

os.getcwd()
# /home/japp/Documentos/

# Esto no imita a ls, no distingue ficheros y directorios


ficheros = os.listdir(".") # hay que poner una ruta

for fichero in ficheros:


print os.path.isdir(fichero) # .isfile(), islink()

Para mayor flexibilidad en la selección de ficheros, por ejemplo usar caracteres


comodín, se puede usar el paquete glob:
from glob import glob

ficheros = glob("*.txt")
# Son listas también pero con una ruta relativa, así que no funciona igual
que listdir
ficheros = glob("/home/japp/") # no devuelve nada
ficheros = glob("/home/japp/*") # Esto si

os.mkdir("registro")
# os.makedirs('/home/japp/Documentos/datos/pruebas') # Linux, Mac
# os.makedirs('C:\\Mis Documentos\\datos\\pruebas') # Windows

os.chmod("registro", 0700)

os.rename("registro", "registros")

Lectura y escritura de ficheros de texto

Si queremos leer o escribir ficheros de texto primero hay que abrirlos en el modo
adecuado (r, w, a) para terner una instacia del fichero, que luego se puede leer
a string de varias formas.
# Leo un fichero CSV con código y nombre de paises
fichero = open("paises.csv")

contenido = fichero.read() # Lo mete todo en un único string


fichero.close()

len(contenido)

print(contenido[:30])
#'nombre, name, nom, iso2, iso3,'

fichero = open("paises.csv")
lineas = fichero.readlines() # Lee línea a línea, devuelve una lista
fichero.close()

len(lineas)
247

De haber querido separar por columnas, pudimos haber usado algo como:
nombre, name, nom, iso2, iso3, phone_code = lineas.split(";")

justo después de readlines(), al hacerlo, split() devuelve una lista de dos elementos
(en este caso) que desempaquetamos en las variables que damos a la izquierda.

Podemos igualmente escribir un fichero creando un fichero en modo lectura y usar el


método write(str) para guardar una cadena de texto o bien
usar writelines(lista) para guardar el contenido de una lista de strings.

¿Y si el fichero es remoto? hay varias maneras de resolverlo, pero lo más cómodo es con
el módulo urllib:
import urllib.request
import csv

# Fichero remoto
# https://gist.github.com/brenes/1095110
url =
"https://gist.githubusercontent.com/brenes/1095110/raw/f8eeb4a7efb257921e6236
ef5ce2dbc13c50c059/paises.csv"

# Terremotos del día de USGS


# url =
"https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.csv"

# Leemos remotamente el fichero csv


respuesta = urllib.request.urlopen(url)

# Pasamos la instancia a un string


contenido = respuesta.read() # o readlines()

# Usamos esta vez el módulo csv de Python para interpretar el CSV


reader = csv.reader(contenido)

Ahora probemos a hacer una selección de los paises que empiezan por «P», pero en su
nombre, no en su código
# Lista de paises que empiezan por P, vacía al principio
lineas_P = []

for linea in lineas:


codigo, nombre = linea.split(";")
if nombre.startswith('P'):
lineas_P.append(linea)

# Abro el fichero donde voy a guardar


f_out = open("paises_P.txt", "w")

f_out.writelines(lineas_P)
f_out.close()

El fichero resultante es un fichero igual que el anterior pero solo con los paises que
empiezan con «P», uno por línea, pero es es línea a línea porque el fichero original
incluye caracteres de nueva línea. El método writelines(lista) no escribe a líneas y
éstas deben añadirse explícitamente:
# Lista de numeros enteros, que paso a string y añado nueva línea
numeros = [str(n)+"\n" for n in range(100)]

f_out = open("numeros.txt", "w")


f_out.writelines(numeros)
f_out.close()

Es posible guardar tambien variable en binario para usarlas después,


empleando shelve():
import shelve

shelf_file = shelve.open('datos')

shelf_file['numeros'] = numeros
shelf_file.close()

# Al cerrar el fichero se guardan los datos, que se pueden recuperar abriendo


el fichero.

shelf_file = shelve.open('datos')
shelf_file['numeros']

El módulo os tiene otros métodos útiles para interactuar con el sistema y los procresos
que ejecuta.
os.getlogin()
#'japp'

os.getgroups()
#[191, 256, 294, 329, 350, 2000]

os.getenv('HOME')

os.putenv('HOME', '/scratch/japp')

os.uname()
# ('Linux', 'vega', '4.1.13-100.fc21.x86_64', '#1 SMP Tue Nov 10 13:13:20 UTC
2015', 'x86_64')

Si se desea más información sobre el equipo, se puede emplear el módulo platform,


que da información más completa y detallada sobre y ordenador y el SO:
import platform

print('uname:', platform.uname())

print('system :', platform.system())


print('node :', platform.node())
print('release :', platform.release())
print('version :', platform.version())
print('machine :', platform.machine())
print('processor:', platform.processor())
print('distribution:', " ".join(platform.dist()) ) # Linux, mac_ver() para
OS X

"""
uname: ('Linux', 'vega', '4.1.13-100.fc21.x86_64', '#1 SMP Tue Nov 10
13:13:20 UTC 2015', 'x86_64', 'x86_64')

system : Linux
node : vega
release : 4.1.13-100.fc21.x86_64
version : #1 SMP Tue Nov 10 13:13:20 UTC 2015
machine : x86_64
processor: x86_64
distribution: fedora 21 Twenty One
"""

Si se desea mayor control sobre los ficheros y directorios, el módulo shutil permite
operaciones con ficheros a alto nivel.
improt shutil

shutil.copy('paises.csv', 'paises-copy.csv') # Copia un fichero

shutil.copytree("/home/japp/Documentos", "/home/japp/Documentos-copia") #
Copia el directorio y su contenido

shutil.move('paises-copy.csv', '/home/japp/Documentos/') # Mueve un fichero

¿Cómo borrar ficheros? Existen tres métodos principales:


os.unlink(path) # Borra el fichero en path
os.rmdir(path) # Borra el directorio en path, que debe estar vacío
shutil.rmtree(path) # Borra path recursivamente

Si queremos borrar con más cuidado podemos usar condicionales:


for filename in os.listdir("."):
if filename.endswith('.csv'):
os.unlink(filename)

En el ejemplo anterior hemos hecho un listado sencillo del directorio en el que estamos.
Para hacer una exploración recursiva de un directorio, distinguiendo en ficheros y
directorios, podemos usar os.walk():
for directorio, subdirectorios, ficheros in
os.walk("/home/japp/Documentos/"):
print('El directorio ' + directorio)

os.walk() devuelve una tupla de tres elementos con el nombre del directorio actual,
una lista de subdirectorios que contiene y una lista de ficheros que contiene.

Con el módulo zip se pueden leer y escribir ficheros zip:


fichero_zip = zipfile.ZipFile('datos', 'w')
ficheros = ['medidas_PV_He.txt', 'medidas_radio.txt', 'bright_star.tsv']

for fichero in ficheros:


newZip.write(fichero, compress_type=zipfile.ZIP_DEFLATED)

fichero_zip.close()

fichero_zip = zipfile.ZipFile("datos.zip")
fichero_zip.namelist()

# informacion sobre un fichero en concreto del zip


bright_star_info = fichero_zip.getinfo('bright_star.tsv')
bright_star_info.file_size
# 926482

bright_star_info.compress_size
# 248269

# Extraigo el contenido
fichero_zip.extract('bright_star.tsv', '/home/japp/Documents/')
fichero_zip.extractall() # todos los ficheros
fichero_zip.close()

Trabajando con fechas y tiempo

La librería estándar de Python incluye varios módulos para tratar y manipular fechas,
tiempo e intervalos. Como con otros módulos, una vez importado el módulo se define
un objeto específico que permite hacer malabarismos con fechas y tiempo. El módulo
principal es datetime, que permite trabajar con fechas y tiempo mientras que el
módulo time, ofrece métodos avanzados para tiempo, ignorando la fecha.
import datetime

print("La fecha y hora actuales: " , datetime.datetime.now() # Devuelve un


objeto datetime
print("Fecha y hora en string con formato: " ,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M")

print("Año actual: ", datetime.date.today().strftime("%Y"))


print("Mes del año: ", datetime.date.today().strftime("%B"))
print("Semana del año: ", datetime.date.today().strftime("%W"))
print("Número de día de la semana: ", datetime.date.today().strftime("%w"))
print("Día del año: ", datetime.date.today().strftime("%j"))
print("Día del mes: ", datetime.date.today().strftime("%d"))
print("Día día de la semana: ", datetime.date.today().strftime("%A"))

import time

print("Segundos desde inicio de época: %s" %time.time())

# Para una fecha específica

fecha = datetime.date(1937, 10, 8) #year, month, day


print(fecha.strftime("%A"))
# Friday

print(fecha.strftime("%b %d %Y %H:%M:%S"))
# Oct 08 1937 00:00:00
En el ejemplo anterior usamos el método strftime() para obtener un string en el
formato deseado según la sintaxis de fechas de Python. De manera similar podemos
usar strptime() para convertir un string de fecha a un objeto date o datetime de
Python:
# Fecha en string
fecha_str = "2017-05-16 10:30:00"

# Formato en el que está la fecha en string


fecha_fmt = "%Y-%m-%d %H:%M:%S"

# Objeto datetime a partir de la fecha en string


fecha = datetime.datetime.strptime(fecha_str, fecha_fmt)

print(fecha.strftime("%A %d %B, %Y"))


# 'Tuesday 16 May, 2017'

# Cambio de idioma
import locale

idioma = locale.setlocale(locale.LC_TIME, "es_ES")


print(fecha.strftime("%A %d %B, %Y"))
# martes 16 mayo, 2017
# Intervalos de tiempo y operaciones con fechas

hoy = datetime.date.today()
print('Hoy:', hoy)

un_dia = datetime.timedelta(days=1)
print('Lapso de un día:', one_day)

ayer = hoy - un_dia


print('Ayer:', ayer)

manhana = hoy + un_dia


print('Manhana :', manhana)

print('Manhana - ayer:', manhana - ayer)


print('Ayer - manhana:', ayer - manhana)

ayer > hoy


False

ayer < hoy


True

Hay que tener en cuenta que los tiempos se toman de ordenador, incluyendo la zona
horaria, por lo que generalmente serán en hora local. Si queremos convertir a otra zona
horaria, debemos usar el módulo pytz:
# Hora local canaria actual
hora_local = datetime.datetime.now()
# datetime.datetime(2017, 5, 12, 10, 30, 0, 379146)
# Hora actual en UTC
hora_utc = datetime.datetime.utcnow()
# datetime.datetime(2017, 5, 12, 9, 30, 0, 226718)

from pytz import timezone

hora_us_pacific = hora_utc.replace(tzinfo=timezone('US/Pacific'))

Finalmente, el módulo calendar ofrece alguna funciones de calendario:


import calendar

cal = calendar.month(2017, 5)
print(cal)
May 2017
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

print(calendar.TextCalendar(calendar.MONDAY).formatyear(2017, 2, 1, 1, 2))

Llamadas al sistema

La forma más sencilla de ejecutar comandos sistema, por ejemplo para lanzar
programas o ejecutar comandos de la consola es el método os.system()
import os

os.system('touch /home/japp/Documents')

Sin embargo system() es muy limitado y no permite recoger el resultado la ejecución,


de haberla. Mucho más útil y potente es el módulo subprocess:
import subprocess

# Uso básico similar a os.system()


subprocess.call(['ls', '-l'])

Puesto que los canales de entrada y salida del proceso call() están ligados a la entrada
y salida padre, no puede capturar la salida del comando que ejecuta, como ocurre
con os.system(). Si queremos capturar la salida podemos emplear check_output() y
luego procesar el resultado como queramos.
output = subprocess.check_output(['ps', '-x'])

print(output)
"""
PID TTY STAT TIME COMMAND
3901 ? S 0:00 sshd: invweb@pts/2
3902 pts/2 Ss 0:00 -bash
4248 pts/2 Sl 0:02 gedit cdb_import.py
4527 ? Sl 0:00 /usr/libexec/dconf-service
6134 ? Sl 0:15 /usr/local/apache//bin/httpd -k start
13324 pts/2 Sl+ 0:00 /usr/bin/python /usr/bin/ipython
13613 pts/2 R+ 0:00 ps -x
26515 ? S 0:03 sshd: invweb@pts/0
26516 pts/0 Ss+ 0:00 -bash
"""

# Separo for filas


output_lines = output.split("\n")

# Trozo que contiene el comando


output_lines[1][27:]

# Busco los procesos que usan Python


resultados = []

for line in output_lines:


if 'python' in line.lower():
resultados.append(line[:5]) # Me quedo con trozo que tiene el PID

print(resultados)

Usando tuberías directamente podemos usar parámetros para indicar la entrada y salida
y capturar errores. Veamos este ejemplo de una función que llama al ping del sistema:
def esta_viva(hostname):
"""
Hace un ping a una maquina para saber si esta conectada
"""

ping = subprocess.Popen(["ping", "-n", "-c 1", hostname],


stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, error = ping.communicate()

if error == "":
print("El ordenador {} está conectado".format(hostname))
return True
else:
print("El ordenador {} está KO".format(hostname))
return False

esta_viva('vega')
Conexión remota por FTP y SSH

La librería estándar de Python incluye un módulo ftp con todas las funcionalidades
necesarias. Veamos un ejemplo para copiar ficheros locales al FTP público del IAC.
import ftplib
import os
from glob import glob

# Origen de los datos


origin_dir = "/home/japp/Documents/"

# directorio destino (en el servidor externo)


destination_dir = "in/curso_python"

# Lista de los ficheros a copiar, todos los *.py


files = glob(origin_dir + "*.py")

ftp = ftplib.FTP("ftp.iac.es")
login = ftp.login("[email protected]", "anonymous")
ftp.cwd(destination_dir)

os.chdir(origin_dir)

for filename in files:


infile = open(filename, 'r')
ftp.storlines('STOR ' + os.path.basename(filename), infile)
infile.close()

Hay que fijarse que solo copia ficheros de uno en uno, si se quiere copiar
recursivamente hay que implementar una copia recursiva con os.walk() o similar e ir
creado directorios con mkdir().

No hay un módulo específico para ssh, pero se puede usar el del sistema usando el
módulo pexpect, que permite manejar envío de información entre un servidor y un
cliente, en ambas direcciones.
import pexpect
import time

host = "vega"
password = "secreto"

ssh_newkey = 'Are you sure you want to continue connecting'


ssh = pexpect.spawn("ssh {}".format(host))
i = ssh.expect([ssh_newkey, 'password:', host, pexpect.EOF], timeout=10)

if i == 0:
ssh.sendline('yes')
# Se dan una lista de posibles salidas del comando: nueva key,
# la contraseña o el prompt del sistema si no pide contraseña
i = ssh.expect([ssh_newkey, 'password: $', pexpect.EOF])
ssh.sendline(password)
ssh.expect(pexpect.EOF)
elif i == 1:
ssh.sendline(password)
ssh.expect(pexpect.EOF, timeout=10)
elif i == 2:
pass

# Extraemos el resultado del comando


p = pexpect.spawn("df -h")
print(p.read())

ssh.close()

También podría gustarte