@Pythonetc Enero 2019



Esta es la octava selección de consejos y programación de Python de mi feed @pythonetc.

Selecciones anteriores:



Dos métodos de clase implícitos.


Para crear un método de clase, debe usar el decorador @classmethod . Entonces, este método se puede invocar directamente desde la clase, y no desde sus instancias, y tomará la clase como primer argumento (generalmente se llama por cls , no por self ).

Sin embargo, hay dos métodos de clase implícitos en el modelo de datos de Python: __new__ y __init_subclass__ . Funcionan como si también estuvieran decorados con @classmethod , aunque este no es el caso ( __new__ crea nuevas instancias de la clase, y __init_subclass__ es el gancho que se llama cuando se crea la clase derivada).

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

Administradores de contexto asíncrono


Si desea que el administrador de contexto pause la rutina al entrar o salir del contexto, use administradores asincrónicos. Luego, en lugar de llamar a m.__enter__() y m.__exit__() Python esperará en m.__aenter__() y m.__aexit__() respectivamente.

Los gestores de contexto asíncronos deben usarse con el async with 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 un administrador de contexto asíncrono


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

En versiones anteriores del lenguaje, puede usar @asyncio_extras.async_contextmanager .

Operador unario plus


Python no tiene un operador ++ ; en cambio, x += 1 . Pero al mismo tiempo, la sintaxis ++x es válida (pero x++ ya no lo es).

El truco es que Python tiene un operador unario más, y ++x es en realidad x.__pos__().__pos__() . Se puede abusar de esto y hacer que ++ funcione como un incremento (pero no recomendaría hacer esto):

 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 

Objeto MagicMock


El objeto MagicMock permite tomar cualquier atributo y llamar a cualquier método. Con este método de acceso, se devuelve un nuevo simulacro. Además, obtiene el mismo objeto de código auxiliar si accede al mismo atributo (o llama 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, este código funcionará con acceso secuencial a los atributos a cualquier profundidad. En este caso, los argumentos de los métodos son 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'> 

Y si establece un valor para cualquier atributo, el código auxiliar ya no devolverá:

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

Sin embargo, esto no funciona con m[1][2] . El hecho es que MagicMock no maneja la llamada al elemento, es solo 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/438778/


All Articles