Dies ist ein ** Feature-Gedicht **, das die Funktionen der funktionalen Programmierung in Python beschreibt. Ich habe es geschrieben, damit Sie es verstehen können, ohne Python zu kennen. Lesen Sie es daher auch, wenn Sie PHP, Java oder JavaScript sind.
【Tor】
[Schreibmotiv]
[Zielgruppe Leser]
[Serienartikel]
Funktionen höherer Ordnung sind Funktionen, die die Funktion als Daten behandeln. Speziell:
Wie ich letztes Mal erklärt habe, sind Funktionen auch Objekte in Python, sodass sie als Daten behandelt werden können. Daher können Sie eine Funktion innerhalb einer ** Funktion ** erstellen. Zum Beispiel:
def create_func():
def newfunc(i): #Erstellen Sie eine neue Funktion in der Funktion
if i % 2 == 0:
return "even"
else:
return "odd"
return newfunc #Gib es zurück
##Versuchen Sie es mit
fn = create_func() #Erstellen Sie eine neue Funktion und weisen Sie sie der Variablen fn zu
print(fn(2)) #2 ist gerade"even"Wird ausgegeben
print(fn(3)) #Weil 3 ungerade ist"odd"Wird ausgegeben
Eine Funktion, die auf diese Weise eine Funktion erstellt und zurückgibt, wird auch als Funktion höherer Ordnung bezeichnet. Außerdem wird die in der Funktion erzeugte Funktion manchmal als "Intrafunktionsfunktion" bezeichnet (im Folgenden wird sie zum leichteren Verständnis als "innere Funktion" bezeichnet).
In anderen Büchern wird es als "eine Funktion, die eine Funktion zurückgibt" anstelle von "eine Funktion, die eine Funktion erstellt" erklärt. Aber ** das Wichtigste ist, eine neue Funktion zu erstellen, anstatt eine Funktion zurückzugeben **. In diesem Gedicht werde ich es als "eine Funktion, die eine Funktion erzeugt" erklären.
Die folgenden zwei Funktionen sind beide sehr ähnlich. Der einzige Unterschied ist der Wert, der zurückgegeben wird, wenn er gerade ist, und der Wert, der zurückgegeben wird, wenn er ungerade ist.
def create_func1():
def newfunc(i):
if i % 2 == 0:
return "even" #Wenn gerade"even"Gib es zurück
else:
return "odd" #Wenn es seltsam ist"odd"Gib es zurück
return newfunc
def create_func2():
def newfunc(i):
if i % 2 == 0:
return "#fcc" #Wenn gerade"#fcc" (Hellrot)Gib es zurück
else:
return "#ccf" #Wenn es seltsam ist"#ccf" (Hellblau)Gib es zurück
return newfunc
Lassen Sie uns diese beiden ähnlichen Funktionen teilen.
def create_func(even_val, odd_val): #Argument zur äußeren Funktion hinzufügen
def newfunc(i): #Innere Funktion(Generierte Funktion)Ist ...
if i % 2 == 0:
return even_val #Wenn gerade_Geändert, um val zurückzugeben
else:
return odd_val #Wenn es seltsam ist_Geändert, um val zurückzugeben
return newfunc
Es gibt zwei Punkte:
Auf diese Weise können Sie ** eine Reihe von Funktionen mit leicht unterschiedlichem Verhalten wie folgt generieren **.
## "even"Wann"odd"Generieren Sie eine Funktion, die zurückgibt
fn1 = create_func("even", "odd")
print(fn1(0)) #=> even
print(fn1(1)) #=> odd
print(fn1(2)) #=> even
print(fn1(3)) #=> odd
## "#fcc"Wann"#ccf"Generieren Sie eine Funktion, die zurückgibt
fn2 = create_func("#fcc", "#ccf")
print(fn2(0)) #=> #fcc
print(fn2(1)) #=> #ccf
print(fn2(2)) #=> #fcc
print(fn2(3)) #=> #ccf
Vergleichen wir dies mit den Funktionen im vorherigen Abschnitt.
Der wichtige Punkt ist, dass ** die innere Funktion auf die lokalen Variablen (in diesem Fall die Argumente) der äußeren Funktion zugreift **. Eine solche Funktion wird als "näher" bezeichnet. Dies wird im nächsten Abschnitt erläutert.
Was ist eine Schließung? Laut Wikipedia:
Closure (näher, Englisch: Closure), Function Closure ist eine Art Funktionsobjekt in der Programmiersprache. In einigen Sprachen wird es durch Lambda-Ausdrücke und anonyme Funktionen realisiert. Es zeichnet sich dadurch aus, dass andere Variablen als Argumente in der Umgebung aufgelöst werden, in der es definiert ist (statischer Bereich), und nicht in der Umgebung zum Zeitpunkt der Ausführung. Man kann sagen, dass es sich um ein Paar aus einer Funktion und einer Umgebung handelt, die sie bewertet. Dieses Konzept lässt sich mindestens auf die SECD-Maschinen der 1960er Jahre zurückführen.
(... weggelassen ...)
Closure ist ein Mechanismus zum Freigeben der Umgebung innerhalb eines Programms. Lexikalische Variablen unterscheiden sich von globalen Variablen darin, dass sie den globalen Namespace nicht belegen. Es unterscheidet sich von einer Objektinstanzvariablen auch dadurch, dass es eher an einen Funktionsaufruf als an eine Objektinstanz gebunden ist.
Nun, ich weiß nicht was es ist. Es scheint, dass Sie ungefähr 145 IQ benötigen, um diese Erklärung zu verstehen.
Stellen Sie sich einen Abschluss vorerst als eine innere Funktion ** vor, die auf die lokalen Variablen der äußeren Funktion zugreift (was akademisch ungenau sein kann, aber für praktische Zwecke ist dieses Verständnis ausreichend. ).
In den Funktionen im vorherigen Abschnitt greift die innere Funktion beispielsweise auf die lokalen Variablen der äußeren Funktion zu (Hinweis: Das Argument ist auch eine der lokalen Variablen). Daher ist die innere Funktion ein Verschluss.
##Dies ist die äußere Funktion
def create_func(even_val, odd_val): #Beachten Sie, dass die Argumente auch lokale Variablen sind
##Dies ist die innere Funktion
## (Da wir auf die äußere lokale Variable zugreifen, ist diese Funktion ein Abschluss)
def newfunc(i):
if i % 2 == 0:
return even_val #Zugriff auf die äußere lokale Variable
else:
return odd_val #Zugriff auf die äußere lokale Variable
return newfunc
Im Gegensatz dazu ist der nächste Fall kein Abschluss. Dies liegt daran, dass die innere Funktion nicht auf die äußere lokale Funktion zugreift.
def create_func(even_val, odd_val):
##Dies ist keine Schließung
## (Weil die innere Funktion nicht auf die äußere lokale Variable zugreift)
def newfunc(i):
if i % 2 == 0:
return "even"
else:
return "odd"
return newfunc
Auch der nächste Fall ist kein Abschluss. Dies liegt daran, dass die innere Funktion auf eine globale Variable zugreift.
##Dies sind globale Variablen
even_val = "#fcc"
odd_val = "#ccf"
def create_func():
##Dies ist auch keine Schließung
## (Ich greife auf eine globale Variable zu)
def newfunc(i):
if i % 2 == 0:
return even_val
else:
return odd_val
return newfunc
Um ein tieferes Verständnis zu erhalten, schauen wir uns an, wie Verschlüsse funktionieren.
Aus Sicht der Implementierung ist ** der tatsächliche Abschlusszustand "eine Funktion mit einer angehängten Variablentabelle" **. Der Unterschied zwischen Abschlüssen und regulären Funktionen besteht im Vorhandensein oder Fehlen dieser Variablentabelle.
Das Folgende ist ein Beispiel in Python3. Sie können sehen, dass die Variablentabelle am Abschluss haftet.
##Funktion, die einen Abschluss zurückgibt
def create_func(even_val, odd_val):
def newfunc(i):
if i % 2 == 0: return even_val
else: return odd_val
return newfunc # (Nicht nur eine Funktion)Schließen Sie den Verschluss zurück
##Wenn Sie einen Verschluss erstellen ...
fn = create_func("red", "blue")
## __closure__Sie können sehen, dass die Variablentabelle angehängt ist
print(fn.__closure__[0].cell_contents) #=> red
print(fn.__closure__[1].cell_contents) #=> blue
##Es scheint, dass Sie es nicht willkürlich ändern können
fn.__closure__[0].cell_contents = "white"
#=> AttributeError: attribute 'cell_contents' of 'cell' objects is not writable
Auch eine normale Funktion (kein Abschluss) hat diese Variablentabelle nicht.
##Eine Funktion, die eine normale Funktion zurückgibt
def create_func():
def newfunc(i):
if i % 2 == 0: return "even"
else: return "odd"
return newfunc # (Keine Schließung)Gibt nur eine Funktion zurück
##Für normale Funktionen gibt es keine Variablentabelle
fn = create_func()
print(fn.__closure__) #=> None
Wie Sie sehen können, ist dem Abschluss eine variable Tabelle zugeordnet. Mit anderen Worten, ** Closure ist eine zustandsbasierte Funktion **. Es wird oft gesagt, dass "Funktionen keinen Status haben", aber beachten Sie, dass Schließungen dies nicht tun.
<Addition 1> Er kommentierte: "Ist es implementierungsabhängig, dass es langsamer ist als lokale Variablen?" Das stimmt, es ist langsamer als lokale Variablen in Python, aber es ist durchaus möglich, dass es nicht in anderen Sprachen und Prozessoren vorliegt. Vielen Dank für den Hinweis. </ Addition 1>
<Addition 2> Er wies darauf hin, dass "Ist es nicht gut zu sagen, dass eine Schließung einen" Zustand "hat? Es scheint besser zu sein," Attribut "oder" Umgebung "zu sagen (Danke). Persönlich denke ich, dass der Ausdruck "Zustand" aus den folgenden Gründen in Ordnung ist. ・ Das Wort „Status“ enthält nicht, ob es aktualisiert werden kann oder nicht. ・ Es ist nur eine Frage der Perspektive, "state" zu einem "Attribut" zu machen (eine Funktion kann als Berechnungsformel für die Rückgabe eines Werts und als Unterscheidungsformel für die Rückgabe eines booleschen Werts angesehen werden, ist aber dennoch eine Funktion. Gleich wie) ・ "Umwelt" ist ein Begriff, den Sie in Fachbüchern sehen können, aber die Definition ist vage und verwirrt Anfänger, und die Substanz ist nichts weiter als eine variable Tabelle. Was ich jedoch "Zustand" nenne, kann etwas sein, das Anfänger Wikipedia Wiki nennen. Wenn Sie interessiert sind, hier, Serie, [Tweet]( Siehe https://twitter.com/tanakahisateru/status/606098308411453442). </ Addition 2>
Haben Sie schon einmal die Erklärung gehört, dass "ein Objekt eine Variable mit einer angehängten Funktion ist"? ** Wenn eine Funktion an eine Variable in einem Objekt angehängt ist und eine Variable an eine Funktion in einem Abschluss angehängt ist **, sind das Objekt und der Abschluss wahrscheinlich sehr ähnlich.
Tatsächlich können ** Verschlüsse durch Objekte nachgeahmt werden **. Das Folgende ist ein Beispiel.
##Die Funktion, die den im vorherigen Abschnitt angezeigten Abschluss zurückgibt
def create_func(even_val, odd_val):
def func(i):
if i % 2 == 0:
return even_val
else:
return odd_val
return func
##Erstellen Sie eine Klasse, die es nachahmt
class CreateFunc(object):
def __init__(self, even_val, odd_val): #Konstrukteur
self.even_val = even_val
self.odd_val = odd_val
def __call__(self, i):
if i % 2 == 0:
return self.even_val
else:
return self.odd_val
##Wie benutzt man
obj = CreteFunc("red", "blue") #Objekt erstellen(Anmerkung 1)
print(obj.__call__(0)) #=> red
print(obj.__call__(1)) #=> blue
print(obj.__call__(2)) #=> red
print(obj.__call__(3)) #=> blue
##Das ist in Ordnung(Anmerkung 2)
print(obj(0)) #=> red
print(obj(1)) #=> blue
print(obj(2)) #=> red
print(obj(3)) #=> blue
## (Anmerkung 1)In Python benötigen Sie beim Erstellen eines Objekts keinen neuen Operator.
##Sie können ein Objekt erstellen, indem Sie die Klasse so aufrufen, als wäre es eine Funktion.
## (Anmerkung 2)Obj in Python.__call__()Obj()Kann wie genannt werden.
Auf diese Weise kann das Objekt nachahmen, was der Verschluss tut. Dies bedeutet, dass Sprachen, die keine Schließungen unterstützen, dasselbe tun können wie Schließungen.
Wie Sie im folgenden Vergleich sehen können, sind die Abschlüsse (in diesem Fall) präziser.
##Nähere Version##Objektversion
def create_func(even, odd): class CreateFunc(object):
def __init__(self, even, odd):
self.even = even
self.odd = odd
def func(i): def __call__(self, i):
if i % 2 == 0: if i % 2 == 0:
return even return self.even
else: else:
return odd return self.odd
return func
fn = create_func("red", "blue") obj = CreateFunc("red", "blue")
fn(0) obj.__call__(0) # or obj(0)
fn(1) obj.__call__(1) # or obj(1)
Wie Sie oben sehen können, sind Verschlüsse und Objekte sehr ähnlich. Wenn es ein schönes IQ145-Mädchen gibt, das darauf besteht, dass "die Objektorientierung, die den Zustand hält, falsch ist! Die funktionale Sprache ist korrekt!", "Aber das Schließen einer höheren funktionalen Sprache ist einem Objekt sehr ähnlich, nicht wahr?" Einschließlich des Punktes, einen Staat zu haben. " Ich bin sicher, IQ145 wird Ihnen eine gute Antwort geben.
"Funktionen höherer Ordnung, die Funktionen generieren" sind beim Erstellen von Wrapper-Funktionen sehr nützlich. Um die Bequemlichkeit von "Funktionen höherer Ordnung, die Funktionen erzeugen" zu zeigen, werden hier "Wrapper-Funktionen" beschrieben.
** Wrapper-Funktionen sind Funktionen, die beim Aufrufen einer vorhandenen Funktion eine andere Verarbeitung hinzufügen **. Zum Beispiel:
Natürlich gibt es auch andere Möglichkeiten, aber vorerst sollten Sie die oben genannten drei Typen beibehalten.
Schauen wir uns ein konkretes Beispiel an.
table = [
{"name": "Haruhi", "size": "C"},
{"name": "Mikuru", "size": "E"},
{"name": "Yuki", "size": "A"},
]
##Ursprüngliche Funktion
def search(name): #Funktion zum Abrufen von Daten
for d in table:
if d["name"] == name:
return d
return None
## (A)Fügen Sie Argumente hinzu oder optimieren Sie sie, bevor Sie die ursprüngliche Funktion aufrufen
def search1(name):
if name == "Michiru":
name = "Mikuru" #Basteln Sie die Argumente
return search(name) #Rufen Sie die ursprüngliche Funktion auf
## (B)Rufen Sie die ursprüngliche Funktion auf und erstellen Sie dann den Rückgabewert
def search2(name, targets=["Mikuru"]):
ret = search(name) #Rufen Sie die ursprüngliche Funktion auf
if name in targets:
ret["size"] = "Es ist eine verbotene Angelegenheit" #Stellen Sie den Rückgabewert her
return ret
## (C)Fügen Sie beim Aufrufen der ursprünglichen Funktion die Vor- und Nachbearbeitung hinzu
def search3(name):
print("** name=%s" % name) #Vorverarbeitung
ret = search(name) #Rufen Sie die ursprüngliche Funktion auf
print("** ret=%s" % ret) #Nachbearbeitung
return ret
Auf diese Weise können Sie mit der Wrapper-Funktion Argumente erstellen, Rückgabewerte erstellen und die Verarbeitung vorher und nachher hinzufügen. Mit anderen Worten, eine Wrapper-Funktion ist eine Funktion, die der ursprünglichen Funktion hinzugefügt wurde.
Schreiben wir nun eine Funktion höherer Ordnung, die diese Wrapper-Funktionen generiert. Die Punkte sind wie folgt.
def normalize(func): #Erhalten Sie die Funktion
def wrapper(name):
if name == "Michiru":
name = "Mikuru" #Nach dem Basteln der Argumente
return func(name) #Rufen Sie die ursprüngliche Funktion auf
return wrapper #Funktionen wie(Schließung)Gib es zurück.
def censor(func, targets): #Erhalten Sie die Funktion
def wrapper(name):
ret = func(name) #Nach dem Aufruf der ursprünglichen Funktion
if name in targets:
ret["size"] = "Verbote" #Stellen Sie den Rückgabewert her
return ret
return wrapper #Funktionen wie(Schließung)Gib es zurück.
def trace(func): #Erhalten Sie die Funktion
def wrapper(name):
print("** name=%s" % name) #Vorverarbeitung hinzufügen
ret = func(name) #Rufen Sie die ursprüngliche Funktion auf
print("** ret=%s" % ret) #Nachbearbeitung hinzufügen
return ret
return wrapper #Funktionen wie(Schließung)Gib es zurück.
Die Verwendung dieser ist wie folgt.
table = [
{"name": "Haruhi", "size": "C"},
{"name": "Mikuru", "size": "E"},
{"name": "Yuki", "size": "A"},
]
def search(name): #Funktion zum Abrufen von Daten
for d in table:
if d["name"] == name:
return d
return None
##Wrapper-Funktion zum Erstellen von Argumenten
search1 = normalize(search)
##Wrapper-Funktion zum Erstellen des Rückgabewerts
search2 = censor(search, ["Mikuru"])
##Wrapper-Funktion mit Vor- und Nachbearbeitung hinzugefügt
search3 = trace(search)
Funktionen höherer Ordnung machen es sehr einfach, Wrapper-Funktionen mit zusätzlichen Funktionen zu generieren.
Nicht nur das. Sie können diese Funktionen problemlos frei kombinieren. Zum Beispiel:
##Wrapper-Funktion, die sowohl Argumente als auch Rückgabewerte erstellt
search12 = censor(normalize(search), ["Mikuru"])
##Wrapper-Funktion mit speziell gestalteten Argumenten und Rückgabewerten sowie zusätzliche Vor- und Nachbearbeitung
search123 = trace(censor(normalize(search), ["Mikuru"]))
Oder:
##Wrapper-Funktion mit speziell gestalteten Argumenten und Rückgabewerten sowie zusätzliche Vor- und Nachbearbeitung
search = normalize(search)
search = censor(search, ["Mikuru"])
search = trace(search)
Warum ist es möglich, frei zu kombinieren? Dies liegt daran, dass die Funktionen höherer Ordnung die ursprüngliche Funktion als Argument verwenden.
###Im ersten Beispiel wurde beispielsweise die ursprüngliche Funktion eingebettet.
###Daher ist die Funktion fest und kann nicht in eine andere Funktion geändert werden.
def search3(name):
print("** name=%s" % name)
ret = search(name) #Funktion ist behoben
print("** ret=%s" % ret)
return ret
###Andererseits ist in die Funktion höherer Ordnung nicht die ursprüngliche Funktion eingebettet.
###Da es als Argument empfangen wird, kann es leicht in eine andere Funktion geändert werden.
def trace(func): #Erhalten Sie die ursprüngliche Funktion als Argument
def wrapper(name):
print("** name=%s" % name)
ret = func(name) #Die Funktion ist nicht festgelegt!
print("** ret=%s" % ret)
return ret
return wrapper
In der funktionalen Programmierung gibt es übrigens das Wort "Teilanwendung". Wenn Sie denken, dass dies "eine der Funktionen ist, die eine Wrapper-Funktion generieren", ist das in Ordnung.
def add(x, y): #Beispiel: Angenommen, Sie haben eine Funktion, die zwei Zahlen hinzufügt
return x + y
def add1(x): #Wenn Sie ein Argument auf 1 setzen, ist dies
return add(x, 1) #Es wird eine "Funktion, die einer bestimmten Zahl 1 hinzufügt".
def adder(y): #Wenn Sie eine beliebige Zahl angeben möchten, anstatt sie auf 1 festzulegen.
def wrapper(x): #Eine solche Funktion höherer Ordnung kann verwendet werden.
return add(x, y)
return wrapper
add3 = adder(3) #Anwendungsbeispiel(add(x, y)Davon ist y auf 3 festgelegt)
print(add3(7)) #=> 10
def apply(func, y): #Machen wir es möglich, die Funktion selbst anzugeben.
def wrapper(x):
return func(x, y)
return wrapper
add3 = apply(add, 3) #Anwendungsbeispiel(add(x, y)Davon ist y auf 3 festgelegt)
print(add3(7)) #=> 10
Auf diese Weise wird das Erstellen einer neuen Funktion aus einer bestimmten Funktion mit einem Teil des angegebenen Arguments als "Teilanwendung" bezeichnet.
Eine Teilanwendung wird in Python jedoch nicht häufig verwendet. Da das Curry erfrischend ist, wird die Erklärung weggelassen.
Python verfügt über eine Funktion namens "Funktionsdekoratoren", mit der Sie Wrapper-Funktionen besser nutzen können. In Python werden "Funktionen höherer Ordnung, die Wrapper-Funktionen generieren" selten direkt aufgerufen und normalerweise über diesen "Funktionsdekorator" verwendet. Dieser "Funktionsdekorateur" ist eine unverzichtbare und wichtige Funktion für die Verwendung von Python.
Der Beispielcode der "Funktion höherer Ordnung, die die Wrapper-Funktion generiert" war übrigens so.
def search(name):
....
search = trace(search)
Dies ist der richtige Code, aber der Funktionsname "Suche" wurde dreimal angezeigt. Daher müssen Sie mindestens drei Stellen ändern, wenn Sie den Funktionsnamen ändern. Es ist ein sogenannter "nicht trocken" -Zustand.
In einem solchen Fall können Sie in Python schreiben: Dies wird als ** Funktionsdekorateur ** bezeichnet. (Es sieht aus wie eine Java-Annotation, ist aber völlig anders.)
@trace #← Dies ist ein Funktionsdekorateur
def search(name):
....
Mit dem Funktionsdekorator müssen Sie den Funktionsnamen nur an einer Stelle schreiben (es ist sehr "TROCKEN"). Es mag zunächst seltsam erscheinen, aber "@trace def search (): ..." ist dasselbe wie "def search (): ...; search = trace (search)", also Denk nicht nach.
Auf Wunsch können Sie auch mehrere Funktionsdekoratoren angeben.
##das ist,
@deco1
@deco2
@deco3
def func():
....
##Gleich wie das
def func():
....
func = deco1(deco2(deco3(func)))
Funktionsdekoratoren werden nicht in anderen Sprachen als Python gefunden. Daher mag es seltsam aussehen, aber es sollte nicht schwierig sein, wenn der Inhalt soweit ist.
Funktionsdekorateure sind schwierig, wenn sie Argumente annehmen. Ich werde es jetzt erklären, aber wenn Sie es nicht verstehen (wenn Sie Python nicht ernsthaft studieren möchten), machen Sie sich keine Sorgen und überspringen Sie es.
Betrachten Sie zunächst den Fall, in dem "eine Funktion höherer Ordnung, die die ursprüngliche Funktion empfängt und eine neue Funktion zurückgibt" andere Argumente als die ursprüngliche Funktion * * akzeptiert.
##Diese Funktion höherer Ordnung hat andere Argumente als func
def censor(func, targets):
def wrapper(name):
ret = func(name)
if name in targets:
ret["size"] = "Es ist eine verbotene Angelegenheit"
return ret
return wrapper
Der Versuch, dies als Funktionsdekorateur zu verwenden, führt zu einem Fehler. Wissen Sie, warum Sie einen Fehler erhalten?
##Dies führt zu einem Fehler
@censor
def search(name):
...
##Weil Zensur()Weil es kein zweites Argument gibt, Ziele von
search = censor(search)
#=> TypeError: censor() missing 1 required positional argument: 'targets'
Der obige Code ist ein Funktionsdekorator, daher wird Python versuchen, "search = censor (search)" auszuführen. Das zweite Argument ("Ziele") von "Zensor ()" ist jedoch nicht angegeben, sodass ein Fehler auftritt.
Um dies zu vermeiden, erstellen Sie eine "Funktion, die ein Argument akzeptiert und einen 'Funktionsdekorateur' zurückgibt". Ein Funktionsdekorator ist eine "Funktion, die eine Funktion empfängt und eine neue Funktion zurückgibt", dh sie erstellt eine "Funktion, die ein Argument empfängt und eine" Funktion zurückgibt, die eine Funktion empfängt und eine neue Funktion zurückgibt "" (!!).
def censor(targets): #Eine Funktion, die ein Argument akzeptiert und einen Funktionsdekorator zurückgibt
def deco(func): #Machen Sie einen Funktionsdekorateur
def wrapper(name): #Erstellen Sie eine Wrapper-Funktion
ret = func(name) #Rufen Sie die ursprüngliche Funktion auf
if name in targets: #Bezieht sich auf das äußerste Argument
ret["size"] = "Es ist eine verbotene Angelegenheit"
return ret #Gibt den Rückgabewert zurück
return wrapper #Gibt eine Wrapper-Funktion zurück
return deco #Gibt einen Funktionsdekorator zurück
Wow, es ist kompliziert ...
Hier erfahren Sie, wie Sie es verwenden.
##das ist
@censor(["Mikuru"])
def search(name):
....
##Gleich wie das
def search(name):
....
deco = censor(["Mikuru"])
search = deco(search)
##Beachten Sie, dass dies anders ist!!
search = censor(search, ["Mikuru"])
Das ist alles für den Funktionsdekorateur mit Argumenten, aber haben Sie verstanden?
Wenn Sie nicht verstanden haben: Sie müssen sich keine Sorgen machen. Eines Tages werden wir es wissen. Wenn Sie dies nicht tun, werden Sie feststellen, dass "selbst wenn Sie die kleinen Dinge nicht verstehen, es keinen großen Einfluss auf Ihr Leben hat".
Diejenigen, die verstehen: Ich bin sicher, Sie denken nur, Sie verstehen. Wenn solch ein "Ich verstehe" extrem ist, schreibe ich Gedichte, die genial und schön sind. Lass uns aufpassen.
Pythons Funktionsdekoratoren sind sicherlich nützlich, wenn Sie sie beherrschen können. Die Art und Weise, wie es erstellt wird, hängt jedoch davon ab, ob ein Argument erforderlich ist oder nicht. Die Definition eines Funktionsdekorators, der ein Argument verwendet, ist sehr mühsam und schwer zu vermitteln. Um klar zu sein, ** Python-Funktionsdekoratoren sind ein Designfehler ** wie dieser.
Nur indem der Funktionsdekorateur Nichtfunktionsargumente akzeptieren kann, können diese Probleme sofort gelöst werden. Warum machst du das nicht ...
##Zum Beispiel, wenn Sie so schreiben könnten
@censor ["Mikuru"], "Es ist eine verbotene Angelegenheit"
def search(name):
....
##Wenn dies ↑ das gleiche ist wie dieses ↓
def search(arg):
....
search = censor(search, ["Mikuru"], "Es ist eine verbotene Angelegenheit")
##Es wird auf die gleiche Weise geschrieben wie ein Funktionsdekorateur, der ein Argument akzeptiert, und ein Dekorator, der dies nicht tut.
##Selbst wenn Sie es ändern, um einen Funktionsdekorator zu verwenden, der kein Argument akzeptiert, ist die Kompatibilität der Benutzerseite
##Ich wünschte, jeder könnte glücklich sein, weil sie behalten wurden.
def censor(func, targets, seal):
def wrapper(name):
ret = func(arg)
if name in targets:
ret["size"] = seal
return ret
return wrapper
</ End>
Ich habe die Funktion des Funktionsdekorators außerhalb von Python nicht gesehen (es mag sein, aber es ist nicht beliebt). Infolgedessen können Benutzer in anderen Sprachen sagen: "Dies ist nur ein Syntaxzucker für Funktionen höherer Ordnung, ist es nicht notwendig?"
Die Funktion Function Decorator ist jedoch eine sehr schöne Funktion, die nicht ** sein muss. Das liegt daran, dass die Funktion des Funktionsdekorators Code sehr gut lesbar macht.
Betrachten Sie beispielsweise den folgenden JavaScript-Code:
var compile = taskdef("*.html", ["*.txt"], function() {
var html = this.product;
var txt = this.materials[0];
system.run("txt2html " + html + " > " + txt);
});
Wenn JavaScript eine Funktion zum Dekorieren von Funktionen hätte, könnte dies geschrieben werden: Denken Sie nicht, dass dieser viel einfacher zu lesen ist?
@taskdef("*.html", ["*.txt"])
function compile() {
var html = this.product;
var txt = this.materials[0];
system.run("txt2html " + html + " > " + txt);
}
Betrachten Sie auch einen anderen Code wie diesen: function () {...}
ist mehrfach verschachtelt.
function test_it_should_exit_with_0_when_same_contents() {
with_dummyfile("A.txt", "aaa", function() {
with_dummyfile("B.txt", "aaa", function() {
status = system.run("compare A.txt B.txt");
asssert_equal(status, 0);
});
});
};
Wenn Sie einen Funktionsdekorator in JavaScript verwenden könnten, könnten Sie wahrscheinlich schreiben: Es ist sehr erfrischend, da es keine Mehrfachverschachtelung gibt.
@with_dummyfile("B.txt", "aaa")
@with_dummyfile("A.txt", "aaa")
function test_it_should_exit_with_0_when_same_contents() {
status = system.run("compare A.txt B.txt");
asssert_equal(status, 0);
};
Da der Funktionsdekorator nur ein Syntaxzucker ist, ist er leicht zu implementieren, aber der erzielte Effekt ist ziemlich groß. Es ist eine kostengünstige Funktion für Sprachen, daher hoffe ich, dass sie auch in anderen Sprachen eingeführt wird.
Eines der Designmuster im Klassendesign ist das "Dekorationsmuster". Ich denke, der Begriff "Funktionsdekorateur" in Python leitet sich wahrscheinlich von diesem Musternamen ab. Für den Zweck des Dekorators * Muster * verwenden wir häufig die Funktion Dekorator * Funktion * als Mittel.
Aber es gibt einen Unterschied zwischen den beiden.
** "Dekorationsmuster" ist keine Sprachfunktion, sondern nur eines der Muster **. Das "Dekorationsmuster" ist so konzipiert, dass die neue Funktion oder Klasse auf die gleiche Weise wie die ursprüngliche Funktion oder Klasse verwendet werden kann (= ** hat dieselbe Schnittstelle **). Dadurch wird die ursprüngliche Funktion oder Klasse durch eine neue ersetzt.
Pythons ** "Function Decorator" -Funktion ist eine der besten Sprachfunktionen (wenn auch nur Syntaxzucker) **. Außerdem müssen im "Funktionsdekorator" die ursprüngliche Funktion und die neue Funktion nicht auf die gleiche Weise verwendet werden (= ** Schnittstellen können unterschiedlich sein **). Es ist üblich, der ursprünglichen Funktion Argumente hinzuzufügen oder diese zu subtrahieren und den Datentyp des Rückgabewerts zu ändern.
Dies zeigt, dass sich die Funktionsdekoratoren von Python von den Dekorationsmustern unterscheiden. Wenn dies der Fall ist, sollte es sich um einen genaueren Namen für die Funktion handeln.
@deco def fn (): ...
ist dasselbe wie def fn (): ...; fn = deco (fn)
@deco (arg) def fn (): ...
ist dasselbe wie def fn (): ...; fn = deco (arg) (fn)
@ deco1 @ deco2 def fn (): ...
ist dasselbe wie def fn (): ...; fn = deco1 (deco2 (fn))
Beim Zugriff auf die lokalen Variablen der äußeren Funktion von der inneren Funktion in Python aus kann das Lesen der Variablen ohne Vorbereitung erfolgen, das Schreiben der Variablen unterliegt jedoch einer Einschränkung.
##Für Python3
def outer():
x = 10
y = 20
def inner():
x = 15 #Das ist innerlich()Wird der lokalen Variablen von zugewiesen.
nonlocal y #Aber durch die Verwendung von nicht lokal,
y = 25 # outer()Kann einer lokalen Variablen von zugewiesen werden.
inner()
print(x) #=> 10 (Nicht geändert)
print(y) #=> 25 (wurde geändert)
outer()
##Für Python2
def outer():
x = 10
y = [20] #Python2 ist nicht lokal, daher benötigen Sie einen solchen Trick
def inner():
x = 15 #Das ist innerlich()Wird der lokalen Variablen von zugewiesen.
y[0] = 25 #Beachten Sie, dass dies die Variable y nicht geändert hat!
inner()
print(x) #=> 10 (Nicht geändert)
print(y[0]) #=> 25 (y hat sich nicht geändert, aber y[0]Wurde geändert)
outer()
Es ist ein bisschen anders als in anderen Sprachen. Es gibt viele Gründe, warum Python eine solche Spezifikation hat.
x = 15
" eine lokale Variable von inner (), wenn sie in der Funktions- / Methodendefinition enthalten ist. " ) Wird als lokale Variable behandelt. "Zusätzlich zu Funktionsdekoratoren verfügt Python über eine Funktion namens "Klassendekoratoren".
@publish #← Dies ist ein Klassendekorateur
class Hello(object):
...
##Dies ist das gleiche wie
class Hello(object):
...
Hello = publish(Hello)
In Python bezieht sich der Begriff "Dekorator" normalerweise auf einen Funktionsdekorator. Es gibt jedoch zwei Arten von Dekoratoren, und es kann mit einem der Entwurfsmuster (wie oben erwähnt), dem "Dekorationsmuster", verwechselt werden. Es ist daher besser, es so weit wie möglich als "Funktionsdekorateur" zu bezeichnen, um Mehrdeutigkeiten zu vermeiden. ..
Der bisherige Beispielcode für Funktionsdekorateure hat angenommen, dass die ursprüngliche Funktion nur ein Argument hat.
def trace(func):
def newfunc(name):
print("** name=%s" % name)
ret = func(name) #← Es wird nur ein Argument angenommen
print("** ret=%s" % ret)
return ret
return newfunc
Daher kann dieser Funktionsdekorator nur auf Funktionen mit nur einem Argument angewendet werden.
@trace
def search(name): #← Anwendbar, da es nur ein Argument gibt
for d in tables:
if d["name"] == name: return d
return None
@trace
def add(x, y): #← Da es zwei Argumente gibt, tritt ein Laufzeitfehler auf.
return x+y
add(10, 20)
#=> TypeError: newfunc() takes 1 positional argument but 2 were given
Um dies auf eine beliebige Anzahl von Argumenten anzuwenden:
def trace(func):
def newfunc(*args): #← Erhalten Sie eine beliebige Anzahl von Argumenten
print("** args=%r" % (name,))
ret = func(*args) #← Übergeben Sie eine beliebige Anzahl von Argumenten
print("** ret=%r" % (ret,))
return ret
return newfunc
@trace
def add(x, y): #← Auch bei zwei Argumenten tritt kein Fehler auf
return x+y
add(x, y)
Gehen Sie wie folgt vor, um beliebige Schlüsselwortargumente aufzunehmen:
def trace(func):
def newfunc(*args, **kwargs): #← Empfangen Sie beliebige Keyword-Argumente
print("** args=%r, %kwargs=%r" % (name, kwargs))
ret = func(*args, **kwargs) #← Übergeben Sie beliebige Schlüsselwortargumente
print("** ret=%r" % (ret,))
return ret
return newfunc
@trace
def add(x, y):
return x+y
add(y=20, x=10) #← Das Aufrufen mit Schlüsselwortargumenten ist in Ordnung
Wenn Sie eine Funktion in Python definieren, wird das Funktionsobjekt mit dem Funktionsnamen und der Dokumentation gefüllt.
def search(name):
"""Search table by name."""
for d in table:
if d["name"] == name:
return d
return None
##Funktionsname und Dokumentation anzeigen
print(search.__name__) #=> search
print(search.__doc__) #=> Search table by name.
Bei Funktionen höherer Ordnung und Funktionsdekoratoren verschwinden sie jedoch oder haben unterschiedliche Namen.
def trace(func):
def newfunc(name):
print("** name=%s" % name)
ret = func(name)
print("** ret=%s" % ret)
return ret
return newfunc
@trace
def search(name):
....
print(search.__name__) #=> newfunc (Der Funktionsname hat sich geändert!)
print(search.__doc__) #=> None (Das Dokument ist verschwunden!)
Das ist nicht sehr gut. Daher ist es eine gute Idee, den Funktionsnamen und die Dokumentation von der ursprünglichen Funktion in die neue Funktion zu kopieren.
def trace(func):
def newfunc(name):
....
#Kopieren Sie den Funktionsnamen und die Dokumentation von der ursprünglichen Funktion
newfunc.__name__ = func.__name__
newfunc.__doc__ = func.__doc__
return newfunc
@trace
def search(name):
....
print(search.__name__) #=> search
print(search.__doc__) #=> Search table by name.
Ein Dienstprogramm, mit dem diese Kopie erstellt wird, wird standardmäßig in Python bereitgestellt. Daher empfiehlt es sich, es zu verwenden.
import functools
def trace(func):
@functools.wraps(func) #← Kopien der ursprünglichen Funktion
def newfunc(name):
....
return newfunc
@trace
def search(name):
....
print(search.__name__) #=> search
print(search.__doc__) #=> Search table by name.
In diesem Gedicht haben wir "Funktionen, die Funktionen erzeugen" unter Funktionen höherer Ordnung erklärt. Sie können damit Funktionen generieren, die sich etwas anders verhalten. Das Wichtigste dabei ist der "Näher", eine Funktion, an die Variablen angehängt sind.
Er erklärte auch, dass eine "Wrapper-Funktion", die einer vorhandenen Funktion Funktionalität hinzufügt, nützlich ist. Funktionen höherer Ordnung sind auch beim Generieren von Wrapper-Funktionen nützlich, und wir haben auch eine Funktion namens "Funktionsdekorator" eingeführt, um sie leichter lesbar zu machen.
Ich denke, dass IQ145 es leichter erklären kann als ein schöner Senior, aber wenn Sie etwas bemerken, kommentieren Sie bitte.
Ich werde ein Beispiel für die Antwort im Kommentarbereich schreiben.
【Frage 1】 Stellen Sie sich eine Funktion vor, die bei jedem Aufruf zwischen zwei Werten wechselt.
## 'red'Wann'blue'Eine Funktion, die abwechselnd zurückgibt
print(toggle_color()) #=> 'red'
print(toggle_color()) #=> 'blue'
print(toggle_color()) #=> 'red'
print(toggle_color()) #=> 'blue'
##Eine Funktion, die abwechselnd 1 und 0 zurückgibt
print(toggle_on_off()) #=> 1
print(toggle_on_off()) #=> 0
print(toggle_on_off()) #=> 1
print(toggle_on_off()) #=> 0
## 'show'Wann'hide'Eine Funktion, die abwechselnd zurückgibt
print(toggle_visible()) #=> 'show'
print(toggle_visible()) #=> 'hide'
print(toggle_visible()) #=> 'show'
print(toggle_visible()) #=> 'hide'
Definieren Sie eine Funktion höherer Ordnung "new_toggle ()", die eine solche Funktion erzeugt. Wie benutzt man:
toggle_color = new_toggle("red", "blue")
toggle_on_off = new_toggle(1, 0)
toggle_visible = new_toggle("show", "hide")
** [Problem 2] ** Definieren wir eine Funktion, die ein Array von Zeichenfolgen verwendet und diese mithilfe der Funktion map () höherer Ordnung (für map (), previous in Groß- und Kleinbuchstaben konvertiert. kwatch / items / 03fd035a955235681577)) ::
def uppers(strings):
return map(lambda s: s.upper(), strings)
def lowers(strings):
return map(lambda s: s.lower(), strings)
print(uppers(['a', 'b', 'c'])) #=> ['A', 'B', 'C']
print(lowers(['A', 'B', 'C'])) #=> ['a', 'b', 'c']
Definieren wir außerdem eine Funktion, um die Länge aller Zeichenfolgen mithilfe von map () auf dieselbe Weise zu ermitteln:
def lengths(strings):
return map(lambda s: len(s), strings)
##Oder Karte zurückgeben(len(s), strings)
print(lengths(["Haruhi", "Mikuru", "Yuki"])) #=> [6, 6, 4]
Alle diese haben den gleichen Code, mit Ausnahme des Lambda-Ausdrucks.
Versuchen Sie also, einen Funktionsmapper höherer Ordnung () zu definieren, der diese Funktionen einfach generiert. Wie benutzt man:
uppers = mapper(lambda s: s.upper())
lowers = mapper(lambda s: s.lower())
lengths = mapper(lambda s: len(s)) #Oder Mapper(len)
print(uppers(['a', 'b', 'c'])) #=> ['A', 'B', 'C']
print(lowers(['A', 'B', 'C'])) #=> ['a', 'b', 'c']
print(lengths(["Haruhi", "Mikuru", "Yuki"])) #=> [6, 6, 4]
** [Problem 3] ** Angenommen, Sie haben eine Funktion, die eine HTTP-Anforderung sendet, z.
##Funktion zum Senden einer HTTP-Anfrage
def http(method, url, params={}, headers={}):
....
##Funktion zum Senden einer HTTPS-Anfrage
def https(method, url, params={}, headers={}):
....
##Wie benutzt man
response = http("GET", "http://www.google.com/", {"q": "python"})
Ich habe hierfür für jede der Methoden GET / POST / PUT / DELETE / HEAD eine Wrapper-Funktion erstellt.
def GET(url, params={}, headers={}):
return http("GET", url, params, headers)
def POST(url, params={}, headers={}):
return http("POST", url, params, headers)
def PUT(url, params={}, headers={}):
return http("PUT", url, params, headers)
def DELETE(url, params={}, headers={}):
return http("DELETE", url, params, headers)
def HEAD(url, params={}, headers={}):
return http("HEAD", url, params, headers)
Dies ist jedoch eine ähnliche Code-Iteration. Gibt es eine Möglichkeit, es präziser zu schreiben?
** [Problem 4] ** Der folgende Code ist in Python geschriebener Testcode. Sie können sehen, dass die Dummy-Datei im Test generiert / gelöscht wird.
import os, unittest
class FileTest(unittest.TestCase):
def test_read(self):
##Erstellen Sie eine Dummy-Datei
filename = "file1.txt"
File.open(filename, "w") as f:
f.write("FOO\n")
##Führen Sie den Test aus
try:
with open(filename) as f:
content = f.read()
self.assertEqual(content, "FOO\n")
##Dummy-Datei löschen
finally:
os.unlink(filename)
Um dies präziser zu schreiben, definieren Sie eine Dekorationsfunktion mit dem Namen "@with_dummyfile ()":
import os, unittest
class FileTest(unittest.TestCase):
@with_dummyfile("file1.txt", "FOO\n") #← Definiere dies
def test_read(self):
with open("file1.txt") as f:
content = f.read()
self.assertEqual(content, "FOO\n")
** [Problem 5] ** Ein bestimmtes Webanwendungsframework wurde wie folgt verwendet. Ich benutze einen Funktionsdekorator namens "@view_config ()", aber es scheint, dass das Lesen und Schreiben lange gedauert hat.
##Es ist zu lang zum Lesen und Schreiben
@view_config(request_method="GET",
route_urlpath="/api/books/{book_id}/comments/{comment_id}")
def show_book(self):
book_id = self.request.matched['book_id']
comment_id = self.request.matched['comment_id']
....
Definieren Sie daher einen Funktionsdekorator namens "@ on ()", der präziser geschrieben werden kann. Wie benutzt man:
@on("GET", "/api/books/{book_id}/comments/{comment_id}")
def show_book(self):
book_id = self.request.matched['book_id']
comment_id = self.request.matched['comment_id']
....
Definieren Sie außerdem einen Dekorator "@ api", der die Parameter im URL-Pfadmuster als Funktionsargumente akzeptiert. Wie benutzt man:
@on("GET", "/api/books/{book_id}/comments/{comment_id}")
@api
def show_book(self, book_id, comment_id):
#Argumentationsbuch_ID ist Selbst.request.matched['book_id']Ist bestanden.
#Argument loben_ID ist Selbst.request.matched['comment_id']Ist bestanden.
....
Wenn Sie können, integrieren Sie die Funktionalität von @ api
in @ on ()
. Wie benutzt man:
@on("GET", "/api/books/{book_id}/comments/{comment_id}")
def show_book(self, book_id, comment_id):
#Argumentationsbuch_ID ist Selbst.request.matched['book_id']Ist bestanden.
#Argument loben_ID ist Selbst.request.matched['comment_id']Ist bestanden.
....
Trinkgeld:
##das ist
keyword_args = {"book_id": "123", "comment_id": "98765"}
show_book(self, **keyword_args)
##Gleich wie das
show_book(self, book_id="123", comment_id="98765")
Recommended Posts