Python avanzado

Context managers

with

>>> with open('context.txt', 'w') as f:
...     f.write("You don't need to close...")
...
...
26
>>> f.closed
True

Generadores

Generadores: yield

>>> def my_gen():
...     yield 2
...     yield 1
...
>>> for n in my_gen():
...     print(n)
...
2
1
>>> list(my_gen())
[2, 1]
>>> g = my_gen()
>>> next(g)
2
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Generadores: yield

>>> def fib():
...     a, b = 0, 1
...     while True:
...         yield b
...         a, b = b, a + b
...
>>> f = fib()
>>> f
<generator object fib at 0x...>
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5

Generadores en builtin functions

>>> range(3)
range(0, 3)
>>> for n in range(3):
...     print(n)
...
0
1
2
>>> d = [0, 1, 2, 3]
>>> r = reversed(d)
>>> for k in r:
...     print(k)
...
3
2
1
0
>>> for k in r:
...     print(k)
>>>
>>> def is_even(x): return x % 2 == 0
>>> d = [0, 1, 2, 3, 4, 5]
>>> even = filter(is_even, d)
>>> for k in even:
...     print(k)
...
0
2
4
>>> for k in even:
...     print(k)
>>>

También map.

Ejemplo completo

  • Inicializar generador con un argumento

  • Recibir parámetro

>>> def multiply_by(k):
...   n = 1
...   while True:
...     msg = yield k * n
...     n += 1
...     if msg:
...       print(f"Llevamos {n} iteraciones")
>>> double = multiply_by(2)
>>> double.__next__()
2
>>> double.send(False)
4
>>> double.send(True)
Llevamos 3 iteraciones
6
>>> next(double)
8

Funciones

Funciones: polimorfismo

def catch_all(*args, **kwargs):
    print('Args: ', args)
    print('Kwargs: ', kwargs)
>>> catch_all()
Args:  ()
Kwargs:  {}
>>> catch_all(1, 2)
Args:  (1, 2)
Kwargs:  {}
>>> catch_all(1, 2, a=1)
Args:  (1, 2)
Kwargs:  {'a': 1}

Funciones: parámetros especiales

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
          |               |              |
          |      Posición o Clave        |
          |                               - Clave solo
          -- Posición solo
def standard_arg(arg):
    print(arg)

def pos_only_arg(arg, /):
    print(arg)

def kwd_only_arg(*, arg):
    print(arg)

def combined_example(pos_only, /, standard, *, kwd_only):
    print(pos_only, standard, kwd_only)
def func(a, b, /, *, double=False):
    rv = a * b
    if double:
        rv = 2 * rv
    return rv
