@Pythonetc November 2018 Zusammenstellung

Published on December 12, 2018

@Pythonetc November 2018 Zusammenstellung


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

    Vorherige Auswahl:



    Atypische Dekorateure


    Funktionsdekoratoren müssen nicht nur neue Funktionen zurückgeben, sie können auch einen anderen Wert zurückgeben:

    def call(*args, **kwargs):
        def decorator(func):
            return func(*args, **kwargs)
        return decorator
    @call(15)
    def sqr_15(x):
        return x * x
    assert sqr_15 == 225

    Dies ist nützlich, wenn Sie einfache Klassen mit nur einer Überschreibungsmethode erstellen möchten:

    from abc import ABCMeta, abstractmethod
    class BinaryOperation(metaclass=ABCMeta):
        def __init__(self, left, right):
            self._left = left
            self._right = right
        def __repr__(self):
            klass = type(self).__name__
            left = self._left
            right = self._right
            return f'{klass}({left}, {right})'
        @abstractmethod
        def do(self):
            pass
        @classmethod
        def make(cls, do_function):
            return type(
                do_function.__name__,
                (BinaryOperation,),
                dict(do=do_function),
            )
    class Addition(BinaryOperation):
        def do(self):
            return self._left + self._right
    @BinaryOperation.make
    def Subtraction(self):
        return self._left - self._right


    __length_hint__


    Mit dem PEP 424 können Generatoren und andere iterierbare Objekte, die keine bestimmte vorbestimmte Größe haben, ihre ungefähre Länge zurückgeben. Dieser Generator gibt beispielsweise ungefähr 50 Elemente zurück:

    (x for x in range(100) if random() > 0.5)

    Wenn Sie etwas Iterierbares schreiben und eine ungefähre Länge zurückgeben möchten, definieren Sie die Methode __length_hint__. Und wenn Sie die Länge genau kennen, dann verwenden Sie __len__. Wenn Sie ein iteriertes Objekt verwenden und wissen möchten, wie lang es sein soll, verwenden Sie operator.length_hint.

    mit Generator


    Der Bediener inkann mit Generatoren verwendet werden: x in g. In diesem Fall wird Python so lange wiederholt, gbis es gefunden wird xoder bis es endet g.

    >>> def g():
    ...     print(1)
    ...     yield 1
    ...     print(2)
    ...     yield 2
    ...     print(3)
    ...     yield 3
    ...
    >>> 2 in g()
    1
    2
    True

    range()es funktioniert jedoch etwas besser. Es gibt eine magisch neu definierte Methode __contains__, aufgrund derer die rechnerische Komplexität ingleich O (1) wird:

    In [1]: %timeit 10**20 in range(10**30)
    375 ns ± 10.7 ns per loop

    Bitte beachten Sie, dass xrange()dies mit einer Funktion von Python 2 nicht funktioniert.

    Operatoren + = und +


    Es gibt zwei verschiedene Operatoren in Python: +=und +. Methoden sind für ihr Verhalten __iadd__und __add__dementsprechend verantwortlich.

    class A:
        def __init__(self, x):
            self.x = x
        def __iadd__(self, another):
            self.x += another.x
            return self
        def __add__(self, another):
            return type(self)(self.x + another.x)

    Wenn __iadd__nicht definiert, a += bfunktioniert es als a = a + b.

    Der semantische Unterschied zwischen +=und +liegt in der Tatsache, dass der erste das Objekt ändert und der zweite ein neues erstellt:

    >>> a = [1, 2, 3]
    >>> b = a
    >>> a += [4]
    >>> a
    [1, 2, 3, 4]
    >>> b
    [1, 2, 3, 4]
    >>> a = a + [5]
    >>> a
    [1, 2, 3, 4, 5]
    >>> b
    [1, 2, 3, 4]

    Funktion als Attribut einer Klasse


    Sie können eine Funktion nicht als Klassenattribut speichern, da sie automatisch in eine Methode konvertiert wird, wenn auf sie über eine Instanz zugegriffen wird:

    >>> class A:
    ...     CALLBACK = lambda x: x ** x
    ...
    >>> A.CALLBACK
    <function A.<lambda> at 0x7f68b01ab6a8>
    >>> A().CALLBACK
    <bound method A.<lambda> of <__main__.A object at 0x7f68b01aea20>>
    >>> A().CALLBACK(4)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: <lambda>() takes 1 positional argument but 2 were given

    Sie können die Funktion in einem einfachen Deskriptor betrügen und umschließen:

    >>> class FunctionHolder:
    ...     def __init__(self, f):
    ...         self._f = f
    ...     def __get__(self, obj, objtype):
    ...         return self._f
    ...
    >>> class A:
    ...     CALLBACK = FunctionHolder(lambda x: x ** x)
    ...
    >>> A().CALLBACK
    <function A.<lambda> at 0x7f68b01ab950>

    Sie können die Situation auch verlassen, indem Sie die Klassenmethode anstelle eines Attributs verwenden.

    class A:
        @classmethod
        def _get_callback(cls):
            return lambda x: x ** x