
Esta é a oitava seleção de dicas e programação em Python do meu feed @pythonetc.
Seleções anteriores:
Dois métodos implícitos de classe
Para criar um método de classe, você precisa usar o decorador 
@classmethod . Então, esse método pode ser chamado diretamente da classe, e não de suas instâncias, e tomará a classe como o primeiro argumento (geralmente é chamado 
cls , não 
self ).
No entanto, existem dois métodos de classe implícitos no modelo de dados Python: 
__new__ e 
__init_subclass__ . Eles funcionam como se também fossem decorados usando 
@classmethod , embora esse não seja o caso ( 
__new__ cria novas instâncias da classe e 
__init_subclass__ é o gancho chamado quando a classe derivada é criada).
 class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo()  
Gerentes de contexto assíncrono
Se você deseja que o gerenciador de contexto pause a rotina ao entrar ou sair do contexto, use gerenciadores assíncronos. Então, em vez de chamar 
m.__enter__() e 
m.__exit__() Python aguardará em 
m.__aenter__() e 
m.__aexit__() respectivamente.
Os gerenciadores de contexto assíncrono devem ser usados com o 
async with sintaxe:
 import asyncio class Slow: def __init__(self, delay): self._delay = delay async def __aenter__(self): await asyncio.sleep(self._delay / 2) async def __aexit__(self, *exception): await asyncio.sleep(self._delay / 2) async def main(): async with Slow(1): print('slow') loop = asyncio.get_event_loop() loop.run_until_complete(main()) 
Definindo um gerenciador de contexto assíncrono
A partir do Python 3.7, o 
contextlib fornece um decorador 
asynccontextmanager que permite definir um gerenciador de contexto assíncrono da mesma maneira que o 
contextmanager :
 import asyncio from contextlib import asynccontextmanager @asynccontextmanager async def slow(delay): half = delay / 2 await asyncio.sleep(half) yield await asyncio.sleep(half) async def main(): async with slow(1): print('slow') loop = asyncio.get_event_loop() loop.run_until_complete(main()) 
Nas versões mais antigas do idioma, você pode usar 
@asyncio_extras.async_contextmanager .
Operador unário mais
Python não possui um operador 
++ ; em vez disso, 
x += 1 . Mas, ao mesmo tempo, a sintaxe 
++x é válida (mas 
x++ não é mais).
O truque é que o Python tem um operador mais unário, e 
++x é na verdade 
x.__pos__().__pos__() . Isso pode ser abusado e fazer com que o 
++ funcione como um incremento (mas eu não recomendaria fazer isso):
 class Number: def __init__(self, value): self._value = value def __pos__(self): return self._Incrementer(self) def inc(self): self._value += 1 def __str__(self): return str(self._value) class _Incrementer: def __init__(self, number): self._number = number def __pos__(self): self._number.inc() x = Number(4) print(x)  
Objeto MagicMock
O objeto 
MagicMock permite que 
MagicMock pegue qualquer atributo e chame qualquer método. Com esse método de acesso, um novo mock é retornado. Além disso, você obtém o mesmo objeto stub se acessar o mesmo atributo (ou chamar o mesmo método):
 >>> from unittest.mock import MagicMock >>> m = MagicMock() >>> a = ma >>> b = mb >>> a is ma True >>> mx() is mx() True >>> mx() <MagicMock name='mock.x()' id='139769776427752'> 
Obviamente, esse código funcionará com acesso seqüencial a atributos em qualquer profundidade. Nesse caso, os argumentos do método são ignorados:
 >>> mabcd <MagicMock name='mock.abcd' id='139769776473480'> >>> mabcd <MagicMock name='mock.abcd' id='139769776473480'> >>> mx().y().z() <MagicMock name='mock.x().y().z()' id='139769776450024'> >>> mx(1).y(1).z(1) <MagicMock name='mock.x().y().z()' id='139769776450024'> 
E se você definir um valor para qualquer atributo, o stub não retornará mais:
 >>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13 
No entanto, isso não funciona com 
m[1][2] . O fato é que o 
MagicMock não manipula a chamada para o elemento, é apenas uma chamada de método:
 >>> m[1][2] = 3 >>> m[1][2] <MagicMock name='mock.__getitem__().__getitem__()' id='139769776049848'> >>> m.__getitem__.return_value.__getitem__.return_value = 50 >>> m[1][2] 50