
Es ist eine neue Auswahl an Tipps und Tricks zu Python und Programmierung von meinem Telegramm-Kanal @pythonetc.
Frühere Veröffentlichungen:
Zwei implizite Klassenmethoden
Um eine Klassenmethode zu erstellen, sollten Sie den Dekorator
@classmethod
. Diese Methode kann direkt von der Klasse aufgerufen werden, nicht von ihren Instanzen, und akzeptiert die Klasse als erstes Argument (normalerweise
cls
, nicht
self
).
Es gibt jedoch zwei implizite Klassenmethoden im Python-Datenmodell:
__new__
und
__init_subclass__
. Sie funktionieren genau so, als wären sie mit
@classmethod
dekoriert, außer sie sind es nicht. (
__new__
erstellt neue Instanzen einer Klasse.
__init_subclass__
ist ein Hook, der aufgerufen wird, wenn eine abgeleitete Klasse erstellt wird.)
class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo()
Asynchrone Kontextmanager
Wenn Sie möchten, dass ein Kontextmanager die Coroutine beim Eingeben oder Verlassen des Kontexts anhält, sollten Sie asynchrone Kontextmanager verwenden. Anstatt
m.__enter__()
und
m.__exit__()
Python wartet auf
m.__aenter__()
bzw. auf
m.__aexit__()
.
Asynchrone Kontextmanager sollten mit asynchroner 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())
Asynchronen Kontextmanager definieren
Seit Python 3.7 bietet
asynccontextmanager
den
asynccontextmanager
Dekorator, mit dem Sie den asynchronen Kontextmanager genauso definieren können 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())
Für ältere Versionen können Sie
@asyncio_extras.async_contextmanager
.
Unärer Plus-Operator
In Python gibt es keinen
++
Operator, stattdessen wird
x += 1
verwendet. Selbst
++x
ist jedoch immer noch eine gültige Syntax (
x++
jedoch nicht).
Der Haken ist, dass Python einen unären Plus-Operator hat und
++x
tatsächlich
x.__pos__().__pos__()
. Wir können diese Tatsache missbrauchen und ++ als Inkrement arbeiten lassen (allerdings nicht empfohlen):
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)
Das Magicmock-Objekt
Mit dem
MagicMock
Objekt können Sie ein beliebiges Attribut abrufen oder eine beliebige Methode aufrufen. Nach diesem Zugriff wird ein neues Modell zurückgegeben. Darüber hinaus erhalten Sie dasselbe Scheinobjekt, 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'>
Dies funktioniert offensichtlich mit sequentiellem Attributzugriff von jeder Tiefe. Methodenargumente werden jedoch 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'>
Sobald Sie einen Wert für ein Attribut festgelegt haben, wird kein Mock mehr zurückgegeben:
>>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13
Es funktioniert jedoch nicht mit
m[1][2]
. Der Grund dafür ist, dass der
MagicMock
von
MagicMock
nicht speziell behandelt wird, sondern lediglich ein 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