Consejos y trucos de mi canal de Telegram @pythonetc, septiembre de 2019



Es una nueva selección de consejos y trucos sobre Python y la programación de mi canal de Telegram @pythonetc.

Publicaciones anteriores


asyncio loop no tiene que ejecutarse para tener tareas. Puede crear y detener tareas aunque el ciclo se haya detenido en este momento. Si se detiene el ciclo, algunas tareas pueden quedar incompletas para siempre.

 import asyncio async def printer(): try: try: while True: print('*') await asyncio.sleep(1) except asyncio.CancelledError: print('x') finally: await asyncio.sleep(2) print('o') # never happens loop = asyncio.get_event_loop() run = loop.run_until_complete task = loop.create_task(printer()) run(asyncio.sleep(1)) # printer works here print('||') run(asyncio.sleep(1)) # printer works here task.cancel() # nothing happens run(asyncio.sleep(1)) # x printed 

Salida:

 * * || * x 

Debe asegurarse de esperar todas las tareas antes de detener el ciclo. En caso de que no lo haga, es posible que finally se omitan algunos bloques y que algunos administradores de contexto no se salgan.


Python le permite sobrecargar muchos operadores diferentes y el operador de turno es uno de ellos. Aquí hay un ejemplo de cómo crear una composición de función usando este operador. Aquí, los signos en forma de flecha muestran la dirección del flujo 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)) # 2.0 print(sqrt_abs2(-4)) # 2.0 


Puede pasar argumentos a una metaclase personalizada desde la definición de clase. La notación de class admite argumentos de palabras clave: class Klass(Parent, arg='arg') . La palabra clave metaclass está reservada para configurar metaclass, pero otras son de uso gratuito.

Aquí hay un ejemplo de metaclase que crea una clase sin uno de los atributos. El nombre de ese 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) # 2 print(a.half_of_100) # 50 a.half # AttributeError 


A veces quieres agotar un generador, pero no te importan los valores que produce. Sin embargo, le interesan algunos efectos secundarios, puede ser una excepción, escribir en un archivo, modificar una variable global, etc.

La forma conveniente y ampliamente utilizada de hacer esto es list(gen()) . Sin embargo, este código guarda todo el valor en la memoria solo para descartarlos inmediatamente después. Eso puede ser indeseable.

Si desea evitar esto, puede usar deque con el tamaño limitado en su lugar:

 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')</code> To be more semantically precise you better define your own <code>exhaust</code> function: <source lang="python"> def exhaust(iterable): for _ in iterable: pass 


Imagine que tiene un par de clases que son padre e hijo, dicen User y Admin . También tiene una función que toma una lista de usuarios como argumento. ¿Puedes proporcionar una lista de administradores entonces? La respuesta es no: la función puede agregar otro usuario a la lista de administradores que no es válido y rompe las garantías que proporciona la lista.

Sin embargo, puede proporcionar una Sequence de administradores ya que la Sequence es de solo lectura. El término apropiado aquí es Sequence es covariante en su tipo de miembros.

Puede definir tipos covariantes proporcionando covariant=True como argumento 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, la función puede requerir un contenedor solo para poner administradores allí. Dichos contenedores de solo escritura son contravariantes en su tipo de miembros:

 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 .

Source: https://habr.com/ru/post/470107/


All Articles