=============== Python avanzado =============== .. Introducir técnicas de Python para entender y escribir programas más cortos y efectivos. Duración 6 horas *Context managers* ================== with ---- .. doctest:: >>> with open('context.txt', 'w') as f: ... f.write("You don't need to close...") ... ... 26 >>> f.closed True Generadores =========== Generadores: *yield* -------------------- .. doctest:: >>> 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 "", line 1, in StopIteration Generadores: *yield* -------------------- .. doctest:: >>> def fib(): ... a, b = 0, 1 ... while True: ... yield b ... a, b = b, a + b ... >>> f = fib() >>> f >>> next(f) 1 >>> next(f) 1 >>> next(f) 2 >>> next(f) 3 >>> next(f) 5 Generadores en *builtin functions* ---------------------------------- .. doctest:: >>> range(3) range(0, 3) >>> for n in range(3): ... print(n) ... 0 1 2 .. nextslide:: .. doctest:: >>> d = [0, 1, 2, 3] >>> r = reversed(d) >>> for k in r: ... print(k) ... 3 2 1 0 >>> for k in r: ... print(k) >>> .. nextslide:: .. doctest:: >>> 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 .. doctest:: >>> 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 ----------------------- .. code:: python def catch_all(*args, **kwargs): print('Args: ', args) print('Kwargs: ', kwargs) .. testsetup:: * def catch_all(*args, **kwargs): print('Args: ', args) print('Kwargs: ', kwargs) .. doctest:: >>> 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 .. nextslide:: .. code:: python 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) .. nextslide:: .. code:: python def func(a, b, /, *, double=False): rv = a * b if double: rv = 2 * rv return rv .. testsetup:: * def func(a, b, /, *, double=False): rv = a * b if double: rv = 2 * rv return rv .. doctest:: >>> func(3, 4) 12 .. doctest:: >>> func(3, 4, double=True) 24 .. nextslide:: .. doctest:: >>> func(3, 4, True) Traceback (most recent call last): File "", line 1, in TypeError: func() takes 2 positional arguments but 3 were given .. nextslide:: .. doctest:: >>> func(b=4, a=3, double=True) Traceback (most recent call last): File "", line 1, in TypeError: func() got some positional-only arguments passed as keyword arguments: 'a, b' Cadenas de documentación ------------------------ .. code:: python def foo(): """What this function does.""" return .. testsetup:: * def foo(): """What this function does.""" return .. doctest:: >>> foo.__name__ 'foo' .. doctest:: >>> foo.__doc__ 'What this function does.' Decoradores =========== Decoradores ----------- .. doctest:: >>> 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 ---------------------------------- .. doctest:: >>> @entry_exit ... def greet(): ... print('Hello') ... >>> greet() Enter greet Hello Exit greet Decoradores: ejemplo completo ----------------------------- .. doctest:: >>> 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 ------------------------ .. testsetup:: from contextlib import contextmanager @contextmanager def message(): print('Start') try: yield finally: print('and end') .. code:: python from contextlib import contextmanager @contextmanager def message(): print('Start') try: yield finally: print('and end') .. doctest:: >>> with message(): ... n = 1000 ... while n > 0: ... n -= 1 Start and end Otras expresiones ================= Expresión *lambda* ------------------ .. doctest:: >>> f = lambda: 1 >>> f() 1 >>> f = lambda x: 2 * x >>> f(2) 4 Operador ternario ----------------- .. code:: python 'Allowed' if age >= 18 else 'Forbidden' Recetas e *idioms* ================== Interpolación de cadenas ------------------------ .. doctest:: >>> print('Hello %s!' % 'world') Hello world! >>> print('%(language)s has %(number)03d quote types.' % ... {'language': "Python", "number": 2}) Python has 002 quote types. * `Referencia printf `_ Formateado de cadenas --------------------- ``str.format(*args, **kwargs)`` .. doctest:: >>> '{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' .. doctest:: >>> 'Coordinates: {latitude}, {longitude}'.format( ... latitude='37.24N', longitude='-115.81W' ... ) 'Coordinates: 37.24N, -115.81W' * `Referencia format string `_ F-strings --------- .. doctest:: >>> name = "Miguel" >>> print(f"{name}") Miguel * `Referencia f-strings `_ Desempaquetado -------------- * Devolver varios valores de una función .. code:: python def division(a, b): quotient = a // b remainder = a % b return quotient, remainder .. testsetup:: * def division(a, b): quotient = a // b remainder = a % b return quotient, remainder .. doctest:: >>> division(42, 8) (5, 2) >>> q, r = division(42, 8) >>> q 5 >>> r 2 Desempaquetado: swap -------------------- .. doctest:: >>> a, b = 1, 42 >>> a, b (1, 42) >>> a, b = b, a >>> a, b (42, 1) Desempaquetado: resto --------------------- .. doctest:: >>> toys = ['Buzz', 'Rex', 'Bo', 'Hamm', 'Slink', ] >>> first, *rest = toys >>> first 'Buzz' >>> rest ['Rex', 'Bo', 'Hamm', 'Slink'] *list comprehension* -------------------- * Sintaxis compacta * Rendimiento óptimo .. code:: python fib = [1, 1, 2, 3, 5, 8, 13, 21] .. code:: python even = [] for n in fib: if n % 2 == 0: even.append(n) .. code:: python even = [n for n in fib if n % 2 == 0] *dict comprehension* -------------------- .. code:: python toys = ['Buzz', 'Rex', 'Bo', 'Hamm', 'Slink', ] .. code:: python len_map = {} for toy in toys: len_map[toy] = len(toy) .. code:: python len_map = {toy: len(toy) for toy in toys} *Generator expression* ---------------------- .. doctest:: >>> fib = [1, 1, 2, 3, 5, 8, 13, 21] >>> even = (n for n in fib if n % 2 == 0) >>> print(even) at 0x...> >>> list(even) [2, 8] Trabajo con secuencias: enumerate --------------------------------- ``enumerate(sequence, start=0)`` .. doctest:: >>> 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])`` .. doctest:: >>> 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]]])`` .. doctest:: >>> 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, ...])`` .. doctest:: >>> words = ['uno', 'dos', 'tres'] >>> numbers = [1, 2, 3] >>> zip(words, numbers) #doctest: +ELLIPSIS >>> list(zip(words, numbers)) [('uno', 1), ('dos', 2), ('tres', 3)] .. doctest:: >>> list(zip(range(1000), 'abcdef')) [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f')] Trabajo con secuencias: *unzip* ------------------------------- .. doctest:: >>> 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 ----------------------- .. figure:: /_static/python-exceptions-hierarchy.png :class: full Herencia de excepciones (1) --------------------------- .. code:: BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception Herencia de excepciones: Exception ---------------------------------- .. code:: +-- 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``. .. code:: python class ErrorPersonalizado(Exception): pass