Wann werden die Standardargumente in Python gebunden? Wenn die Variable im Abschluss gebunden ist, wird die Auswertung verschoben.

Hinweis

Python-Begriffe und Formulierungen sind ziemlich verdächtig.

TLTR

Motivation

Ich habe einen Artikel über die Bewertung der Python-Verzögerung gelesen und zusammengefasst.

Beispiel 1 Wann wird das Standardargument einer Funktion ausgewertet?

Python kann Standardargumente festlegen. ** Die Standardargumente der Funktion werden jedoch bei der Definition der Funktion ausgewertet. Nicht zur Laufzeit. Es ist eine unerwartete Gefahr, wenn Sie die Spezifikation von ** nicht kennen.

Manchmal ist das Standardargument = Keine gut

Definieren Sie eine append_to-Funktion, die nur die übergebenen Argumente zur Liste hinzufügt. Es hat standardmäßig eine leere Liste.

Anhängen, um der Liste ein Argument hinzuzufügen_Funktionieren


>>> def append_to(element, to=[]):
...     to.append(element)
...     return to
...

Fügen wir 12,42 hinzu. Weil es ein Argument "to = []" gibt [12] [42] Es scheint so als.

>>> my_list = append_to(12)
>>> print(my_list)
[12]
>>> my_other_list = append_to(42)
>>> print(my_other_list)
[12, 42]

Es ist nicht das, was du erwartet hast. Was bedeutet das.

Ursache

Das Standardargument to ist definiert als to = []. Wir werden den Wert hier nacheinander aktualisieren.

Wenn "to = []" im Prozess des Werdens zu "to = []" -> "to = [12]" -> "to = [12,42]" ausgewertet wird,

to = [] -> to = [12] -> to = [] -> to = [42], was das gewünschte Ergebnis zu liefern scheint, aber ** das Standardargument ist eine Funktion Es wird zum Zeitpunkt der Definition bewertet. ** **.

Mit anderen Worten, to ist "to = []", wenn eine Funktion definiert wird, aber ** tut nicht "to = []" jedes Mal, wenn sie aufgerufen wird **, also wenn "appent_to (42)", "to = [12] Es bedeutet, dass es bleibt.

Lösung

Was ist stattdessen zu tun? Es scheint, dass "to = None" gemacht werden sollte.

Es fühlt sich ein wenig überflüssig an. Ich weiß nicht was ich tun soll.

def append_to(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to


my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)

[12]
[42]

Ich habe das gewünschte Ergebnis erzielt.

Beispiel 2 Bewertung der Schließverzögerung

Python kann faul auswerten. Grob gesagt bedeutet verzögerte Bewertung, bei Bedarf zu bewerten.

Wenn Sie ein Anfänger wie ich sind, der normalerweise Python lernt, lautet dies "Ich weiß! Dies ist der, den ich im ** Seminar gesehen habe".

Wenn Sie von nun an mittelgroßen Code schreiben möchten, müssen Sie zwangsläufig die Standardargumente der Funktion entwerfen.

Es gibt jedoch eine solche Python-Spezifikation.

** Variable Bindung beim Verschluss wird verzögert ausgewertet **

Schauen wir uns ein Codebeispiel an, das einen sogenannten Abschluss verwendet, bei dem Werte in einer Funktion gespeichert werden.

Das Ziel ist Code, der eine Liste mit dem Inhalt der Liste multipliziert mit dem Wert des Arguments zurückgibt.

Ein Skript, das eine Liste mit dem Inhalt der Liste multipliziert mit dem Wert des Arguments zurückgibt


functions = []
for n in [1, 2, 3]:
    def func(x):
        return n * x
    functions.append(func)

# You would expect this to print [2, 4, 6]
print('{}'.format(str([function(2) for function in functions])))

Es scheint, dass "[2, 4, 6]([1 * 2, 2 * 2, 3 * 2])" ausgegeben wird, aber ...

python


[6, 6, 6]

Wurde ausgegeben.

Ursache

Python-Verschlüsse werden aufgeschoben ausgewertet. Die Werte der im Abschluss verwendeten Variablen werden zum Zeitpunkt ihres Aufrufs ebenfalls referenziert.

Daher ist n nicht zu dem Zeitpunkt festgelegt, zu dem der Abschluss in der for-Anweisung aufgerufen wird, und es wird in "Funktionen" im Zustand "n * x" gespeichert. Sozusagen "[n * x, n * x, n * x]" Wenn es von der print-Anweisung ausgeführt wird, wird daher auf den letzten Zustand von n, n == 3, verwiesen, also[6,6,6]([3 * 2,3 * 2,3 * 2) ], X = 2).

Versuchen Sie zu debuggen

Verwenden Sie den Debugger, um die Reihenfolge der Aktualisierung der Werte der for- und print-Anweisungen anzuzeigen.

Da der Wert der Variablen n durch die Iteration auf bereits 3 aktualisiert wurde, wird die Funktion durch Druck die ganze Zeit n == 3 ausgewertet. Sie können aus dem Druck ersehen, dass n == 3 dreimal referenziert wird.

2020-05-192.gif

Es ist schwer zu verstehen, also werde ich einen anderen posten.

2020-05-17.gif

Lösung

Ändern Sie das Abschlussargument von "x" in "x, n = n". Das Problem wird gelöst, indem der Wert von n übergeben wird, dh der Wert von n an die Funktionen übergeben wird, die den Abschluss jedes Mal speichern, wenn er mit einer for-Anweisung wiederholt wird.

