Funktionale Funktionen erstellen

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Abschnitt wird die Idee der Verwendung von Funktionen zur Erstellung anderer Funktionen vorgestellt.

Einführung

Betrachten Sie die folgende Funktion.

def add(x, y):
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Dies ist eine Funktion, die eine andere Funktion zurückgibt.

>>> a = add(3,4)
>>> a
<function add.<locals>.do_add at 0x7f27d8a38790>
>>> a()
Adding 3 4
7

Lokale Variablen

Beobachten Sie, wie die innere Funktion auf Variablen verweist, die von der äußeren Funktion definiert wurden.

def add(x, y):
    def do_add():
        ## `x` und `y` sind oberhalb von `add(x, y)` definiert
        print('Adding', x, y)
        return x + y
    return do_add

Beobachten Sie weiter, dass diese Variablen irgendwie am Leben bleiben, nachdem add() abgeschlossen ist.

>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
Adding 3 4      ## Woher kommen diese Werte?
7

Closures

Wenn eine innere Funktion als Ergebnis zurückgegeben wird, ist diese innere Funktion als Closure bekannt.

def add(x, y):
    ## `do_add` ist ein Closure
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Wesentliches Merkmal: Ein Closure behält die Werte aller Variablen bei, die das spätere ordnungsgemäße Ausführen der Funktion erforderlich macht. Denken Sie sich ein Closure als eine Funktion plus eine zusätzliche Umgebung, die die Werte der Variablen enthält, auf die es angewiesen ist.

Verwendung von Closures

Closures sind ein wesentliches Merkmal von Python. Ihre Verwendung ist jedoch oft subtil. Allgemeine Anwendungen:

  • Verwendung in Callback-Funktionen.
  • Verzögerte Auswertung.
  • Dekoratorfunktionen (später).

Verzögerte Auswertung

Betrachten Sie eine Funktion wie diese:

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

Verwendungsbeispiel:

def greeting():
    print('Hello Guido')

after(30, greeting)

after führt die bereitgestellte Funktion... später aus.

Closures tragen zusätzliche Informationen mit sich.

def add(x, y):
    def do_add():
        print(f'Adding {x} + {y} -> {x+y}')
    return do_add

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

after(30, add(2, 3))
## `do_add` hat die Referenzen x -> 2 und y -> 3

Code-Wiederholung

Closures können auch als Technik zur Vermeidung von übermäßiger Code-Wiederholung verwendet werden. Sie können Funktionen schreiben, die Code erzeugen.

Übung 7.7: Verwendung von Closures zur Vermeidung von Wiederholungen

Eine der leistungsfähigsten Eigenschaften von Closures ist ihre Verwendung bei der Erzeugung von wiederholendem Code. Wenn Sie sich an Übung 5.7 erinnern, denken Sie an den Code zur Definition einer Eigenschaft mit Typüberprüfung.

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price
  ...
    @property
    def shares(self):
        return self._shares

    @shares.setter
    def shares(self, value):
        if not isinstance(value, int):
            raise TypeError('Expected int')
        self._shares = value
  ...

Anstatt diesen Code immer wieder zu tippen, können Sie ihn mithilfe eines Closures automatisch erstellen.

Erstellen Sie eine Datei typedproperty.py und legen Sie den folgenden Code darin ab:

## typedproperty.py

def typedproperty(name, expected_type):
    private_name = '_' + name
    @property
    def prop(self):
        return getattr(self, private_name)

    @prop.setter
    def prop(self, value):
        if not isinstance(value, expected_type):
            raise TypeError(f'Expected {expected_type}')
        setattr(self, private_name, value)

    return prop

Versuchen Sie es nun, indem Sie eine Klasse wie diese definieren:

from typedproperty import typedproperty

class Stock:
    name = typedproperty('name', str)
    shares = typedproperty('shares', int)
    price = typedproperty('price', float)

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Versuchen Sie, eine Instanz zu erstellen und zu überprüfen, ob die Typüberprüfung funktioniert.

>>> s = Stock('IBM', 50, 91.1)
>>> s.name
'IBM'
>>> s.shares = '100'
... sollte einen TypeError erzeugen...
>>>

Übung 7.8: Vereinfachung von Funktionsaufrufen

Im obigen Beispiel könnten Benutzer die Aufrufe wie typedproperty('shares', int) als etwas umständlich bei der Eingabe empfinden - insbesondere wenn sie oft wiederholt werden. Fügen Sie die folgenden Definitionen zur typedproperty.py-Datei hinzu:

String = lambda name: typedproperty(name, str)
Integer = lambda name: typedproperty(name, int)
Float = lambda name: typedproperty(name, float)

Nehmen Sie nun die Stock-Klasse auf und verwenden Sie stattdessen diese Funktionen:

class Stock:
    name = String('name')
    shares = Integer('shares')
    price = Float('price')

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Ah, das ist etwas besser. Der wichtigste Punkt hier ist, dass Closures und lambda oft verwendet werden können, um Code zu vereinfachen und lästige Wiederholungen zu eliminieren. Dies ist oft vorteilhaft.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab "Returning Functions" abgeschlossen. Sie können in LabEx weitere Labs ausprobieren, um Ihre Fähigkeiten zu verbessern.