@ Pythonetc Januar 2019



Dies ist die achte Auswahl von Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.

Vorherige Auswahl:



Zwei implizite Klassenmethoden


Um eine Klassenmethode zu erstellen, müssen Sie den @classmethod Dekorator verwenden. Dann kann diese Methode direkt von der Klasse und nicht von ihren Instanzen aufgerufen werden, und die Klasse wird als erstes Argument verwendet (normalerweise heißt sie cls , nicht self ).

Es gibt jedoch zwei implizite Klassenmethoden im Python-Datenmodell: __new__ und __init_subclass__ . Sie funktionieren so, als wären sie auch mit @classmethod dekoriert, obwohl dies nicht der Fall ist ( __new__ erstellt neue Instanzen der Klasse, und __init_subclass__ ist der Hook, der beim __init_subclass__ der abgeleiteten Klasse aufgerufen wird).

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

Asynchrone Kontextmanager


Wenn der Kontextmanager beim Betreten oder Verlassen des Kontexts die Coroutine anhalten soll, verwenden Sie asynchrone Manager. Anstatt m.__enter__() und m.__exit__() m.__aenter__() Python auf m.__aenter__() bzw. m.__aexit__() .

Asynchrone Kontextmanager müssen mit der async with Syntax verwendet werden:

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

Definieren eines asynchronen Kontextmanagers


Ab Python 3.7 bietet asynccontextmanager einen asynccontextmanager Dekorator, mit dem Sie einen asynchronen Kontextmanager auf dieselbe Weise wie den 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()) 

In älteren Versionen der Sprache können Sie @asyncio_extras.async_contextmanager .

Unärer Plus-Operator


Python hat keinen ++ Operator, stattdessen wird x += 1 . Gleichzeitig ist die Syntax ++x gültig ( x++ jedoch nicht mehr).

Der Trick ist, dass Python einen unären Plus-Operator hat und ++x tatsächlich x.__pos__().__pos__() . Dies kann missbraucht werden und ++ wie ein Inkrement funktionieren lassen (aber ich würde dies nicht empfehlen):

 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 

MagicMock-Objekt


Mit dem MagicMock Objekt können MagicMock ein beliebiges Attribut übernehmen und eine beliebige Methode aufrufen. Mit dieser Zugriffsmethode wird ein neuer Mock zurückgegeben. Darüber hinaus erhalten Sie dasselbe Stub-Objekt, wenn Sie auf dasselbe Attribut zugreifen (oder dieselbe Methode aufrufen):

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

Offensichtlich funktioniert dieser Code mit sequentiellem Zugriff auf Attribute in jeder Tiefe. In diesem Fall werden die Argumente der Methoden ignoriert:

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

Und wenn Sie einen Wert für ein Attribut festlegen, gibt der Stub nicht mehr zurück:

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

Dies funktioniert jedoch nicht mit m[1][2] . Tatsache ist, dass MagicMock den Aufruf des Elements nicht verarbeitet, sondern nur einen Methodenaufruf:

 >>> 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/de438778/


All Articles