Dicas e truques do meu canal Telegram @pythonetc, setembro de 2019



É uma nova seleção de dicas e truques sobre Python e programação do meu canal Telegram @pythonetc.

Publicações anteriores


asyncio loop asyncio não precisa ser executado para ter tarefas. Você pode criar e parar tarefas, mesmo que o loop esteja parado no momento. Se o loop for interrompido, algumas tarefas poderão permanecer incompletas para sempre.

 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 

Saída:

 * * || * x 

Você deve aguardar todas as tarefas antes de interromper o loop. Caso contrário, alguns blocos finally serão ignorados e alguns gerenciadores de contexto não serão encerrados.


O Python permite sobrecarregar muitos operadores diferentes e o operador shift é um deles. Aqui está um exemplo de como criar uma composição de função usando este operador. Aqui, sinais em forma de seta mostram a direção do fluxo de dados:

 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 


Você pode passar argumentos para a metaclasse personalizada a partir da definição de classe. A notação de class suporta argumentos de palavra-chave: class Klass(Parent, arg='arg') . A palavra-chave metaclass é reservada para definir a metaclasse, mas outras são de uso gratuito.

Aqui está um exemplo de metaclasse que cria classe sem um dos atributos. O nome desse atributo é fornecido no argumento de 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 


Às vezes, você deseja esgotar um gerador, mas não se importa com os valores que ele gera. Você se preocupa com algum efeito colateral, porém, pode ser uma exceção, gravar em um arquivo, modificação global de variável etc.

A maneira conveniente e amplamente usada de fazer isso é list(gen()) . No entanto, esse código salva todo o valor na memória apenas para descartá-los imediatamente depois. Isso pode ser indesejável.

Se você quiser evitar isso, use o deque com tamanho limitado:

 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 você tem um par de classes pai e filho, digamos User e Admin . Você também tem uma função que recebe uma lista de usuários como argumento. Você pode fornecer uma lista de administradores então? A resposta é não: a função pode adicionar outro usuário à lista de administradores inválida e quebras que a lista fornece.

No entanto, você pode fornecer uma Sequence de administradores, pois a Sequence é somente leitura. O termo apropriado aqui é Sequence é covariante em seu tipo de membros.

Você pode definir tipos covariantes fornecendo covariant=True como um 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 outro lado, a função pode exigir apenas um contêiner para colocar os administradores lá. Esses contêineres somente para gravação são contrários ao tipo de membros:

 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) 

Classes que não são covariantes nem contravariantes são chamadas invariantes .

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


All Articles