Capítulo 1. Fundamentos de Python para DevOps
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
DevOps, la combinación del desarrollo de software con las operaciones de tecnología de la información, ha sido un campo candente durante la última década. Se han roto las fronteras tradicionales entre el desarrollo, la implementación, el mantenimiento y la garantía de calidad del software, lo que ha permitido la creación de equipos más integrados. Python ha sido un lenguaje popular tanto en las operaciones informáticas tradicionales como en DevOps debido a su combinación de flexibilidad, potencia y facilidad de uso.
El lenguaje de programación Python se hizo público a principios de los años 90 para su uso en la administración de sistemas. Ha tenido un gran éxito en este ámbito y ha conseguido una amplia adopción. Python es un lenguaje de programación de propósito general que se utiliza en casi todos los ámbitos. Las industrias de efectos visuales y del cine lo adoptaron. Más recientemente, se ha convertido en el lenguaje de facto de la ciencia de datos y el aprendizaje automático (AM), y se ha utilizado en sectores que van desde la aviación a la bioinformática. Python cuenta con un amplio arsenal de herramientas para cubrir las necesidades más diversas de sus usuarios. Aprender toda la Biblioteca Estándar de Python (las capacidades que vienen con cualquier instalación de Python) sería una tarea desalentadora. Intentar aprender todos los paquetes de terceros que animan el ecosistema Python sería una empresa inmensa. La buena noticia es que no necesitas hacer esas cosas. Puedes convertirte en un poderoso profesional de DevOps aprendiendo sólo un pequeño subconjunto de Python.
En este capítulo, nos basamos en nuestras décadas de experiencia en Python DevOps para enseñar sólo los elementos del lenguaje que necesitas. Éstas son las partes de Python DevOps que se utilizan a diario. Forman la caja de herramientas esencial para hacer las cosas. Una vez que domines estos conceptos básicos, podrás añadir herramientas más complicadas, como verás en capítulos posteriores.
Instalar y ejecutar Python
Si quieres probar el código de este resumen, necesitas tener instalado Python 3.7 o posterior (la última versión es la 3.8.0 en el momento de escribir esto) y acceso a una shell. En macOS X, Windows y la mayoría de las distribuciones de Linux, puedes abrir la aplicación terminal para acceder a una shell. Para ver qué versión de Python estás utilizando, abre un intérprete de comandos y escribe python
--version
:
$
python --version
Python 3.8.0
Los instaladores de Python pueden descargarse directamente del sitio web Python.org. También puedes utilizar un gestor de paquetes como Apt, RPM, MacPorts, Homebrew, Chocolatey o muchos otros.
La cáscara de Python
La forma más sencilla de ejecutar Python es utilizar el intérprete interactivo incorporado. Sólo tienes que escribir python
en un intérprete de comandos. Entonces podrás ejecutar interactivamente sentencias Python. Escribe exit()
para salir del intérprete.
$ python Python 3.8.0 (default, Sep 23 2018, 09:47:03) [Clang 9.0.0 (clang-900.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> 1 + 2 3 >>> exit()
Scripts en Python
El código Python se ejecuta desde un archivo con extensión .py:
# This is my first Python script print('Hello world!')
Guarda este código en un archivo llamado hola.py. Para invocar el script, en un intérprete de comandos ejecuta python
seguido del nombre del archivo:
$ python hello.py Hello world!
Los scripts de Python son la forma en que se ejecuta la mayor parte del código Python de producción.
IPython
Además del intérprete de comandos interactivo incorporado, hay varios intérpretes de comandos interactivos de terceros que ejecutan código Python. Una de las más populares es IPython. IPython ofrece introspección (la capacidad de obtener dinámicamente información sobre objetos), resaltado de sintaxis, comandos mágicos especiales (que trataremos más adelante en este capítulo) y muchas más funciones, lo que hace que sea un placer utilizarlo para explorar Python. Para instalar IPython, utiliza el gestor de paquetes de Python, pip
:
$
pip install ipython
Ejecutarlo es similar a ejecutar el intérprete de comandos interactivo incorporado descrito en la sección anterior:
$ ipython Python 3.8.0 (default, Sep 23 2018, 09:47:03) Type 'copyright', 'credits' or 'license' for more information IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: print('Hello') Hello In [2]: exit()
Cuadernos Jupyter
El proyecto Jupyter, derivado del proyecto iPython, permite crear documentos que contengan texto, código y visualizaciones. Estos documentos son potentes herramientas para combinar código en ejecución, resultados y texto formateado. Jupyter permite la entrega de documentación junto con el código. Ha alcanzado una gran popularidad, especialmente en el mundo de la ciencia de datos. A continuación te explicamos cómo instalar y ejecutar los cuadernos Jupyter:
$
pip install jupyter$
jupyter notebook
Este comando abre una pestaña del navegador web que muestra el directorio de trabajo actual. Desde aquí, puedes abrir las libretas existentes en el proyecto actual o crear otras nuevas.
Programación procedimental
Si has estado en contacto con la programación, probablemente habrás oído términos como programación orientada a objetos (POO) y programación funcional. Se trata de diferentes paradigmas arquitectónicos utilizados para organizar programas. Uno de los paradigmas más básicos, la programación procedimental, es un excelente punto de partida. La programación procedimental es la emisión de instrucciones a un ordenador en una secuencia ordenada:
>>>
i
=
3
>>>
j
=
i
+
1
>>>
i
+
j
7
Como puedes ver en este ejemplo, hay tres sentencias que se ejecutan en orden desde la primera línea hasta la última. Cada sentencia utiliza el estado producido por las anteriores. En este caso, la primera sentencia asigna el valor 3 a una variable llamada i
. En la segunda sentencia, el valor de esta variable se utiliza para asignar un valor a una variable llamada j
, y en la tercera sentencia, se suman los valores de ambas variables. No te preocupes todavía por los detalles de estas sentencias; fíjate en que se ejecutan en orden y se basan en el estado creado por las sentencias anteriores.
Variables
Una variable es un nombre que apunta a algún valor. En el ejemplo anterior, las variables son i
y j
. Las variables en Python pueden asignarse a nuevos valores:
>>>
dog_name
=
'spot'
>>>
dog_name
'spot'
>>>
dog_name
=
'rex'
>>>
dog_name
'rex'
>>>
dog_name
=
't-'
+
dog_name
>>>
dog_name
't-rex'
>>>
Las variables de Python utilizan tipado dinámico. En la práctica, esto significa que pueden reasignarse a valores de distintos tipos o clases:
>>>
big
=
'large'
>>>
big
'large'
>>>
big
=
1000
*
1000
>>>
big
1000000
>>>
big
=
{}
>>>
big
{}
>>>
Aquí la misma variable se asigna a una cadena, a un número y a un diccionario. Las variables pueden reasignarse a valores de cualquier tipo.
Matemáticas básicas
Las operaciones matemáticas básicas como la suma, la resta, la multiplicación y la división se pueden realizar utilizando los operadores matemáticos incorporados:
>>>
1
+
1
2
>>>
3
-
4
–
1
>>>
2
*
5
10
>>>
2
/
3
0.6666666666666666
Ten en cuenta que el símbolo //
es para la división de enteros. El símbolo **
crea un exponente, y %
es el operador de módulo:
>>>
5
/
2
2.5
>>>
5
//
2
2
>>>
3
**
2
9
>>>
5
%
2
1
Comentarios
Los comentarios son texto ignorado por el intérprete de Python. Son útiles para documentar el código y pueden ser aprovechados por algunos servicios para proporcionar documentación independiente. Los comentarios de una sola línea se delimitan anteponiendo #
. Un comentario de una sola línea puede empezar al principio de una línea o en cualquier punto posterior. Todo lo que sigue a #
forma parte del comentario hasta que se produce un nuevo salto de línea:
# This is a comment
1
+
1
# This comment follows a statement
Los comentarios multilínea se encierran a su vez en bloques que comienzan y terminan con """
o '''
:
"""
This statement is a block comment.
It can run for multiple lines
"""
'''
This statement is also a block comment
'''
Funciones incorporadas
Las funciones son sentencias agrupadas como una unidad. Invocas una función escribiendo el nombre de la función, seguido de un paréntesis. Si la función tiene argumentos, éstos aparecen dentro de los paréntesis. Python tiene muchas funciones incorporadas. Dos de las funciones incorporadas más utilizadas son print
y range
.
Imprimir
La función print
produce una salida que el usuario de un programa puede ver. Es menos relevante en entornos interactivos, pero es una herramienta fundamental a la hora de escribir scripts en Python. En el ejemplo anterior, el argumento de la función print
se escribe como salida cuando se ejecuta el script:
# This is my first Python script
(
"Hello world!"
)
$
python
hello
.
py
Hello
world
!
print
se puede utilizar para ver el valor de una variable o para dar información sobre el estado de un programa. print
generalmente da salida al flujo de salida estándar y es visible como salida del programa en un intérprete de comandos.
Gama
Aunque range
es una función incorporada, técnicamente no es una función en absoluto. Es un tipo que representa una secuencia de números. Al llamar al constructor range()
, se devuelve un objeto que representa una secuencia de números. Los objetos Rango cuentan a través de una secuencia de números. La función range
admite hasta tres argumentos enteros. Si sólo aparece un argumento, la secuencia estará representada por los números desde cero hasta ese número, pero sin incluirlo. Si aparece un segundo argumento, éste representa el punto de partida, en lugar de la opción por defecto de empezar desde 0. El tercer argumento se puede utilizar para especificar la distancia entre pasos, y por defecto es 1.
>>>
range
(
10
)
range
(
0
,
10
)
>>>
list
(
range
(
10
))
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
>>>
list
(
range
(
5
,
10
))
[
5
,
6
,
7
,
8
,
9
]
>>>
list
(
range
(
5
,
10
,
3
))
[
5
,
8
]
>>>
range
mantiene una huella de memoria pequeña, incluso en secuencias largas, ya que sólo almacena los valores de inicio, parada y paso. La función range
puede iterar a través de secuencias largas de números sin limitaciones de rendimiento.
Control de ejecución
Python tiene muchas construcciones para controlar el flujo de ejecución de sentencias. Puedes agrupar sentencias que desees ejecutar juntas como un bloque de código. Estos bloques pueden ejecutarse varias veces utilizando los bucles for
y while
o ejecutarse sólo en determinadas condiciones utilizando las sentencias if
, los bucles while
o los bloques try-except
. Utilizar estas construcciones es el primer paso para aprovechar el poder de la programación. Los distintos lenguajes delimitan los bloques de código utilizando diferentes convenciones. Muchos lenguajes con sintaxis similar al lenguaje C (un lenguaje muy influyente utilizado en la escritura de Unix) utilizan llaves alrededor de un grupo de sentencias para definir un bloque. En Python, se utiliza la indentación para indicar un bloque. Las sentencias se agrupan por indentación en bloques que se ejecutan como una unidad.
Nota
Al intérprete de Python no le importa si utilizas tabuladores o espacios para la indentación, siempre que seas coherente. Sin embargo, la guía de estilo de Python, PEP-8, recomienda utilizar cuatro espacios en blanco para cada nivel de indentación.
if/elif/else
if/elif/else
son formas comunes de bifurcarse entre decisiones en el código. Un bloque directamente después de una sentencia se ejecuta si esa sentencia se evalúa como : if
True
>>>
i
=
45
>>>
if
i
==
45
:
...
(
'i is 45'
)
...
...
i
is
45
>>>
Aquí utilizamos el operador ==
, que devuelve True
si los elementos son iguales y False
si no lo son. Opcionalmente, este bloque puede seguir a una sentencia elif
o else
con un bloque de acompañamiento. En el caso de una sentencia elif
, este bloque sólo se ejecuta si elif
se evalúa como True
:
>>>
i
=
35
>>>
if
i
==
45
:
...
(
'i is 45'
)
...
elif
i
==
35
:
...
(
'i is 35'
)
...
...
i
is
35
>>>
Múltiples bucles elif
pueden agregarse juntos. Si estás familiarizado con las sentencias switch
de otros lenguajes, esto simula el mismo comportamiento de elegir entre varias opciones. Añadir una sentencia else
al final ejecuta un bloque si ninguna de las otras condiciones se evalúa como True
:
>>>
i
=
0
>>>
if
i
==
45
:
...
(
'i is 45'
)
...
elif
i
==
35
:
...
(
'i is 35'
)
...
elif
i
>
10
:
...
(
'i is greater than 10'
)
...
elif
i
%
3
==
0
:
...
(
'i is a multiple of 3'
)
...
else
:
...
(
'I don'
t
know
much
about
i
...
')
...
...
i
is
a
multiple
of
3
>>>
Puedes anidar sentencias if
, creando bloques que contengan sentencias if
que sólo se ejecuten si una sentencia if
externa es True
:
>>>
cat
=
'spot'
>>>
if
's'
in
cat
:
...
(
"Found an 's' in a cat"
)
...
if
cat
==
'Sheba'
:
...
(
"I found Sheba"
)
...
else
:
...
(
"Some other cat"
)
...
else
:
...
(
" a cat without 's'"
)
...
...
Found
an
's'
in
a
cat
Some
other
cat
>>>
para Bucles
for
Los bucles te permiten repetir un bloque de sentencias (un bloque de código) una vez por cada miembro de una secuencia (grupo ordenado de elementos). A medida que iteras por la secuencia, el bloque de código puede acceder al elemento actual. Uno de los usos más comunes de los bucles es iterar a través de un objeto para realizar una tarea un número determinado de veces: range
>>>
for
i
in
range
(
10
):
...
x
=
i
*
2
...
(
x
)
...
...
0
2
4
6
8
10
12
14
16
18
>>>
En este ejemplo, nuestro bloque de código es el siguiente:
...
x
=
i
*
2
...
(
x
)
Repetimos este código 10 veces, cada vez asignando la variable i
al siguiente número de la secuencia de enteros del 0-9. Los bucles for
pueden utilizarse para iterar a través de cualquiera de los tipos de secuencia de Python. Los verás más adelante en este capítulo.
Bucles while
while
los bucles repiten un bloque mientras una condición se evalúe como : True
>>>
count
=
0
>>>
while
count
<
3
:
...
(
f
"The count is {count}"
)
...
count
+=
1
...
...
The
count
is
0
The
count
is
1
The
count
is
2
>>>
Es esencial definir una forma de que tu bucle termine. De lo contrario, te quedarás atrapado en el bucle hasta que tu programa se bloquee. Una forma de hacerlo es definir tu sentencia condicional de modo que al final se evalúe como False
. Un patrón alternativo utiliza la sentencia break
para salir de un bucle mediante una condicional anidada:
>>>
count
=
0
>>>
while
True
:
...
(
f
"The count is {count}"
)
...
if
count
>
5
:
...
break
...
count
+=
1
...
...
The
count
is
0
The
count
is
1
The
count
is
2
The
count
is
3
The
count
is
4
The
count
is
5
The
count
is
6
>>>
Manejo de excepciones
Las excepciones son un tipo de error que hace que tu programa se bloquee si no se maneja (atrapa). Atraparlas con un bloque try-except
permite que el programa continúe. Estos bloques se crean indentando el bloque en el que podría producirse la excepción, poniendo una sentencia try
antes de él y una sentencia except
después, seguidas de un bloque de código que debería ejecutarse cuando se produzca el error:
>>>
thinkers
=
[
'Plato'
,
'PlayDo'
,
'Gumby'
]
>>>
while
True
:
...
try
:
...
thinker
=
thinkers
.
pop
()
...
(
thinker
)
...
except
IndexError
as
e
:
...
(
"We tried to pop too many thinkers"
)
...
(
e
)
...
break
...
...
...
Gumby
PlayDo
Plato
We
tried
to
pop
too
many
thinkers
pop
from
empty
list
>>>
Hay muchas excepciones incorporadas, como IOError
, KeyError
, y ImportError
. Muchos paquetes de terceros también definen sus propias clases de excepciones. Indican que algo ha ido muy mal, por lo que sólo merece la pena atraparlas si estás seguro de que el problema no será fatal para tu software. Puedes especificar explícitamente qué tipo de excepción atraparás. Lo ideal es que atrapes el tipo de excepción exacto (en nuestro ejemplo, se trataba de la excepción IndexError
).
Objetos incorporados
En esta visión general, no trataremos la programación orientada a objetos. Sin embargo, el lenguaje Python viene con bastantes clases incorporadas.
¿Qué es un objeto?
En la programación orientada a objetos, los datos o estado y la funcionalidad aparecen juntos. Los conceptos esenciales que hay que entender al trabajar con objetos son la instanciación de clases (crear objetos a partir de clases) y la sintaxis de puntos (la sintaxis para acceder a los atributos y métodos de un objeto). Una clase define atributos y métodos compartidos por sus objetos. Piensa en ella como el dibujo técnico de un modelo de coche. La clase puede instanciarse para crear una instancia. La instancia, u objeto, es un único coche construido a partir de esos dibujos.
>>>
# Define a class for fancy defining fancy cars
>>>
class
FancyCar
():
...
pass
...
>>>
type
(
FancyCar
)
<
class
'
type
'>
>>>
# Instantiate a fancy car
>>>
my_car
=
FancyCar
()
>>>
type
(
my_car
)
<
class
'
__main__
.
FancyCar
'>
No necesitas preocuparte por crear tus propias clases en este punto. Sólo tienes que entender que cada objeto es una instanciación de una clase.
Métodos y atributos de los objetos
Los objetos almacenan datos en atributos. Estos atributos son variables adjuntas al objeto o a la clase del objeto. Los objetos definen la funcionalidad en métodos de objeto (métodos definidos para todos los objetos de una clase) y métodos de clase (métodos adjuntos a una clase y compartidos por todos los objetos de la clase), que son funciones adjuntas al objeto.
Nota
En la documentación de Python, las funciones adjuntas a objetos y clases se denominan métodos.
Estas funciones tienen acceso a los atributos del objeto y pueden modificar y utilizar sus datos. Para llamar a un método de un objeto o acceder a uno de sus atributos, utilizamos la sintaxis de puntos :
>>>
# Define a class for fancy defining fancy cars
>>>
class
FancyCar
():
...
# Add a class variable
...
wheels
=
4
...
# Add a method
...
def
driveFast
(
self
):
...
(
"Driving so fast"
)
...
...
...
>>>
# Instantiate a fancy car
>>>
my_car
=
FancyCar
()
>>>
# Access the class attribute
>>>
my_car
.
wheels
4
>>>
# Invoke the method
>>>
my_car
.
driveFast
()
Driving
so
fast
>>>
Así que aquí nuestra clase FancyCar
define un método llamado driveFast
y un atributo wheels
. Cuando instancias una instancia de FancyCar
llamada my_car
, puedes acceder al atributo e invocar al método utilizando la sintaxis de puntos.
Secuencias
Las secuencias son una familia de tipos incorporados, que incluyen los tipos lista, tupla, rango, cadena y binario. Las secuencias representan colecciones ordenadas y finitas de elementos.
Secuencia de operaciones
Hay muchas operaciones que funcionan en todos los tipos de secuencias. Aquí cubrimos algunas de las operaciones más utilizadas.
Puedes utilizar los operadores in
y not in
para comprobar si un elemento existe o no en una secuencia:
>>>
2
in
[
1
,
2
,
3
]
True
>>>
'a'
not
in
'cat'
False
>>>
10
in
range
(
12
)
True
>>>
10
not
in
range
(
2
,
4
)
True
Puedes hacer referencia al contenido de una secuencia utilizando su número de índice. Para acceder al elemento en algún índice, utiliza corchetes con el número de índice como argumento. El primer elemento indexado está en la posición 0, el segundo en la 1, y así sucesivamente hasta el número uno menos que el número de elementos:
>>>
my_sequence
=
'Bill Cheatham'
>>>
my_sequence
[
0
]
'B'
>>>
my_sequence
[
2
]
'l'
>>>
my_sequence
[
12
]
'm'
La indexación puede aparecer desde el final de una secuencia, en lugar de desde el principio, utilizando números negativos. El último elemento tiene el índice -1, el penúltimo tiene el índice -2, y así sucesivamente:
>>>
my_sequence
=
"Bill Cheatham"
>>>
my_sequence
[
–
1
]
'm'
>>>
my_sequence
[
–
2
]
'a'
>>>
my_sequence
[
–
13
]
'B'
El índice de un elemento es el resultado del método index
. Por defecto, devuelve el índice de la primera aparición del elemento, pero los argumentos opcionales pueden definir un subrango en el que buscar:
>>>
my_sequence
=
"Bill Cheatham"
>>>
my_sequence
.
index
(
'C'
)
5
>>>
my_sequence
.
index
(
'a'
)
8
>>>
my_sequence
.
index
(
'a'
,
9
,
12
)
11
>>>
my_sequence
[
11
]
'a'
>>>
Puedes producir una nueva secuencia a partir de una secuencia utilizando el troceado. Una rebanada aparece invocando una secuencia con corchetes que contienen argumentos opcionales start
, stop
y step
:
my_sequence[start:stop:step]
start
es el índice del primer elemento a utilizar en la nueva secuencia, stop
el primer índice más allá de ese punto, y step
, la distancia entre elementos. Todos estos argumentos son opcionales y se sustituyen por valores por defecto si se omiten. Esta sentencia produce una copia de la secuencia original. El valor por defecto para start
es 0, para stop
es la longitud de la secuencia, y para step
es 1. Ten en cuenta que si el paso no aparece, también se puede omitir el : correspondiente:
>>>
my_sequence
=
[
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
]
>>>
my_sequence
[
2
:
5
]
[
'c'
,
'd'
,
'e'
]
>>>
my_sequence
[:
5
]
[
'a'
,
'b'
,
'c'
,
'd'
,
'e'
]
>>>
my_sequence
[
3
:]
[
'd'
,
'e'
,
'f'
,
'g'
]
>>>
Los números negativos pueden utilizarse para indexar hacia atrás:
>>>
my_sequence
[
–
6
:]
[
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
]
>>>
my_sequence
[
3
:
–
1
]
[
'd'
,
'e'
,
'f'
]
>>>
Las secuencias comparten muchas operaciones para obtener información sobre ellas y su contenido. len
devuelve la longitud de la secuencia, min
el miembro más pequeño, max
el más grande y count
el número de un elemento concreto. min
y max
sólo funcionan en secuencias con elementos que sean comparables. Recuerda que funcionan con cualquier tipo de secuencia:
>>>
my_sequence
=
[
0
,
1
,
2
,
0
,
1
,
2
,
3
,
0
,
1
,
2
,
3
,
4
]
>>>
len
(
my_sequence
)
12
>>>
min
(
my_sequence
)
0
>>>
max
(
my_sequence
)
4
>>>
my_sequence
.
count
(
1
)
3
>>>
Listas
Las listas, una de las estructuras de datos de Python más utilizadas, representan una colección ordenada de elementos de cualquier tipo. El uso de corchetes indica una sintaxis de lista.
La función list()
puede utilizarse para crear una lista vacía o una lista basada en otro objeto iterable finito (como otra secuencia):
>>>
list
()
[]
>>>
list
(
range
(
10
))
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
>>>
list
(
"Henry Miller"
)
[
'H'
,
'e'
,
'n'
,
'r'
,
'y'
,
' '
,
'M'
,
'i'
,
'l'
,
'l'
,
'e'
,
'r'
]
>>>
Las listas creadas utilizando directamente corchetes son la forma más habitual. En este caso, los elementos de la lista deben enumerarse explícitamente. Recuerda que los elementos de una lista pueden ser de distintos tipos:
>>>
empty
=
[]
>>>
empty
[]
>>>
nine
=
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
>>>
nine
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
>>>
mixed
=
[
0
,
'a'
,
empty
,
'WheelHoss'
]
>>>
mixed
[
0
,
'a'
,
[],
'WheelHoss'
]
>>>
La forma más eficaz de añadir un único elemento a una lista es append
el elemento al final de la lista. Un método menos eficaz, insert
, te permite insertar un elemento en la posición de índice que elijas:
>>>
pies
=
[
'cherry'
,
'apple'
]
>>>
pies
[
'cherry'
,
'apple'
]
>>>
pies
.
append
(
'rhubarb'
)
>>>
pies
[
'cherry'
,
'apple'
,
'rhubarb'
]
>>>
pies
.
insert
(
1
,
'cream'
)
>>>
pies
[
'cherry'
,
'cream'
,
'apple'
,
'rhubarb'
]
>>>
El contenido de una lista puede añadirse a otra utilizando el método extend
:
>>>
pies
[
'cherry'
,
'cream'
,
'apple'
,
'rhubarb'
]
>>>
desserts
=
[
'cookies'
,
'paste'
]
>>>
desserts
[
'cookies'
,
'paste'
]
>>>
desserts
.
extend
(
pies
)
>>>
desserts
[
'cookies'
,
'paste'
,
'cherry'
,
'cream'
,
'apple'
,
'rhubarb'
]
>>>
La forma más eficaz y habitual de eliminar el último elemento de una lista y devolver su valor es pop
. Se puede proporcionar un argumento de índice a este método, eliminando y devolviendo el elemento en ese índice. Esta técnica es menos eficaz, ya que hay que volver a indexar la lista:
>>>
pies
[
'cherry'
,
'cream'
,
'apple'
,
'rhubarb'
]
>>>
pies
.
pop
()
'rhubarb'
>>>
pies
[
'cherry'
,
'cream'
,
'apple'
]
>>>
pies
.
pop
(
1
)
'cream'
>>>
pies
[
'cherry'
,
'apple'
]
También existe un método remove
, que elimina la primera aparición de un elemento.
>>>
pies
.
remove
(
'apple'
)
>>>
pies
[
'cherry'
]
>>>
Una de las funciones más potentes e idiomáticas de Python, las comprensiones de listas, te permiten utilizar la funcionalidad de un bucle for
en una sola línea. Veamos un ejemplo sencillo, empezando con un bucle for
que eleva al cuadrado todos los números del 0 al 9 y los añade a una lista:
>>>
squares
=
[]
>>>
for
i
in
range
(
10
):
...
squared
=
i
*
i
...
squares
.
append
(
squared
)
...
...
>>>
squares
[
0
,
1
,
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
]
>>>
Para sustituirlo por una comprensión de lista, hacemos lo siguiente:
>>>
squares
=
[
i
*
i
for
i
in
range
(
10
)]
>>>
squares
[
0
,
1
,
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
]
>>>
Ten en cuenta que la funcionalidad del bloque interno se pone en primer lugar, seguida de la declaración for
. También puedes añadir condicionales a las comprensiones de listas, filtrando los resultados:
>>>
squares
=
[
i
*
i
for
i
in
range
(
10
)
if
i
%
2
==
0
]
>>>
squares
[
0
,
4
,
16
,
36
,
64
]
>>>
Otras técnicas para la comprensión de listas son el anidamiento y el uso de múltiples variables, pero la forma más directa que se muestra aquí es la más común.
Cuerdas
El tipo de secuencia cadena es una colección de caracteres ordenados rodeados de comillas. A partir de Python 3, las cadenas utilizan por defecto la codificación UTF-8.
Puedes crear cadenas utilizando el método constructor de cadenas str(),
o encerrando directamente el texto entre comillas:
>>>
str
()
''
>>>
"some new string!"
'some new string!'
>>>
'or with single quotes'
'or with single quotes'
El constructor de cadenas puede utilizarse para crear cadenas a partir de otros objetos:
>>>
my_list
=
list
()
>>>
str
(
my_list
)
'[]'
Puedes crear cadenas de varias líneas utilizando comillas triples alrededor del contenido:
>>>
multi_line
=
"""This is a
... multi-line string,
... which includes linebreaks.
... """
>>>
(
multi_line
)
This
is
a
multi
-
line
string
,
which
includes
linebreaks
.
>>>
Además de los métodos que comparten todas las secuencias, las cadenas tienen bastantes métodos propios de su clase.
Es relativamente habitual que el texto del usuario tenga espacios en blanco al final o al principio. Si alguien escribe "sí" en un formulario en lugar de "sí", normalmente querrás tratarlos igual. Las cadenas de Python tienen un método strip
para este caso. Devuelve una cadena con los espacios en blanco eliminados del principio y del final. También hay métodos para eliminar los espacios en blanco sólo de la parte derecha o izquierda de la cadena:
>>>
input
=
" I want more "
>>>
input
.
strip
()
'I want more'
>>>
input
.
rstrip
()
' I want more'
>>>
input
.
lstrip
()
'I want more '
Por otro lado, si quieres añadir relleno a una cadena, puedes utilizar los métodos ljust
o rjust
. Cualquiera de ellos rellena con espacios en blanco por defecto, o toma un argumento de carácter :
>>>
output
=
'Barry'
>>>
output
.
ljust
(
10
)
'Barry '
>>>
output
.
rjust
(
10
,
'*'
)
'*****Barry'
A veces quieres dividir una cadena en una lista de subcadenas. Tal vez tengas una frase que quieras convertir en una lista de palabras, o una cadena de palabras separadas por comas. El método split
descompone una cadena en una lista de cadenas. Por defecto, utiliza espacios en blanco como token para hacer las rupturas. Se puede utilizar un argumento opcional para añadir otro carácter en el que pueda romperse la división:
>>>
text
=
"Mary had a little lamb"
>>>
text
.
split
()
[
'Mary'
,
'had'
,
'a'
,
'little'
,
'lamb'
]
>>>
url
=
"gt.motomomo.io/v2/api/asset/143"
>>>
url
.
split
(
'/'
)
[
'gt.motomomo.io'
,
'v2'
,
'api'
,
'asset'
,
'143'
]
Puedes crear fácilmente una nueva cadena a partir de una secuencia de cadenas y join
convertirlas en una sola cadena. Este método inserta una cadena como separador entre una lista de otras cadenas:
>>>
items
=
[
'cow'
,
'milk'
,
'bread'
,
'butter'
]
>>>
" and "
.
join
(
items
)
'cow and milk and bread and butter'
Cambiar las mayúsculas y minúsculas de un texto es algo habitual, tanto si se trata de uniformizar las mayúsculas y minúsculas para la comparación como de cambiarlas en preparación para el consumo del usuario. Las cadenas de Python tienen varios métodos para hacer de esto un proceso fácil:
>>>
name
=
"bill monroe"
>>>
name
.
capitalize
()
'Bill monroe'
>>>
name
.
upper
()
'BILL MONROE'
>>>
name
.
title
()
'Bill Monroe'
>>>
name
.
swapcase
()
'BILL MONROE'
>>>
name
=
"BILL MONROE"
>>>
name
.
lower
()
'bill monroe'
Python también proporciona métodos para comprender el contenido de una cadena. Ya sea para comprobar el caso del texto, o para ver si representa un número, hay bastantes métodos incorporados para interrogar. Aquí tienes algunos de los métodos más utilizados:
>>>
"William"
.
startswith
(
'W'
)
True
>>>
"William"
.
startswith
(
'Bill'
)
False
>>>
"Molly"
.
endswith
(
'olly'
)
True
>>>
"abc123"
.
isalnum
()
True
>>>
"abc123"
.
isalpha
()
False
>>>
"abc"
.
isalnum
()
True
>>>
"123"
.
isnumeric
()
True
>>>
"Sandy"
.
istitle
()
True
>>>
"Sandy"
.
islower
()
False
>>>
"SANDY"
.
isupper
()
True
Puedes insertar contenido en una cadena y controlar su formato en tiempo de ejecución. Tu programa puede utilizar los valores de variables u otro contenido calculado en cadenas. Este enfoque se utiliza tanto para crear texto consumido por el usuario como para escribir registros de software.
La forma más antigua de formatear cadenas en Python procede de la función printf
del lenguaje C. Puedes utilizar el operador de módulo, %
, para insertar valores formateados en una cadena. Esta técnica se aplica a la forma string % values
, donde los valores pueden ser una única no-tupla o una tupla de múltiples valores. La propia cadena debe tener un especificador de conversión para cada valor. El especificador de conversión, como mínimo, comienza con un %
y va seguido de un carácter que representa el tipo de valor insertado:
>>>
"
%s
+
%s
=
%s
"
%
(
1
,
2
,
"Three"
)
'1 + 2 = Three'
>>>
Los argumentos de formato adicionales incluyen el especificador de conversión. Por ejemplo, puedes controlar el número de posiciones que imprime un flotante, %f
:
>>>
"
%.3f
"
%
1.234567
'1.235'
Este mecanismo de formateo de cadenas fue el dominante en Python durante años, y lo encuentras en el código heredado. Este enfoque ofrece algunas características atractivas, como compartir sintaxis con otros lenguajes. También tiene algunos escollos. En particular, debido al uso de una secuencia para contener los argumentos, son frecuentes los errores relacionados con la visualización de los objetos tuple
y dict
. Recomendamos adoptar opciones de formato más modernas, como el método de cadena format
, las cadenas de plantilla y las cadenas f, tanto para evitar estos errores como para aumentar la sencillez y legibilidad de tu código.
Python 3 introdujo una nueva forma de dar formato a las cadenas utilizando el método de cadena format
. Esta forma de formatear también se ha trasladado a Python 2. Esta especificación utiliza llaves en la cadena para indicar campos de sustitución, en lugar de los especificadores de conversión basados en módulos del formato antiguo. Los valores de inserción se convierten en argumentos del método de cadena format
. El orden de los argumentos determina su orden de colocación en la cadena de destino:
>>>
'{} comes before {}'
.
format
(
'first'
,
'second'
)
'first comes before second'
>>>
Puedes especificar números de índice entre paréntesis para insertar valores en un orden distinto al de la lista de argumentos. También puedes repetir un valor especificando el mismo número de índice en varios campos de sustitución:
>>>
'{1} comes after {0}, but {1} comes before {2}'
.
format
(
'first'
,
'second'
,
'third'
)
'second comes after first, but second comes before third'
>>>
Una característica aún más potente es que los valores de inserción se pueden especificar por nombre:
>>>
'''{country} is an island.
... {country} is off of the coast of
... {continent} in the {ocean}'''
.
format
(
ocean
=
'Indian Ocean'
,
...
continent
=
'Africa'
,
...
country
=
'Madagascar'
)
'Madagascar is an island.
Madagascar
is
off
of
the
coast
of
Africa
in
the
Indian
Ocean
'
Aquí funciona un dict
para suministrar los valores clave de los campos de sustitución basados en nombres:
>>>
values
=
{
'first'
:
'Bill'
,
'last'
:
'Bailey'
}
>>>
"Won't you come home {first} {last}?"
.
format
(
**
values
)
"Won't you come home Bill Bailey?"
También puedes especificar argumentos de especificación de formato. Aquí añaden relleno izquierdo y derecho utilizando >
y <
. En el segundo ejemplo, especificamos un carácter para utilizar en el relleno:
>>>
text
=
"|{0:>22}||{0:<22}|"
>>>
text
.
format
(
'O'
,
'O'
)
'| O||O |'
>>>
text
=
"|{0:<>22}||{0:><22}|"
>>>
text
.
format
(
'O'
,
'O'
)
'|<<<<<<<<<<<<<<<<<<<<<O||O>>>>>>>>>>>>>>>>>>>>>|'
Las especificaciones de formato se realizan utilizando el minilenguaje de especificación de formato. Nuestro tema también utiliza otro tipo de lenguaje llamado cadenas f.
Las cadenas f de Python utilizan el mismo lenguaje de formateo que el método format
, pero ofrecen un mecanismo más sencillo e intuitivo para utilizarlas. Las cadenas f se anteponen con f o F antes de la primera comilla. Al igual que la cadena format
descrita anteriormente, las cadenas f utilizan llaves para delimitar los campos de sustitución. Sin embargo, en una cadena f, el contenido del campo de sustitución es una expresión. Este enfoque significa que puede referirse a variables definidas en el ámbito actual o implicar cálculos:
>>>
a
=
1
>>>
b
=
2
>>>
f
"a is {a}, b is {b}. Adding them results in {a + b}"
'a is 1, b is 2. Adding them results in 3'
Al igual que en las cadenas format
, las especificaciones de formato en las cadenas f ocurren dentro de las llaves después de la expresión del valor y comienzan con un :
:
>>>
count
=
43
>>>
f
"|{count:5d}"
'| 43'
La expresión del valor puede contener expresiones anidadas, variables de referencia y expresiones en la construcción de la expresión padre:
>>>
padding
=
10
>>>
f
"|{count:{padding}d}"
'| 43'
Consejo
Te recomendamos encarecidamente que utilices cadenas f para la mayoría de tus formateos de cadenas. Combinan la potencia del minilenguaje de especificación con una sintaxis sencilla e intuitiva.
Las plantillas de cadenas están diseñadas para ofrecer un mecanismo sencillo de sustitución de cadenas. Estos métodos incorporados funcionan para tareas como la internacionalización, donde son necesarias sustituciones simples de palabras. Utilizan $
como carácter de sustitución, con llaves opcionales alrededor. Los caracteres que siguen directamente a $
identifican el valor que se va a insertar. Cuando se ejecuta el método substitute
de la plantilla de cadenas, estos nombres se utilizan para asignar valores.
Nota
Los tipos y funciones incorporados están disponibles siempre que ejecutes código Python, pero para acceder al mundo más amplio de funcionalidades disponibles en el ecosistema Python, necesitas utilizar la sentencia import
. Este enfoque te permite añadir funcionalidad de la Biblioteca Estándar de Python o de servicios de terceros a tu entorno. Puedes importar selectivamente partes de un paquete utilizando la palabra clave from
:
>>>
from
string
import
Template
>>>
greeting
=
Template
(
"$hello Mark Anthony"
)
>>>
greeting
.
substitute
(
hello
=
"Bonjour"
)
'Bonjour Mark Anthony'
>>>
greeting
.
substitute
(
hello
=
"Zdravstvuyte"
)
'Zdravstvuyte Mark Anthony'
>>>
greeting
.
substitute
(
hello
=
"Nǐn hǎo"
)
'Nǐn hǎo Mark Anthony'
Dicts
Aparte de las cadenas y las listas, los dictos pueden ser las clases incorporadas a Python más utilizadas. Un dict es un mapeo de claves a valores. La búsqueda de un valor concreto mediante una clave es muy eficaz y rápida. Las claves pueden ser cadenas, números, objetos personalizados o cualquier otro tipo no mutable.
Nota
Un objeto mutable es aquel cuyo contenido puede cambiar en su lugar. Las listas son un ejemplo primario; el contenido de la lista puede cambiar sin que cambie la identidad de la lista. Las cadenas no son mutables. Creas una cadena nueva cada vez que cambias el contenido de una existente.
Los dicts se representan como pares clave/valor separados por comas y rodeados de llaves. Los pares clave/valor constan de una clave, dos puntos (:) y, a continuación, un valor.
Puedes crear un objeto dict utilizando el constructor dict()
. Sin argumentos, crea un dict vacío. También acepta una secuencia de pares clave/valor como argumento:
>>>
map
=
dict
()
>>>
type
(
map
)
<
class
'
dict
'>
>>>
map
{}
>>>
kv_list
=
[[
'key-1'
,
'value-1'
],
[
'key-2'
,
'value-2'
]]
>>>
dict
(
kv_list
)
{
'key-1'
:
'value-1'
,
'key-2'
:
'value-2'
}
También puedes crear un dict directamente utilizando llaves:
>>>
map
=
{
'key-1'
:
'value-1'
,
'key-2'
:
'value-2'
}
>>>
map
{
'key-1'
:
'value-1'
,
'key-2'
:
'value-2'
}
Puedes acceder al valor asociado a una tecla utilizando la sintaxis de corchetes:
>>>
map
[
'key-1'
]
'value-1'
>>>
map
[
'key-2'
]
'value-2'
Puedes utilizar la misma sintaxis para establecer un valor. Si la clave no está en el dict, se añade como una nueva entrada. Si ya existe, el valor cambia al nuevo valor:
>>>
map
{
'key-1'
:
'value-1'
,
'key-2'
:
'value-2'
}
>>>
map
[
'key-3'
]
=
'value-3'
>>>
map
{
'key-1'
:
'value-1'
,
'key-2'
:
'value-2'
,
'key-3'
:
'value-3'
}
>>>
map
[
'key-1'
]
=
13
>>>
map
{
'key-1'
:
13
,
'key-2'
:
'value-2'
,
'key-3'
:
'value-3'
}
Si intentas acceder a una clave que no se ha definido en un dict, se lanzará una excepción KeyError
:
>>>
map
[
'key-4'
]
Traceback
(
most
recent
call
last
):
File
"<input>"
,
line
1
,
in
<
module
>
map
[
'key-4'
]
KeyError
:
'key-4'
Puedes comprobar si la clave existe en un dict utilizando la sintaxis in
que vimos con las secuencias. En el caso de los dicts, comprueba la existencia de claves:
>>>
if
'key-4'
in
map
:
...
(
map
[
'key-4'
])
...
else
:
...
(
'key-4 not there'
)
...
...
key
-
4
not
there
Una solución más intuitiva es utilizar el método get()
. Si no has definido una clave en un dict, devuelve un valor por defecto suministrado. Si no has proporcionado un valor por defecto, devuelve None
:
>>>
map
.
get
(
'key-4'
,
'default-value'
)
'default-value'
Utiliza del
para eliminar un par clave-valor de un dict:
>>>
del
(
map
[
'key-1'
])
>>>
map
{
'key-2'
:
'value-2'
,
'key-3'
:
'value-3'
}
El método keys()
devuelve un objeto dict_keys
con las claves del dict. El método values()
devuelve un objeto dict_values
, y el método items()
devuelve pares clave-valor. Este último método es útil para recorrer el contenido de un dict:
>>>
map
.
keys
()
dict_keys
([
'key-1'
,
'key-2'
])
>>>
map
.
values
()
dict_values
([
'value-1'
,
'value-2'
])
>>>
for
key
,
value
in
map
.
items
():
...
(
f
"{key}: {value}"
)
...
...
key
-
1
:
value
-
1
key
-
2
:
value
-
2
Parecidas a las comprensiones de listas, las comprensiones de dict son sentencias de una línea que devuelven un dict iterando a través de una secuencia:
>>>
letters
=
'abcde'
>>>
# mapping individual letters to their upper-case representations
>>>
cap_map
=
{
x
:
x
.
upper
()
for
x
in
letters
}
>>>
cap_map
[
'b'
]
'B'
Funciones
Ya has visto algunas funciones incorporadas de Python. Ahora pasa a escribir las tuyas propias. Recuerda que una función es un mecanismo para encapsular un bloque de código. Puedes repetir el comportamiento de este bloque en varios puntos sin tener que duplicar el código. Tu código estará mejor organizado, será más comprobable, mantenible y más fácil de entender.
Anatomía de una función
La primera línea de una definición de función empieza con la palabra clave def
, seguida del nombre de la función, los parámetros de la función entre paréntesis y, a continuación, :
. El resto de la función es un bloque de código y está sangrado:
def <FUNCTION NAME>(<PARAMETERS>): <CODE BLOCK>
Si se proporciona primero una cadena que utilice sintaxis multilínea en el bloque sangrado, actúa como documentación. Utilízala para describir lo que hace tu función, cómo funcionan los parámetros y qué se puede esperar que devuelva. Verás que estas docstrings son inestimables para comunicarte con los futuros usuarios de tu código. Varios programas y servicios también los utilizan para crear documentación. Proporcionar docstrings se considera una buena práctica y es muy recomendable:
>>>
def
my_function
():
...
'''This is a doc string.
...
... It should describe what the function does,
... what parameters work, and what the
... function returns.
... '''
Los argumentos de las funciones aparecen entre paréntesis tras el nombre de la función. Pueden ser posicionales o de palabra clave. Los argumentos posicionales utilizan el orden de los argumentos para asignar valor:
>>>
def
positioned
(
first
,
second
):
...
"""Assignment based on order."""
...
(
f
"first: {first}"
)
...
(
f
"second: {second}"
)
...
...
>>>
positioned
(
1
,
2
)
first
:
1
second
:
2
>>>
Con argumentos de palabra clave, asigna a cada argumento un valor por defecto:
>>>
def
keywords
(
first
=
1
,
second
=
2
):
...
'''Default values assigned'''
...
(
f
"first: {first}"
)
...
(
f
"second: {second}"
)
...
...
Los valores por defecto se utilizan cuando no se pasan valores durante la invocación de la función. Los parámetros de palabra clave se pueden llamar por su nombre durante la invocación de la función, en cuyo caso no importará el orden:
>>>
keywords
(
0
)
first
:
0
second
:
2
>>>
keywords
(
3
,
4
)
first
:
3
second
:
4
>>>
keywords
(
second
=
'one'
,
first
=
'two'
)
first
:
two
second
:
one
Al utilizar parámetros de palabra clave, todos los parámetros definidos después de un parámetro de palabra clave deben ser también parámetros de palabra clave. Todas las funciones devuelven un valor. La palabra clave return
se utiliza para establecer este valor. Si no se establece desde la definición de una función, ésta devuelve None
:
>>>
def
no_return
():
...
'''No return defined'''
...
pass
...
>>>
result
=
no_return
()
>>>
(
result
)
None
>>>
def
return_one
():
...
'''Returns 1'''
...
return
1
...
>>>
result
=
return_one
()
>>>
(
result
)
1
Funciones como objetos
Las funciones son objetos. Se pueden pasar o almacenar en estructuras de datos. Puedes definir dos funciones, ponerlas en una lista e iterar por la lista para invocarlas:
>>>
def
double
(
input
):
...
'''double input'''
...
return
input
*
2
...
>>>
double
<
function
double
at
0x107d34ae8
>
>>>
type
(
double
)
<
class
'
function
'>
>>>
def
triple
(
input
):
...
'''Triple input'''
...
return
input
*
3
...
>>>
functions
=
[
double
,
triple
]
>>>
for
function
in
functions
:
...
(
function
(
3
))
...
...
6
9
Funciones anónimas
Cuando necesites crear una función muy limitada, puedes crear una sin nombre (anónima) utilizando la palabra clave lambda
. En general, debes limitar su uso a situaciones en las que una función espera una función pequeña como argumento. En este ejemplo, tomas una lista de listas y la ordenas. El mecanismo de ordenación por defecto compara basándose en el primer elemento de cada sublista:
>>>
items
=
[[
0
,
'a'
,
2
],
[
5
,
'b'
,
0
],
[
2
,
'c'
,
1
]]
>>>
sorted
(
items
)
[[
0
,
'a'
,
2
],
[
2
,
'c'
,
1
],
[
5
,
'b'
,
0
]]
Para ordenar basándote en algo distinto de la primera entrada, puedes definir un método que devuelva la segunda entrada del elemento y pasarlo al parámetro key
de la función de ordenación:
>>>
def
second
(
item
):
...
'''return second entry'''
...
return
item
[
1
]
...
>>>
sorted
(
items
,
key
=
second
)
[[
0
,
'a'
,
2
],
[
5
,
'b'
,
0
],
[
2
,
'c'
,
1
]]
Con la palabra clave lambda
, puedes hacer lo mismo sin la definición completa de la función. Las lambdas funcionan con la palabra clave lambda
seguida de un nombre de parámetro, dos puntos y un valor de retorno:
lambda <PARAM>: <RETURN EXPRESSION>
Ordena utilizando lambdas, primero utilizando la segunda entrada y luego utilizando la tercera:
>>>
sorted
(
items
,
key
=
lambda
item
:
item
[
1
])
[[
0
,
'a'
,
2
],
[
5
,
'b'
,
0
],
[
2
,
'c'
,
1
]]
>>>
sorted
(
items
,
key
=
lambda
item
:
item
[
2
])
[[
5
,
'b'
,
0
],
[
2
,
'c'
,
1
],
[
0
,
'a'
,
2
]]
Ten cuidado al utilizar lambdas de forma más general, ya que pueden crear código poco documentado y de lectura confusa si se utilizan en lugar de funciones generales.
Utilizar expresiones regulares
La necesidad de hacer coincidir patrones en cadenas surge una y otra vez. Podrías estar buscando un identificador en un archivo de registro o comprobando la entrada de un usuario en busca de palabras clave o una miríada de otros casos. Ya has visto comparaciones sencillas de patrones utilizando la operación in
para secuencias, o los métodos de cadena .endswith
y .startswith
. Para realizar comparaciones más sofisticadas, necesitas una herramienta más potente. Las expresiones regulares, a menudo denominadas regex, son la respuesta. Las expresiones regulares utilizan una cadena de caracteres para definir patrones de búsqueda. El paquete re
de Python ofrece operaciones de expresiones regulares similares a las de Perl. El módulo re
utiliza barras invertidas(\) para delimitar los caracteres especiales utilizados en las coincidencias. Para evitar confusiones con las secuencias de escape de las cadenas regulares, se recomienda utilizar cadenas en bruto al definir patrones de expresiones regulares. A las cadenas sin procesar se les antepone una r antes de la primera comilla.
Nota
Las cadenas de Python tienen varias secuencias de escape. Entre las más comunes están el salto de línea \n
y el tabulador \t
.
Buscando en
Digamos que tienes una lista cc de un correo electrónico como texto y quieres saber más sobre quién está en esta lista:
In
[
1
]:
cc_list
=
'''Ezra Koenig <ekoenig@vpwk.com>,
...: Rostam Batmanglij <rostam@vpwk.com>,
...: Chris Tomson <ctomson@vpwk.com,
...: Bobbi Baio <bbaio@vpwk.com'''
Si quieres saber si un nombre está en este texto, puedes utilizar la sintaxis de pertenencia a la secuencia in
:
In
[
2
]:
'Rostam'
in
cc_list
Out
[
2
]:
True
Para obtener un comportamiento similar, puedes utilizar la función re.search
, que devuelve un objeto re.Match
sólo si hay una coincidencia:
In
[
3
]:
import
re
In
[
4
]:
re
.
search
(
r
'Rostam'
,
cc_list
)
Out
[
4
]:
<
re
.
Match
object
;
span
=
(
32
,
38
),
match
=
'Rostam'
>
Puedes utilizarlo como condición para comprobar la afiliación:
>>>
if
re
.
search
(
r
'Rostam'
,
cc_list
):
...
(
'Found Rostam'
)
...
...
Found
Rostam
Juegos de caracteres
Hasta ahora re
no te ha dado nada que no pudieras conseguir utilizando el operador in
. Sin embargo, ¿qué ocurre si buscas a una persona en un texto, pero no recuerdas si se llama Bobbi o Robby?
Con las expresiones regulares, puedes utilizar grupos de caracteres, cualquiera de los cuales podría aparecer en un lugar. Se denominan conjuntos de caracteres. Los caracteres entre los que debe elegirse una coincidencia aparecen entre corchetes en la definición de la expresión regular. Puedes hacer coincidir la B o la R, seguidas de obb, y la i o la y:
In
[
5
]:
re
.
search
(
'[RB]obb[yi]'
,
',obbi'
)
Out
[
5
]:
<
re
.
Match
object
;
span
=
(
0
,
5
),
match
=
',obbi'
>
Puedes poner caracteres individuales separados por comas en un conjunto de caracteres o utilizar rangos. El rango A-Z incluye todas las letras mayúsculas; el rango 0-9 incluye los dígitos del cero al nueve:
In
[
6
]:
re
.
search
(
r
'Chr[a-z][a-z]'
,
cc_list
)
Out
[
6
]:
<
re
.
Match
object
;
span
=
(
69
,
74
),
match
=
'Chris'
>
El signo + después de un elemento en una expresión regular coincide con uno o más de ese elemento. Un número entre paréntesis coincide con un número exacto de caracteres:
In
[
7
]:
re
.
search
(
r
'[A-Za-z]+'
,
cc_list
)
Out
[
7
]:
<
re
.
Match
object
;
span
=
(
0
,
4
),
match
=
'Ezra'
>
In
[
8
]:
re
.
search
(
r
'[A-Za-z]{6}'
,
cc_list
)
Out
[
8
]:
<
re
.
Match
object
;
span
=
(
5
,
11
),
match
=
'Koenig'
>
Podemos construir una coincidencia utilizando una combinación de conjuntos de caracteres y otros caracteres para hacer una coincidencia ingenua de una dirección de correo electrónico. El carácter . tiene un significado especial. Es un comodín y coincide con cualquier carácter. Para que coincida con el carácter . real, debes escaparlo utilizando una barra invertida:
In
[
9
]:
re
.
search
(
r
'[A-Za-z]+@[a-z]+\.[a-z]+'
,
cc_list
)
Out
[
9
]:
<
re
.
Match
object
;
span
=
(
13
,
29
),
match
=
'ekoenig@vpwk.com'
>
Este ejemplo es sólo una demostración de conjuntos de caracteres. No representa toda la complejidad de una expresión regular lista para la producción de correos electrónicos.
Clases de personajes
Además de conjuntos de caracteres, la página re
de Python ofrece clases de caracteres. Se trata de conjuntos de caracteres prefabricados. Algunos de los más utilizados son \w
, que equivale a [a-zA-Z0-9_]
y \d
, que equivale a [0-9]
. Puedes utilizar el modificador + para hacer coincidir varios caracteres:
>>>
re
.
search
(
r
'\w+'
,
cc_list
)
<
re
.
Match
object
;
span
=
(
0
,
4
),
match
=
'Ezra'
>
Y puedes sustituir nuestro comparador de correo electrónico primitivo por \w
:
>>>
re
.
search
(
r
'\w+\@\w+\.\w+'
,
cc_list
)
<
re
.
Match
object
;
span
=
(
13
,
29
),
match
=
'ekoenig@vpwk.com'
>
Grupos
Puedes utilizar paréntesis para definir grupos en una coincidencia. Se puede acceder a estos grupos desde el objeto coincidencia. Están numerados en el orden en que aparecen, siendo el grupo cero la coincidencia completa:
>>>
re
.
search
(
r
'(\w+)\@(\w+)\.(\w+)'
,
cc_list
)
<
re
.
Match
object
;
span
=
(
13
,
29
),
match
=
'ekoenig@vpwk.com'
>
>>>
matched
=
re
.
search
(
r
'(\w+)\@(\w+)\.(\w+)'
,
cc_list
)
>>>
matched
.
group
(
0
)
'ekoenig@vpwk.com'
>>>
matched
.
group
(
1
)
'ekoenig'
>>>
matched
.
group
(
2
)
'vpwk'
>>>
matched
.
group
(
3
)
'com'
Grupos designados
También puedes proporcionar nombres a los grupos añadiendo ?P<NAME>
en la definición del grupo. Así podrás acceder a los grupos por su nombre en lugar de por su número:
>>>
matched
=
re
.
search
(
r
'(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<TLD>\w+)'
,
cc_list
)
>>>
matched
.
group
(
'name'
)
'ekoenig'
>>>
(
f
'''name: {matched.group("name")}
... Secondary Level Domain: {matched.group("SLD")}
... Top Level Domain: {matched.group("TLD")}'''
)
name
:
ekoenig
Secondary
Level
Domain
:
vpwk
Top
Level
Domain
:
com
Buscar todo
Hasta ahora, hemos demostrado que sólo devuelve la primera coincidencia encontrada. También podemos utilizar findall
para devolver todas las coincidencias como una lista de cadenas:
>>>
matched
=
re
.
findall
(
r
'\w+\@\w+\.\w+'
,
cc_list
)
>>>
matched
[
'ekoenig@vpwk.com'
,
'rostam@vpwk.com'
,
'ctomson@vpwk.com'
,
'cbaio@vpwk.com'
]
>>>
matched
=
re
.
findall
(
r
'(\w+)\@(\w+)\.(\w+)'
,
cc_list
)
>>>
matched
[(
'ekoenig'
,
'vpwk'
,
'com'
),
(
'rostam'
,
'vpwk'
,
'com'
),
(
'ctomson'
,
'vpwk'
,
'com'
),
(
'cbaio'
,
'vpwk'
,
'com'
)]
>>>
names
=
[
x
[
0
]
for
x
in
matched
]
>>>
names
[
'ekoenig'
,
'rostam'
,
'ctomson'
,
'cbaio'
]
Buscar Iterador
Cuando se trata de textos grandes, como los registros, es útil no procesar el texto de una sola vez. Puedes producir un objeto iterador utilizando el método finditer
. Este objeto procesa el texto hasta que encuentra una coincidencia y se detiene. Al pasarlo a la función next
devuelve la coincidencia actual y continúa procesando hasta encontrar la siguiente coincidencia. De esta forma, puedes tratar cada coincidencia individualmente sin dedicar recursos a procesar toda la entrada a la vez:
>>>
matched
=
re
.
finditer
(
r
'\w+\@\w+\.\w+'
,
cc_list
)
>>>
matched
<
callable_iterator
object
at
0x108e68748
>
>>>
next
(
matched
)
<
re
.
Match
object
;
span
=
(
13
,
29
),
match
=
'ekoenig@vpwk.com'
>
>>>
next
(
matched
)
<
re
.
Match
object
;
span
=
(
51
,
66
),
match
=
'rostam@vpwk.com'
>
>>>
next
(
matched
)
<
re
.
Match
object
;
span
=
(
83
,
99
),
match
=
'ctomson@vpwk.com'
>
El objeto iterador, matched
, también puede utilizarse en un bucle for
:
>>>
matched
=
re
.
finditer
(
"(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<TLD>\w+)"
,
cc_list
)
>>>
for
m
in
matched
:
...
(
m
.
groupdict
())
...
...
{
'name'
:
'ekoenig'
,
'SLD'
:
'vpwk'
,
'TLD'
:
'com'
}
{
'name'
:
'rostam'
,
'SLD'
:
'vpwk'
,
'TLD'
:
'com'
}
{
'name'
:
'ctomson'
,
'SLD'
:
'vpwk'
,
'TLD'
:
'com'
}
{
'name'
:
'cbaio'
,
'SLD'
:
'vpwk'
,
'TLD'
:
'com'
}
Sustitución
Además de buscar y comparar, las expresiones regulares pueden utilizarse para sustituir parte o la totalidad de una cadena:
>>>
re
.
sub
(
"\d"
,
"#"
,
"The passcode you entered was 09876"
)
'The passcode you entered was #####'
>>>
users
=
re
.
sub
(
"(?P<name>\w+)\@(?P<SLD>\w+)\.(?P<TLD>\w+)"
,
"\g<TLD>.\g<SLD>.\g<name>"
,
cc_list
)
>>>
(
users
)
Ezra
Koenig
<
com
.
vpwk
.
ekoenig
>
,
Rostam
Batmanglij
<
com
.
vpwk
.
rostam
>
,
Chris
Tomson
<
com
.
vpwk
.
ctomson
,
Chris
Baio
<
com
.
vpwk
.
cbaio
Compilando
Todos los ejemplos hasta ahora han llamado directamente a métodos del módulo re
. Esto es adecuado para muchos casos, pero si la misma coincidencia se va a producir muchas veces, se puede mejorar el rendimiento compilando la expresión regular en un objeto. Este objeto puede reutilizarse para las coincidencias sin necesidad de recompilarlo:
>>>
regex
=
re
.
compile
(
r
'\w+\@\w+\.\w+'
)
>>>
regex
.
search
(
cc_list
)
<
re
.
Match
object
;
span
=
(
13
,
29
),
match
=
'ekoenig@vpwk.com'
>
Las expresiones regulares ofrecen muchas más funciones de las que hemos tratado aquí. De hecho, se han escrito muchos libros sobre su uso, pero ahora deberías estar preparado para la mayoría de los casos básicos.
Evaluación perezosa
La evaluación perezosa es la idea de que, especialmente cuando se trata de grandes cantidades de datos, no quieres procesar todos los datos antes de utilizar los resultados. Ya has visto esto con el tipo range
, donde la huella de memoria es la misma, incluso para uno que represente un gran grupo de números.
Generadores
Puedes utilizar los generadores de forma similar a los objetos range
. Realizan alguna operación sobre los datos en trozos según se les solicite. Detienen su estado entre llamadas. Esto significa que puedes almacenar variables necesarias para calcular la salida, a las que se accede cada vez que se llama al generador.
Para escribir una función generadora, utiliza la palabra clave yield
en lugar de una sentencia return. Cada vez que se llama al generador, éste devuelve el valor especificado por yield
y luego pausa su estado hasta la siguiente llamada. Escribamos un generador que simplemente cuente, devolviendo cada número subsiguiente:
>>>
def
count
():
...
n
=
0
...
while
True
:
...
n
+=
1
...
yield
n
...
...
>>>
counter
=
count
()
>>>
counter
<
generator
object
count
at
0x10e8509a8
>
>>>
next
(
counter
)
1
>>>
next
(
counter
)
2
>>>
next
(
counter
)
3
Ten en cuenta que el generador mantiene un registro de su estado y, por tanto, la variable n
en cada llamada al generador refleja el valor establecido previamente. Implementemos un generador Fibonacci:
>>>
def
fib
():
...
first
=
0
...
last
=
1
...
while
True
:
...
first
,
last
=
last
,
first
+
last
...
yield
first
...
>>>
f
=
fib
()
>>>
next
(
f
)
1
>>>
next
(
f
)
1
>>>
next
(
f
)
2
>>>
next
(
f
)
3
También podemos iterar utilizando el generador en un bucle for
:
>>>
f
=
fib
()
>>>
for
x
in
f
:
...
(
x
)
...
if
x
>
12
:
...
break
...
1
1
2
3
5
8
13
Comprensiones del generador
Podemos utilizar las comprensiones de generador para crear generadores de una línea. Se crean utilizando una sintaxis similar a la de las comprensiones de lista, pero se utilizan paréntesis en lugar de corchetes:
>>>
list_o_nums
=
[
x
for
x
in
range
(
100
)]
>>>
gen_o_nums
=
(
x
for
x
in
range
(
100
))
>>>
list_o_nums
[
0
,
1
,
2
,
3
,
...
97
,
98
,
99
]
>>>
gen_o_nums
<
generator
object
<
genexpr
>
at
0x10ea14408
>
Incluso con este pequeño ejemplo, podemos ver la diferencia de memoria utilizada al utilizar el método sys.getsizeof
, que devuelve el tamaño de un objeto, en bytes:
>>>
import
sys
>>>
sys
.
getsizeof
(
list_o_nums
)
912
>>>
sys
.
getsizeof
(
gen_o_nums
)
120
Más funciones de IPython
Ya has visto algunas de las características de IPython al principio del capítulo. Ahora vamos a ver algunas características más avanzadas, como la ejecución de comandos shell desde dentro del intérprete IPython y el uso de funciones mágicas.
Utilizar IPython para ejecutar comandos de Unix Shell
Puedes utilizar IPython para ejecutar comandos de shell. Ésta es una de las razones más convincentes para realizar acciones DevOps en el shell de IPython. Veamos un ejemplo muy sencillo en el que el carácter !
, que IPython utiliza para identificar los comandos del shell, se coloca delante del comando ls
:
In
[
3
]:
var_ls
=
!
ls
-
l
In
[
4
]:
type
(
var_ls
)
Out
[
4
]:
IPython
.
utils
.
text
.
SList
La salida del comando se asigna a una variable Python var_ls
. El type
de esta variable es IPython.utils.text.SList
. El tipo SList
convierte un comando shell normal en un objeto que tiene tres métodos principales: fields
, grep
, y sort
. Aquí tienes un ejemplo en acción utilizando el comando Unix df
. El método sort
puede interpretar los espacios en blanco de este comando Unix y luego ordenar la tercera columna por tamaño:
In
[
6
]:
df
=
!
df
In
[
7
]:
df
.
sort
(
3
,
nums
=
True
)
Veamos a continuación SList
y .grep
. Aquí tienes un ejemplo que busca qué comandos con kill
como parte de su nombre están instalados en el directorio /usr/bin:
In
[
10
]:
ls
=
!
ls
-
l
/
usr
/
bin
In
[
11
]:
ls
.
grep
(
"kill"
)
Out
[
11
]:
[
'-rwxr-xr-x 1 root wheel 1621 Aug 20 2018 kill.d'
,
'-rwxr-xr-x 1 root wheel 23984 Mar 20 23:10 killall'
,
'-rwxr-xr-x 1 root wheel 30512 Mar 20 23:10 pkill'
]
Lo más importante es que IPython es un entorno de ensueño para hackear pequeños scripts de shell.
Utilizar los comandos mágicos de IPython
Si te acostumbras a utilizar IPython, también deberías acostumbrarte a utilizar los comandos mágicos incorporados. Básicamente, son atajos con un gran efecto. Los comandos mágicos se indican precediéndolos de %%
. He aquí un ejemplo de cómo escribir Bash en línea dentro de IPython. Ten en cuenta que esto es sólo un pequeño comando, pero podría ser un script Bash entero:
In
[
13
]:
%%
bash
...
:
uname
-
a
...
:
...
:
Darwin
nogibjj
.
local
18.5
.
0
Darwin
Kernel
Version
18.5
.
0
:
Mon
Mar
...
El %%writefile
es bastante complicado porque puedes escribir y probar scripts Python o Bash sobre la marcha, utilizando IPython para ejecutarlos. No es para nada un mal truco de fiesta:
In
[
16
]:
%%
writefile
print_time
.
py
...
:
#!/usr/bin/env python
...
:
import
datetime
...
:
(
datetime
.
datetime
.
now
()
.
time
())
...
:
...
:
...
:
Writing
print_time
.
py
In
[
17
]:
cat
print_time
.
py
#!/usr/bin/env python
import
datetime
(
datetime
.
datetime
.
now
()
.
time
())
In
[
18
]:
!
python
print_time
.
py
19
:
06
:
00.594914
Otro comando muy útil, %who
, te mostrará lo que hay cargado en memoria. Resulta muy útil cuando llevas mucho tiempo trabajando en un terminal:
In
[
20
]:
%
who
df
ls
var_ls
Ejercicios
-
Escribe una función Python que tome un nombre como argumento e imprima ese nombre.
-
Escribe una función Python que tome una cadena como argumento e imprima si está en mayúsculas o minúsculas.
-
Escribe una comprensión de lista que dé como resultado una lista con todas las letras de la palabra smogtether en mayúsculas.
-
Escribe un generador que alterne entre devolver Pares e Impares.
Get Python para DevOps now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.