Consejos y trucos de mi canal de Telegram @pythonetc, enero 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:



Dos métodos de clase implícitos.


Para crear un método de clase, debe usar el decorador @classmethod . Este método puede llamarse directamente desde la clase, no desde sus instancias, y acepta la clase como primer argumento (generalmente llamado cls , no self ).

Sin embargo, hay dos métodos de clase implícitos en el modelo de datos de Python: __new__ y __init_subclass__ . Funcionan exactamente como si estuvieran decorados con @classmethod excepto que no lo están. ( __new__ crea nuevas instancias de una clase, __init_subclass__ es un __init_subclass__ que se llama cuando se crea una clase derivada).

 class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo() # <class '__main__.Foo'> 

Gestores de contexto asincrónicos


Si desea que un administrador de contexto suspenda la rutina al entrar o salir del contexto, debe usar administradores de contexto asíncronos. En lugar de llamar a m.__enter__() y m.__exit__() Python sí espera m.__aenter__() y espera m.__aexit__() respectivamente.

Los gestores de contexto asincrónicos deben usarse con asincronización con sintaxis:

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

Definición de gestor de contexto asíncrono


Desde Python 3.7, contextlib proporciona el decorador asynccontextmanager que le permite definir el administrador de contexto asíncrono exactamente de la misma manera que 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()) 

Para versiones anteriores, puede usar @asyncio_extras.async_contextmanager .

Operador unario plus


No hay un operador ++ en Python, se usa x += 1 su lugar. Sin embargo, incluso ++x sigue siendo una sintaxis válida (pero x++ no lo es).

El problema es que Python tiene un operador unario más, y ++x es en realidad x.__pos__().__pos__() . Podemos abusar de este hecho y hacer que ++ funcione como un incremento (aunque no se recomienda):

 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) # 4 ++x print(x) # 5 

El objeto magicmock


El objeto MagicMock permite obtener cualquier atributo de él o llamar a cualquier método. Nuevo simulacro será devuelto a tal acceso. Además, obtienes el mismo objeto simulado si accedes al mismo atributo (o llamas al mismo 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, esto funcionará con el acceso secuencial de atributos de cualquier profundidad. Sin embargo, los argumentos del método se ignoran:

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

Una vez que establece un valor para cualquier atributo, ya no devuelve simulacro:

 >>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13 

Sin embargo, no funciona con m[1][2] . La razón es que el acceso al elemento no es tratado especialmente por MagicMock , es simplemente una llamada al 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 

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


All Articles