Fügen Sie dazu den Wert von n in das Argument ein, z. B. "def func (x, n = n):", und lassen Sie es die Form einer Neuzuweisung annehmen. Dies ermöglicht es dem Abschluss, den Wert von n aus der Spezifikation des Haltens des Werts innerhalb dieser Funktion zu aktualisieren.

Ein Skript, das eine Liste mit dem Inhalt der Liste multipliziert mit dem Wert des Arguments zurückgibt



functions = []
for n in [1, 2, 3]:
    def func(x, n=n):# n=Hinzugefügt n.
    # def func(x):
        return n * x
    functions.append(func)


print('{}'.format(str([function(2) for function in functions])))


Ausgabeergebnis


[2,4,6]

Werfen wir einen Blick auf den Debugger.

2020-05-193.gif

Ich frage mich, ob das Verhalten von func mit dem Debugger gut ausgedrückt werden kann, aber ich konnte dafür sorgen, dass es sich wie erwartet verhält.

Andere Lösungen

Es scheint, dass Sie die Bibliothek verwenden sollten. Diesmal wird es weggelassen.

from functools import partial
from operator import mul

Zusammenfassung

Python führt eine faule Auswertung durch. Dies ist etwas verwirrend, wenn Sie regelmäßig prozedurale Programme programmieren. Es ist eine Funktion, die aus einer funktionalen Sprache stammt, also etwas eigenartig.

Ich bin nicht sicher, wie ich die Variablenbindung (Zuweisung in einer funktionalen Sprache) in Python richtig verwenden soll. </ del>

Nachtrag 2020/05/19

Es gab einen Punkt im Kommentar.

Ich habe in Beispiel 1 geschrieben, dass Pythons Standardargumente aufgeschoben ausgewertet werden. Dies war jedoch ein falsches Verständnis. Das Standardargument von Python ist eine Spezifikation, die gebunden wird, wenn eine Funktion deklariert wird, anstatt verzögert ausgewertet und gebunden zu werden. Es scheint korrekter zu sein, sich nicht intuitiv zu verhalten, wie in Beispiel 1.

Verweise

https://ja.coder.work/so/python/1087272 https://python-guideja.readthedocs.io/ja/latest/writing/gotchas.html https://stackoverflow.com/questions/36463498/late-binding-python-closures

Recommended Posts

Wann werden die Standardargumente in Python gebunden? Wenn die Variable im Abschluss gebunden ist, wird die Auswertung verschoben.
Seien Sie vorsichtig, wenn Sie den Standardargumentwert in der Python 3-Serie angeben
Wann werden die Standardargumente in Python gebunden? Wenn die Variable im Abschluss gebunden ist, wird die Auswertung verschoben.
Python: Klassen- und Instanzvariablen
Python-Klassen- und Instanzvariablen
In Python-Klassenvariablen versteckte Landminen
Wann wird die mmap (2) -Datei aktualisiert? (2)
Wann wird die mmap (2) -Datei aktualisiert? (1)
[Python] Seien Sie vorsichtig, wenn Sie Druck verwenden
[Python] Erbt eine Klasse mit Klassenvariablen
[Python] Verwenden Sie und und oder beim Erstellen von Variablen
Sagen Sie voraus, wann die ISS sichtbar sein wird
Den Typ mit Python beherrschen? (Wann ist eine Typprüfung durchzuführen?)
Dinge, auf die Sie achten müssen, wenn Sie Standardargumente in Python verwenden
Python: Verwenden Sie die im Zeichenfolgenformat definierten Variablen unverändert
Vorsichtsmaßnahmen beim Festlegen von Standardwerten für Argumente in Python-Funktionsdefinitionen
Sagen Sie voraus, wann die ISS sichtbar sein wird
Wenn Sie sudo in Ubuntu hinzufügen, wird es als Standardpython bezeichnet.
Sie werden in 100 Tagen Ingenieur - 29. Tag - Python - Grundlagen der Python-Sprache 5
Sie werden in 100 Tagen Ingenieur - Tag 33 - Python - Grundlagen der Python-Sprache 8
Sie werden in 100 Tagen Ingenieur - 26. Tag - Python - Grundlagen der Python-Sprache 3
Sie werden in 100 Tagen Ingenieur - Tag 32 - Python - Grundlagen der Python-Sprache 7
Sie werden in 100 Tagen Ingenieur - 28. Tag - Python - Grundlagen der Python-Sprache 4
Überprüfen Sie, ob die Zeichen in Python ähnlich sind
Wann wirken sich Python-Funktionen auf die Argumente des Aufrufers aus?
Verwenden Sie pydantic beim Lesen von Umgebungsvariablen in Python
Was zu tun ist, wenn es nicht in der Sudoers-Datei enthalten ist. Dieser Vorfall wird gemeldet.
Python Hinweis: Wenn der Befehl pip nicht verwendet werden kann
Grundeinstellungen bei Verwendung der foursquare-API mit Python
Ruft Umgebungsvariablen in Python ab, andernfalls legen Sie Standardwerte fest
Python-Standardargumente werden nicht bei jedem Aufruf initialisiert
Effektives Python-Hinweiselement 20 Verwenden Sie None und die Dokumentationszeichenfolge, wenn Sie dynamische Standardargumente angeben