Apuntes de Python 3 (2ª Parte)

11 Noviembre 2016 at 17:22 by Adrián Pérez

python3Ya tenemos aquí el segundo post de apuntes de Python 3, que sigue al primero que publiqué hace ya unos meses. Pincha en el siguiente link si te lo perdiste:

Apuntes de Python3 (1ª Parte)

1. Funciones (continuación)

Argumentos de la función (continuación)

Podemos setear un valor por defecto de uno o más argumentos de la función, de tal manera que si al llamar a la función no le pasamos dichos argumentos, éstos tomarán el valor por defecto. Podemos pasarle como valor por defecto, una variable, pero mucho cuidado pues el valor por defecto se seteará en el momento de la definición, con lo que si modificas la variable después de la definición de la función, no variará el valor por defecto del argumento.

>>> def myfunc(a,b=33,c='hello'):
>>>   print(a,b,c)
>>>
>>> myfunc(1,2)
1 2 hello

Respecto a los argumentos al llamar a una función, deberíamos usar "keyword arguments" (aka "kwargs") en lugar de argumentos posicionales. Es más largo pero es mucho más claro.

def myfunc(a,b,c):
   pass
 
myfunc(1,2,3)            #argumentos posicionales
myfunc(a=1,b=2,c=3)      #kwargs

Podemos usar un único argumento al definir la función para capturar cualquier número de argumentos posicionales (un asterísco) o kwargs (dos asteríscos) que se le pasen a la función.

def myfunc(*myargs):     #myargs será una tupla con todos los argumentos posicionales
def myfunc(**mykwargs):  #mykwargs será un diccionario con todos los kwargs

2. Tipos de datos

Listas

En las listas, la primera posición = 0. Las listas disponen de todo un seguido de funciones (append, extend, insert, pop, clear, count...).

Pilas (stacks)

Dentro de las estructuras de datos, en python también podemos usar las listas (haciendo uso de las operaciones de "append()" y "pop()") como si fueran las típicas pilas (o stacks), también conocidas como colas LIFO (el último elemento que entra en la cola, es el primero que sale).

Colas

También podemos usar las listas como colas FIFO (el primer elemento que entra en la cola, es el primero que sale), aunque las listas no son eficientes en estas situaciones. Por ello, usaremos el objeto dque (Double Queue) importado desde el módulo "collections" cuando queramos usar colas FIFO, con sus correspondientes operaciones.

from collections import deque
queue = dque(["one","two","three"])

List comprehensions

Si programas siguiendo la filosofía "pythonica" harás uso de List Comprehensions muy a menudo.

Básicamente se trata de una manera propia de Python para generar listas (que en otros lenguajes seguramente haríamos con un "for"). Si por ejemplo queremos tener una lista de los cuadrados de 0 a 9 (incluídos), con otros lenguajes haríamos un for para ir añadiendo elementos a la lista. Con Python, usaremos una "list comprehension" tal que así:

lista=[x**2 for x in range(10)]

Podemos tener múltiples "for" en una "list comprehension", que sería como tener un for dentro de otro. Así pues, una list comprehension la conforman unos corchetes que contienen una expresión ("x**2" en el ejemplo) seguida de un for ("for x in range(10)" en el ejemplo), y a continuación opcionalmente más fors y/o ifs, que al final devuelven una lista.

>>> lista=[1,2,-3,-4,5]
>>> listcomprehen=[x for x in lista if x>=0]   #Filtramos los valores de "lista" para eliminar los negativos.
>>> listcomprehen
[1, 2, 5]

Así pues, podremos usar "list comprehensions" en múltiples situaciones:

  • Generar nuevas listas
  • Filtrar los elementos de una lista
  • Aplicar una función a los elementos de una lista
  • Llamar a un método para los elementos de una lista

Tuplas

Las tuplas, al igual que en el resto de lenguajes, son una secuencia de diferentes tipos de elementos, opcionalmente entre paréntesis. Las tuplas son immutables, pero pueden contener objetos mutables. Podemos "desempaquetar" la tupla asignando sus elementos a diferentes variables, igual que hacemos al retornar múltiples variables en una función.

>>> tuplavacía=()
>>> tupla=(123,456,'hola',('a',7,8))
>>> w,x,y,z=tupla #Aquí hacemos el "un-packing"
>>> w
123
>>> z
('a', 7, 8)

Set

Set es otra estructura de datos, consistente en una colección no ordenada (random) de elementos no duplicados. Por lo tanto no hay índices, no podemos acceder a un elemento por su índice. Puede ser útil para eliminar duplicados de una lista, por ejemplo, transformando la lista en un set, y de nuevo en una lista. Los sets soportan operaciones matemáticas como unión, intersección, diferencia, etc.

>>> set = {'a','b','c','a'}        # podemos añadir elementos duplicados
>>> set                            # pese a que en realidad se descartan
{'b', 'a', 'c'}
>>> 'b' in set                     # podemos validar que un elemento esté en el set
True

NOTAS:

  • No usar "{}" para crear un set vacío, pues define un diccionario. En su lugar usar "set()".
  • Los sets también tienen "set comprehensions". Ej. a = {c for x in 'abcdefg' if x not in 'abc'}

Diccionarios

