@Pythonetc setembro de 2019



Uma nova seleção de dicas e programação em Python no meu feed @pythonetc.

Coleções anteriores


No asyncio um loop não precisa ser iniciado para conter tarefas. Você pode criar e interromper tarefas, mesmo quando o ciclo está parado. Se for interrompido, algumas tarefas podem 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('') # 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)) #  printed 

Resultado:

 * * || *  

Aguarde a conclusão de todas as tarefas antes de interromper o ciclo. Se isso não for feito, você poderá pular alguns blocos finally e alguns gerenciadores de contexto não serão desativados.


O Python permite substituir muitos operadores, incluindo o operador de deslocamento bit a bit. Aqui está um exemplo de criação de uma composição de funções usando este operador. As setas indicam a direção da transferência 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 


Ao definir uma classe, você pode passar argumentos para sua metaclasse. A notação de class suporta palavras-chave como argumentos: class Klass(Parent, arg='arg') . A palavra-chave metaclasse é reservada para a escolha de uma metaclasse e você pode usar outras pessoas como desejar.

Aqui está um exemplo de uma metaclasse que cria uma classe sem um dos atributos. O nome do 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ê precisa esgotar o gerador, mas ao mesmo tempo não está interessado nos valores que ele cria, mas em alguns efeitos colaterais. Por exemplo, uma exceção, gravando em um arquivo, alterando uma variável global etc.

Existe uma maneira conveniente e popular de fazer isso é list(gen()) . No entanto, esse método salva todos os valores na memória e os exclui imediatamente. Isso pode ser redundante. Se você deseja evitar esse comportamento, pode usar o deque com um limite de tamanho:

 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') 

Por uma questão de precisão semântica, você pode definir sua própria função de exhaust :

 def exhaust(iterable): for _ in iterable: pass 


Suponha que você tenha algumas classes - o filho principal, User e Admin . E você também tem uma função que usa uma lista de usuários como argumento. Você pode fornecer uma lista de administradores? Não: a função pode adicionar outro usuário à lista de administradores, o que é incorreto e viola as garantias fornecidas pela lista.

No entanto, você pode fornecer um tipo de Sequence porque é somente leitura. Mais precisamente, neste caso, Sequence é covariante no tipo de participantes.

Você pode definir tipos de covariant=True fornecendo covariant=True como um 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 outro lado, uma função pode exigir um contêiner apenas para colocar administradores nele. Esses contêineres, disponíveis apenas para gravação, são contrários aos tipos 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) 

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

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


All Articles