Compilation @ pythonetc, Januar 2019



    Dies ist die achte Sammlung von Tipps zu Python und zur Programmierung aus dem @ pythonetc-Kanal meines Autors.

    Vorherige Auswahl:



    Zwei implizite Klassenmethoden


    Um eine Klassenmethode zu erstellen, müssen Sie einen Dekorator verwenden @classmethod. Dann kann diese Methode direkt von der Klasse aus aufgerufen werden und nicht von ihren Instanzen. Die Klasse wird als erstes Argument verwendet (es wird normalerweise aufgerufen cls, nicht self).

    Im Python-Datenmodell gibt es jedoch zwei implizite Klassenmethoden: __new__und __init_subclass__. Sie funktionieren so, als wären sie auch mit Hilfe dekoriert worden @classmethod, obwohl dies nicht der Fall ist (es __new__erstellt neue Instanzen der Klasse, ist aber __init_subclass__ein Hook, der beim Erstellen einer 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 Sie möchten, dass der Kontextmanager Coruntine beim Eintreten oder Verlassen des Kontexts unterbricht, verwenden Sie asynchrone Manager. Dann statt Aufruf m.__enter__()und m.__exit__()Python tun await auf m.__aenter__()und m.__aexit__()sind.

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

    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 Sie den asynchronen Kontextmanager


    Ab Python 3.7 wird contextlibein Dekorationsprogramm bereitgestellt asynccontextmanager, mit dem Sie einen asynchronen Kontextmanager auf dieselbe Weise definieren können 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 verwenden @asyncio_extras.async_contextmanager.

    Unärer Plus-Operator


    Es gibt keinen Operator in Python ++, er wird stattdessen verwendet x += 1. Gleichzeitig ist die Syntax ++xjedoch gültig (und x++nicht mehr).

    Der Trick ist, dass es in Python einen unären Operator "Plus" gibt, und das ist es ++xtatsächlich x.__pos__().__pos__(). Dies kann missbraucht werden und kann ++als Inkrement verwendet werden (aber ich würde das 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 Objekt MagicMockkönnen Sie jedes Attribut übernehmen und eine beliebige Methode aufrufen. Bei dieser Zugriffsmethode wird ein neuer Stub (Mock) zurückgegeben. Außerdem erhalten Sie dasselbe Stub-Objekt, wenn Sie auf dasselbe Attribut zugreifen (oder dieselbe Methode aufrufen):

    >>> from unittest.mock import MagicMock
    >>> m = MagicMock()
    >>> a = m.a
    >>> b = m.b
    >>> a is m.a
    True
    >>> m.x() is m.x()
    True
    >>> m.x()
    <MagicMock name='mock.x()' id='139769776427752'>

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

    >>> m.a.b.c.d
    <MagicMock name='mock.a.b.c.d' id='139769776473480'>
    >>> m.a.b.c.d
    <MagicMock name='mock.a.b.c.d' id='139769776473480'>
    >>> m.x().y().z()
    <MagicMock name='mock.x().y().z()' id='139769776450024'>
    >>> m.x(1).y(1).z(1)
    <MagicMock name='mock.x().y().z()' id='139769776450024'>

    Wenn Sie einen Attributwert festlegen, wird der Stub nicht mehr zurückgegeben:

    >>> m.a.b.c.d = 42
    >>> m.a.b.c.d
    42
    >>> m.x.return_value.y.return_value = 13
    >>> m.x().y()
    13

    Dies funktioniert jedoch nicht mit m[1][2]. Tatsache ist, dass MagicMockder Aufruf des Elements nicht verarbeitet wird. Es handelt sich lediglich um 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

    Jetzt auch beliebt: