El Tutorial de Python1 PDF
El Tutorial de Python1 PDF
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.
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 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:
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.
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.
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 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.
>>>
>>>
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
#!/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.
Algunos ejemplos:
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 %:
>>>
>>>
>>> 5 ** 2 # 5 squared
25
>>> 2 ** 7 # 2 to the power of 7
128
>>> 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:
>>>
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
>>>
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:
>>>
>>>
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:
>>>
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):
Dos o más cadenas literales (es decir, las que están entre comillas) una al lado de
la otra se concatenan automáticamente.
>>>
>>>
Sin embargo, esto solo funciona con dos literales, no con variables o expresiones:
>>>
>>>
Los índices también pueden ser números negativos, para empezar a contar desde
la derecha:
>>>
Tenga en cuenta que dado que -0 es lo mismo que 0, los índices negativos
comienzan desde -1.
>>>
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
>>>
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.
>>>
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.
>>>
Sin embargo, los índices de corte fuera de rango se manejan con elegancia cuando
se usan para cortar:
>>>
>>> word[4:42]
'on'
>>> word[42:]
''
>>>
>>>
>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34
Ver también
Tipo de secuencia de texto: str
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.
>>>
Al igual que las cadenas (y todos los demás tipos de secuencia integrados ), las
listas se pueden indexar y dividir:
>>>
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]
>>>
A diferencia de las cadenas, que son inmutables , las listas son de tipo mutable , es
decir, es posible cambiar su contenido:
>>>
>>>
>>>
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'
>>>
>>>
>>> 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:
>>>
4.1. ifDeclaraciones
Quizás el tipo de declaración más conocido es la ifdeclaración. Por ejemplo:
>>>
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):
>>>
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:
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:
>>>
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
Para iterar sobre los índices de una secuencia, puede combinar range()y de
la len()siguiente manera:
>>>
>>>
>>> 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]
>>>
>>> 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
>>>
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:
>>>
>>>
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:
>>>
>>>
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.
>>>
>>> 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:
>>>
Este ejemplo también presenta la inpalabra clave. Esto prueba si una secuencia
contiene un cierto valor.
i = 5
def f(arg=i):
print(arg)
i = 6
f()
imprimirá 5.
print(f(1))
print(f(2))
print(f(3))
Esto imprimirá
[1]
[1, 2]
[1, 2, 3]
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
>>>
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 .
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
>>>
>>>
>>> standard_arg(2)
2
>>> standard_arg(arg=2)
2
>>>
>>> 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'
>>>
>>> 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
>>>
>>> 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
Finalmente, considere esta definición de función que tiene una posible colisión entre
el argumento posicional name y **kwdsque tiene namecomo clave:
No hay una llamada posible que lo haga regresar Trueya que la palabra
clave 'name' siempre se vinculará al primer parámetro. Por ejemplo:
>>>
4.7.3.5. Resumen
Como orientación:
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.
>>>
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]
>>>
>>>
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')]
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.
>>>
>>>
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
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.
list.append( x )
list.extend( iterable )
list.insert( yo , x )
list.remove( x )
list.pop( [ i] )
list.clear( )
Devuelve un índice de base cero en la lista del primer elemento cuyo valor
es igual ax . Aumenta un ValueErrorsi no existe tal elemento.
list.count( x )
list.reverse( )
Invierta los elementos de la lista en su lugar.
list.copy( )
>>>
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:
>>>
>>>
>>> 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:
>>>
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,.
>>>
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:
>>>
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.
>>>
>>>
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
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
>>>
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:
>>>
>>>
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:
>>>
>>>
>>> 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.
>>>
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):
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 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)
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
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
Esto significaría que importaría los tres submódulos del paquete con
nombre.from sound.effects import *sound
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
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 *
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.
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.
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.
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.
>>>
>>>
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().
Algunos ejemplos:
>>>
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.
>>>
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.
>>>
>>>
>>>
>>>
>>>
>>>
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.
>>>
Esto también se puede hacer pasando la tabla como argumentos de palabras clave
con la notación '**'.
>>>
>>>
(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'
>>>
>>>
>>> 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.
>>>
>>> # 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.
>>>
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
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:
>>>
Si desea leer todas las líneas de un archivo en una lista, también puede
usar list(f)o f.readlines().
>>>
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)
>>>
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()
Nota
Si tiene un objeto x, puede ver su representación de cadena JSON con una simple
línea de código:
>>>
x = json.load(f)
Ver también
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 .
>>>
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).
>>>
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:
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
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:
>>>
>>> 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.
>>>
>>>
>>>
>>> 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
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
"""
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
"""
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 .
>>>
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Por ejemplo:
>>>
>>>
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.
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
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.
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
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.
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"
scope_test()
print("In global scope:", spam)
También puede ver que no había ningún enlace previo para el spam antes de
la globalasignación.
class ClassName:
<statement-1>
.
.
.
<statement-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).
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'
crea una nueva instancia de la clase y asigna este objeto a la variable local x.
def __init__(self):
self.data = []
x = MyClass()
>>>
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.
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print(x.counter)
del x.counter
x.f()
xf = x.f
while True:
print(xf())
seguirá imprimiendo hasta el final de los tiempos.hello world
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:
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:
>>> 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']
class Dog:
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
>>>
>>> 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.
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:
class C:
f = f1
def g(self):
return 'hello world'
h = g
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>
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).
Python también admite una forma de herencia múltiple. Una definición de clase con
varias clases base se ve así:
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)
class MappingSubclass(Mapping):
class Employee:
pass
9,8. Iteradores
A estas alturas, probablemente haya notado que la mayoría de los objetos
contenedores se pueden recorrer usando una fordeclaración:
>>>
>>> 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
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]
>>>
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]
>>>
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.
>>>
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.
>>> 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
>>>
>>> import os
>>> dir(os)
<returns a list of all module functions>
>>> help(os)
<returns an extensive manual page created from the module's
docstrings>
>>>
>>>
import argparse
>>>
>>>
>>> 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'
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
def average(values):
"""Computes the arithmetic mean of a list of numbers.
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)
>>>
>>>
>>>
>>>
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.
>>>
>>>
>>> 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))
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.
import struct
start += 16
filename = data[start:start+filenamesize]
start += filenamesize
extra = data[start:start+extra_size]
print(filename, hex(crc32), comp_size, uncomp_size)
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.
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)
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')
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:
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857')
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.
Para crear un entorno virtual, decida el directorio donde desea colocarlo y ejecute
el venvmódulo como un script con la ruta del directorio:
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.
En Windows, ejecute:
tutorial-env\Scripts\activate.bat
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']
>>>
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:
pip uninstall seguido de uno o más nombres de paquetes eliminará los paquetes del
entorno virtual.
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
Notas al pie
0.125
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.
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.
0.0001100110011001100110011001100110011001100110011...
>>>
>>> 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
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:
>>>
>>> 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:
>>>
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í:
>>>
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.
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.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
>>>
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
>>>
>>>
>>>
>>> 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!
>>>
>>> 0.1 * 2 ** 55
3602879701896397.0
>>>
>>> 3602879701896397 * 10 ** 55 // 2 ** 55
1000000000000000055511151231257827021181583404541015625
>>>
>>>
>>> Fraction.from_float(0.1)
Fraction(3602879701896397, 36028797018963968)
>>> (0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)
>>> Decimal.from_float(0.1)
Decimal('0.10000000000000000555111512312578270211815834045410156
25')
16. Apéndice
16.1. Modo interactivo
16.1.1. Manejo de errores
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.
$ chmod +x myscript.py
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.
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)
>>>
Notas al pie
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")
dado1=random.randint(1,6)
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)
# bloque principal
lista=cargar()
print("Lista generada aleatoriamente")
imprimir(lista)
mezclar(lista)
print("La misma lista luego de mezclar")
imprimir(lista)
Ver video
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.
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.
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 .
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.
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.
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.
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.
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.
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 -*-
__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/
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
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.
os.chdir("/home/japp/Documentos/")
os.getcwd()
# /home/japp/Documentos/
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")
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")
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.
¿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"
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 = []
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)]
shelf_file = shelve.open('datos')
shelf_file['numeros'] = numeros
shelf_file.close()
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')
print('uname:', platform.uname())
"""
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.copytree("/home/japp/Documentos", "/home/japp/Documentos-copia") #
Copia el directorio y su contenido
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.
fichero_zip.close()
fichero_zip = zipfile.ZipFile("datos.zip")
fichero_zip.namelist()
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()
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
import time
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"
# Cambio de idioma
import locale
hoy = datetime.date.today()
print('Hoy:', hoy)
un_dia = datetime.timedelta(days=1)
print('Lapso de un día:', one_day)
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)
hora_us_pacific = hora_utc.replace(tzinfo=timezone('US/Pacific'))
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')
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
"""
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
"""
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
ftp = ftplib.FTP("ftp.iac.es")
login = ftp.login("[email protected]", "anonymous")
ftp.cwd(destination_dir)
os.chdir(origin_dir)
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"
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
ssh.close()