>>> func(3, 4)
12
>>> func(3, 4, double=True)
24
>>> func(3, 4, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes 2 positional arguments but 3 were given
>>> func(b=4, a=3, double=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() got some positional-only arguments passed as keyword arguments: 'a, b'

Cadenas de documentación

def foo():
    """What this function does."""
    return
>>> foo.__name__
'foo'
>>> foo.__doc__
'What this function does.'

Decoradores

Decoradores

>>> def entry_exit(fn):
...     def new_fn():
...         print('Enter', fn.__name__)
...         fn()
...         print('Exit', fn.__name__)
...     return new_fn
...
>>> def greet():
...     print('Hello')
...
>>> greet_decorated = entry_exit(greet)
>>>
>>> greet()
Hello
>>> greet_decorated()
Enter greet
Hello
Exit greet

Decoradores: sintaxis simplificada

>>> @entry_exit
... def greet():
...     print('Hello')
...
>>> greet()
Enter greet
Hello
Exit greet

Decoradores: ejemplo completo

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print('Calling decorated function')
...         return f(*args, **kwds)
...     return wrapper
...
>>> @my_decorator
... def example():
...     """Docstring"""
...     print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'

Context manager

Context managers propios

from contextlib import contextmanager

@contextmanager
def message():
    print('Start')
    try:
        yield
    finally:
        print('and end')
>>> with message():
...     n = 1000
...     while n > 0:
...         n -= 1
Start
and end

Otras expresiones

Expresión lambda

>>> f = lambda: 1
>>> f()
1
>>> f = lambda x: 2 * x
>>> f(2)
4

Operador ternario

'Allowed' if age >= 18 else 'Forbidden'

Recetas e idioms

Interpolación de cadenas

>>> print('Hello %s!' % 'world')
Hello world!
>>> print('%(language)s has %(number)03d quote types.' %
...       {'language': "Python", "number": 2})
Python has 002 quote types.

Formateado de cadenas

str.format(*args, **kwargs)

>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
'a, b, c'
>>> '{}, {}, {}'.format('a', 'b', 'c')
'a, b, c'
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
>>> '{0}{1}{0}'.format('abra', 'cad')
'abracadabra'
>>> 'Coordinates: {latitude}, {longitude}'.format(
...     latitude='37.24N', longitude='-115.81W'
... )
'Coordinates: 37.24N, -115.81W'

F-strings

>>> name = "Miguel"
>>> print(f"{name}")
Miguel

Desempaquetado

  • Devolver varios valores de una función

def division(a, b):
    quotient = a // b
    remainder = a % b
    return quotient, remainder
>>> division(42, 8)
(5, 2)
>>> q, r = division(42, 8)
>>> q
5
>>> r
2

Desempaquetado: swap

>>> a, b = 1, 42
>>> a, b
(1, 42)
>>> a, b = b, a
>>> a, b
(42, 1)

Desempaquetado: resto

>>> toys = ['Buzz', 'Rex', 'Bo', 'Hamm', 'Slink', ]
>>> first, *rest = toys
>>> first
'Buzz'
>>> rest
['Rex', 'Bo', 'Hamm', 'Slink']

list comprehension

  • Sintaxis compacta

  • Rendimiento óptimo

fib = [1, 1, 2, 3, 5, 8, 13, 21]
even = []
for n in fib:
   if n % 2 == 0:
       even.append(n)
even = [n for n in fib if n % 2 == 0]

dict comprehension

toys = ['Buzz', 'Rex', 'Bo', 'Hamm', 'Slink', ]
len_map = {}
for toy in toys:
    len_map[toy] = len(toy)
len_map = {toy: len(toy) for toy in toys}

Generator expression

>>> fib = [1, 1, 2, 3, 5, 8, 13, 21]
>>> even = (n for n in fib if n % 2 == 0)
>>> print(even)
<generator object <genexpr> at 0x...>
>>> list(even)
[2, 8]

Trabajo con secuencias: enumerate

enumerate(sequence, start=0)

>>> colores = ['rojo', 'amarillo', 'verde']
>>> list(enumerate(colores))
[(0, 'rojo'), (1, 'amarillo'), (2, 'verde')]
>>> list(enumerate(colores, 1))
[(1, 'rojo'), (2, 'amarillo'), (3, 'verde')]

Trabajo con secuencias: range

range(stop) o range(start, stop[, step])

>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(range(0, 30, 5))
[0, 5, 10, 15, 20, 25]

Trabajo con secuencias: sorted

sorted(iterable[, key[, reverse]]])

>>> toys = ['Buzz', 'Rex', 'Bo', 'Hamm', 'Slink', ]
>>> sorted(toys)  # Alphabetic
['Bo', 'Buzz', 'Hamm', 'Rex', 'Slink']
>>> sorted(toys, key=len)  # length
['Bo', 'Rex', 'Buzz', 'Hamm', 'Slink']
>>> sorted(toys, key=lambda x: x[1])  # second char alphabetic
['Hamm', 'Rex', 'Slink', 'Bo', 'Buzz']

Trabajo con secuencias: zip

zip([iterable, ...])

>>> words = ['uno', 'dos', 'tres']
>>> numbers = [1, 2, 3]
>>> zip(words, numbers)  
<zip object at 0x...>
>>> list(zip(words, numbers))
[('uno', 1), ('dos', 2), ('tres', 3)]
>>> list(zip(range(1000), 'abcdef'))
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f')]

Trabajo con secuencias: unzip

>>> z = [('uno', 1), ('dos', 2), ('tres', 3)]
>>> list(zip(*z))
[('uno', 'dos', 'tres'), (1, 2, 3)]
>>> words, numbers = zip(*z)
>>> words
('uno', 'dos', 'tres')
>>> numbers
(1, 2, 3)

Excepciones

Herencia de excepciones

../_images/python-exceptions-hierarchy.png

Herencia de excepciones (1)

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception

Herencia de excepciones: Exception

+-- Exception
     +-- StopIteration
     +-- ArithmeticError
     +-- AssertionError
     +-- AttributeError
     +-- BufferError
     +-- EOFError
     +-- ImportError
     +-- LookupError
     +-- MemoryError
     +-- NameError
     +-- OSError
     +-- ReferenceError
     +-- RuntimeError
     +-- SyntaxError
     +-- SystemError
     +-- TypeError
     +-- ValueError
     +-- Warning

Excepciones

exception ImportError

Se lanza cuando no consigue encontrar un módulo o un nombre.

exception SyntaxError

Error de sintaxis en la escritura del programa.

exception KeyError

No encuentra la clave al acceder a un diccionario.

exception IndexError

El índice es incorrecto en una lista o tupla.

exception UnicodeError

Problemas en la conversión de o hacia Unicode.

exception ValueError

Error genérico producido porque el valor recibido, aunque tiene el tipo correcto, tiene un valor inesperado.

exception TypeError

La operación o función no se puede aplicar a ese tipo.

exception KeyboardInterrupt

Se lanza cuando se interrumpe la ejecución de un programa después de pulsar las teclas Control-C.

Creación de excepciones propias

Es posible crear excepciones propias. Para ello basta crear una clase que herede de Exception.

class ErrorPersonalizado(Exception):
    pass