Los diccionarios (conocidos en otros lenguajes como "arrays asociativos") son un conjunto no ordenado de pares clave-valor, donde la clave es única. Los diccionarios están indexados por las claves, las cuales pueden ser de cualquier tipo mientras sean immmutables (si son tuplas, todos los integrantes de la tupla han de ser de tipo immutable), aunque los diccionarios en sí son mutables.

Podemos crear un diccionario especificando directamente los pares clave-valor, o mediante la función "dict()" pasándole una lista de tuplas, donde cada tupla será la clave valor.

>>> mydict1 = {'joe':45, 'sam':50}
>>> mydict2 = dict([('joe',45),('sam',50)])
>>> mydict3 = dict(joe=45, sam=50)  # cuando las keys son strings simples, podemos usar este otro formato
>>> mydict['joe']
45

Los diccionarios también tienen "dict comprehensions", el formato de los cuales es muy similar al de los sets, pero incluyendo el par clave valor al principio (clave:valor).

mydict = {x:x**2 for x in (1,2,3)}    # en el ejemplo, x**2 es el valor

3. Módulos

Cuando se hace el import de un módulo, lo que se está haciendo no es cargar el código fuente, si no que se está ejecutando ese módulo. Sin embargo, habitualmente los módulos contendrán definiciones (funciones o clases).

Cada módulo cuenta con su propia tabla de símbolos privada, con lo que las variables globales que tenga el módulo residirán en el namespace del módulo, y podrán ser llamadas desde otro módulo vía su namespace.

Además del import del módulo, con Python podemos importar únicamente los nombres (función, clase o variable) que necesitemos de un determinado módulo, directamente en nuestro namespace (en el namespace del script que está importando el módulo).

>>> from module import func1, func2
>>> func1(a,b)              # La función se importa en mi namespace

Sin embargo, esta práctica puede llevar a problemas si tenemos nombres duplicados en ambos módulos.

Cuando se hace un import de un módulo, por defecto se buscará el módulo en las siguientes ubicaciones. Se puede usar sys.path para definir nuevas ubicaciones desde nuestro script.

  • El mismo directorio donde está el script desde el que estamos haciendo el import.
  • La variable de entorno PYTHONPATH, que igual que PATH contiene un listado de directorios.
  • Los directorios "site-packages" de la versión correspondiente (típicamente /var/lib/python/site-packages).

Para agrupar varios módulos podemos usar paquetes. Un paquete no es más que un conjunto de subdirectorios que a su vez contienen los módulos que lo conforman. Cada subdirectorio que forme parte del paquete deberá contener un fichero llamado __init__.py, el cual puede (aunque no es obligatorio) definir explícitamente los módulos que contiene el subdirectorio, mediante la función "__all__", o símplemente dejarse vacío.

4. I/O

Output

Podemos convertir cualquier tipo de datos a un string con:

  • str(): cuando quien lo leerá será un humano.
  • repr(): quien leerá el resultado será una aplicación o un interprete. Si estamos convirtiendo un string, el output mantendrá las comillas de inicio y fin de string, así como las backslashes para escapar.

Respecto a formatear el output, podemos hacerlo a mano (concatenación, slicing, etc.) o mediante la función format que tiene los strings. En ella, se sustituirán los "{}" por los valores indicados:

>>> print('Hola, me llamo {} y vengo de {}'.format('Adri','Barcelona'))
Hola, me llamo Adri y vengo de Barcelona

También podemos referirnos a la posición de los objetos pasados al método "format", así como usar keyword args. Igualmente, podemos usar {!a} (ascii), {!s} (str()) y {!r} (repr()) para convertir el varlor del objeto antes de formatearlo. Format nos ofrece también otras opciones de formateo rápido de cara a diccionarios.

Lecturas y escrituras

Con el método open abriremos un fichero y lo devolveremos en forma de objeto tipo fichero (file-like object), sobre el cual podremos ejecutar una serie de métodos (read, write, etc.).

Además del segundo parámetro correspondiente al modo de apertura del fichero, al tratar con ficheros de texto, deberemos especificar siempre como tercer parámetro el encoding del fichero, para poder trabajar únicamente con texto sin encoding y prevenir múltiples problemas.

Al finalizar el tratamiento del texto, volveremos a codificar dicho texto en el formato original. Esto se conoce como The unicode sandwich.

>>> f = open('myfile','w', encoding='utf-8')
>>> f.write(u'\blablabla')
>>> f.close()

Se recomienda encarecidamente usar "with" para ficheros y file-like objects, pues éste se encargará de no dejar recursos abiertos.

// Escrituras
>>> with open('myfile','w', encoding='utf-8') as f:
       f.write(u'\blablabla')
// Lecturas
>>> with open('myfile','r', encoding='utf-8') as f:
       for line in f:
           print(line,end="")

Acabamos de ver como en el ejemplo anterior, cuando haces un loop sobre un file-like object, por defecto, leerá una línea completa por cada iteración (el loop aplica un "readline()" por nosotros), sin necesidad de especificar nada más.

Comentar que todo lo que no sea un string necesita serializarse (pasarse a string) antes de poder escribirse en el fichero. Python provee varios módulos para serializar/deserializar objetos, como por ejemplo el módulo "json".

En breve el tercer post con (aun) más apuntes.

NOTAS:

Las funciones lambda en python son pequeñas funciones anónimas (de uso atómico) restringidas a una única expresión en el cuerpo de la función, tal que así:

lambda <argumentos>:<cuerpo de la función>

Tenemos varias funciones built-in en Python 3.