
Una nueva selección de consejos y programación de Python de mi feed @pythonetc.
←
Colecciones anterioresEn
asyncio
no es necesario iniciar un bucle para contener tareas. Puede crear y detener tareas incluso cuando se detiene el ciclo. Si se detiene, algunas tareas pueden permanecer incompletas.
import asyncio async def printer(): try: try: while True: print('*') await asyncio.sleep(1) except asyncio.CancelledError: print('') finally: await asyncio.sleep(2) print('')
Resultado:
* * || *
Asegúrese de esperar a que se completen todas las tareas antes de detener el ciclo. Si esto no se hace, puede omitir algunos bloques y algunos administradores de contexto no se deshabilitarán.
Python le permite anular muchos operadores, incluido el operador de desplazamiento bit a bit. Aquí hay un ejemplo de cómo crear una composición de funciones usando este operador. Las flechas indican la dirección de la transferencia de datos:
from collections import deque from math import sqrt class Compose: def __init__(self): self._functions = deque() def __call__(self, *args, **kwargs): result = None for f in self._functions: result = f(*args, **kwargs) args = [result] kwargs = dict() return result def __rshift__(self, f): self._functions.append(f) return self def __lshift__(self, f): self._functions.appendleft(f) return self compose = Compose sqrt_abs = (compose() << sqrt << abs) sqrt_abs2 = (compose() >> abs >> sqrt) print(sqrt_abs(-4))
Al definir una clase, puede pasar argumentos a su metaclase. La notación de
class
admite palabras clave como argumentos:
class Klass(Parent, arg='arg')
. La palabra clave metaclass está reservada para elegir una metaclase, y puede usar otras como desee.
Aquí hay un ejemplo de una metaclase que crea una clase sin uno de los atributos. El nombre del atributo se proporciona en el argumento
remove
:
class FilterMeta(type): def __new__(mcs, name, bases, namespace, remove=None, **kwargs): if remove is not None and remove in namespace: del namespace[remove] return super().__new__(mcs, name, bases, namespace) class A(metaclass=FilterMeta, remove='half'): def half(x): return x // 2 half_of_4 = half(4) half_of_100 = half(100) a = A() print(a.half_of_4)
A veces necesita agotar el generador, pero al mismo tiempo no le interesan los valores que crea, sino algunos efectos secundarios. Por ejemplo, una excepción, escribir en un archivo, cambiar una variable global, etc.
Hay una manera conveniente y popular de hacer esto es
list(gen())
. Sin embargo, este método guarda todos los valores en la memoria y luego los elimina inmediatamente. Esto puede ser redundante. Si desea evitar este comportamiento, puede usar
deque
con un límite de tamaño:
from collections import deque def inversed(nums): for num in nums: yield 1 / num try: deque(inversed([1, 2, 0]), maxlen=0) except ZeroDivisionError: print('E')
En aras de la precisión semántica, puede definir su propia función de
exhaust
:
def exhaust(iterable): for _ in iterable: pass
Supongamos que tiene un par de clases: el padre hijo, el
User
y el
Admin
. Y también tiene una función que toma una lista de usuarios como argumento. ¿Puedes proporcionar una lista de administradores? No: la función puede agregar otro usuario a la lista de administradores, lo cual es erróneo y viola las garantías proporcionadas por la lista.
Sin embargo, puede proporcionar un tipo de
Sequence
porque es de solo lectura. Más precisamente, en este caso, la
Sequence
es covariante en el tipo de participantes.
Puede definir tipos covariantes proporcionando
covariant=True
como argumento para
TypeVar
:
from typing import TypeVar, Generic T = TypeVar('T', covariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def get(self) -> T: return self._var class User: pass class Admin(User): pass def print_user_from_holder(holder: Holder[User]) -> None: print(holder.get()) h: Holder[Admin] = Holder(Admin()) print_user_from_holder(h)
Por el contrario, una función puede requerir un contenedor solo para colocar administradores en él. Dichos contenedores, que están disponibles solo para grabación, son
contravariantes por tipo de participantes:
from typing import TypeVar, Generic T = TypeVar('T', contravariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def change(self, x: T): self._var = x class User: pass class Admin(User): pass def place_admin_to_holder(holder: Holder[Admin]) -> None: holder.change(Admin()) h: Holder[User] = Holder(User()) place_admin_to_holder(h)
Las clases que no son covariantes ni contravariantes se llaman
invariantes .