
Uma nova seleção de dicas e programação em Python no meu feed @pythonetc.
←
Coleções anterioresNo
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('')
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))
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)
À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 .