Was ist das idiomatische Python-Äquivalent dieses C / C ++ - Codes?
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
Wie implementiert man das statische Element auf Funktionsebene im Gegensatz zur Klassenebene? Und ändert das Platzieren der Funktion in einer Klasse etwas?
_
Präfix zu verwenden.Antworten:
Ein bisschen umgekehrt, aber das sollte funktionieren:
Wenn der Zählerinitialisierungscode oben statt unten angezeigt werden soll, können Sie einen Dekorator erstellen:
Verwenden Sie dann den folgenden Code:
foo.
Leider müssen Sie weiterhin das Präfix verwenden.( Bildnachweis : @ony )
quelle
if "counter" not in foo.__dict__: foo.counter = 0
als erste Zeile von setzenfoo()
. Dies würde helfen, Code außerhalb der Funktion zu vermeiden. Ich bin mir nicht sicher, ob dies 2008 möglich war. PS Diese Antwort wurde gefunden, als nach der Möglichkeit gesucht wurde, statische Funktionsvariablen zu erstellen, sodass dieser Thread noch "lebendig" ist :)foo
undfoo.counter =
eng miteinander verbunden ist. Letztendlich bevorzuge ich jedoch den Dekorationsansatz, da der Dekorateur auf keinen Fall aufgerufen wird und es semantisch offensichtlicher ist, was er tut (@static_var("counter", 0)
ist einfacher und für meine Augen sinnvoller alsif "counter" not in foo.__dict__: foo.counter = 0
, insbesondere, wenn Sie letzteres verwenden müssen der Funktionsname (zweimal), der sich ändern könnte).def foo():
if not hasattr(foo,"counter"): foo.counter=0
foo.counter += 1
Sie können einer Funktion Attribute hinzufügen und diese als statische Variable verwenden.
Wenn Sie die Variable nicht außerhalb der Funktion einrichten möchten, können Sie alternativ
hasattr()
eineAttributeError
Ausnahme vermeiden :Auf jeden Fall sind statische Variablen eher selten, und Sie sollten einen besseren Platz für diese Variable finden, höchstwahrscheinlich innerhalb einer Klasse.
quelle
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1
sollte dasselbe tun und stattdessen Ausnahmen verwenden.try
zusätzliche Kosten hinzu? Nur neugierig.Man könnte auch überlegen:
Argumentation:
if
Verzweigung (denken Sie an eine StopIteration- Ausnahme).quelle
def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
hasattr()
hierfür ist nicht einfacher und auch weniger effizient.Andere Antworten haben gezeigt, wie Sie dies tun sollten. Hier ist ein Weg, den Sie nicht sollten:
Standardwerte werden nur bei der ersten Auswertung der Funktion initialisiert, nicht bei jeder Ausführung. Sie können also eine Liste oder ein anderes veränderbares Objekt zum Speichern statischer Werte verwenden.
quelle
def foo(arg1, arg2, _localstorage=DataClass(counter=0))
finde ich es gut lesbar. Ein weiterer guter Punkt ist das einfache Umbenennen von Funktionen.types.SimpleNamespace
,def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):
ohne eine spezielle Klasse definieren zu müssen.Viele Leute haben bereits vorgeschlagen, 'hasattr' zu testen, aber es gibt eine einfachere Antwort:
Kein Versuch / außer, kein Test hasattr, nur getattr mit einem Standard.
quelle
try
/except
basierten Ansatz von ravwojdyla ziemlich bedeutungslos. Ein einfachesipython
%%timeit
Mikrobenchmark ergab die Kosten fürtry
/except
bei 255 ns pro Anruf gegenüber 263 ns für diegetattr
basierte Lösung. Ja, dastry
/except
ist schneller, aber es ist nicht gerade "zweifellos gewinnen"; Es ist eine winzige Mikrooptimierung. Schreiben Sie den Code, der klarer erscheint. Machen Sie sich keine Sorgen über geringfügige Leistungsunterschiede wie diesen.Hier ist eine vollständig gekapselte Version, für die kein externer Initialisierungsaufruf erforderlich ist:
In Python sind Funktionen Objekte, und wir können ihnen einfach Mitgliedsvariablen über das spezielle Attribut hinzufügen oder Affen-Patches hinzufügen
__dict__
. Das integriertevars()
Element gibt das spezielle Attribut zurück__dict__
.BEARBEITEN: Beachten Sie, dass
try:except AttributeError
bei diesem Ansatz die Variable im Gegensatz zur alternativen Antwort nach der Initialisierung immer für die Codelogik bereit ist. Ich denke, dietry:except AttributeError
Alternative zu den folgenden ist weniger trocken und / oder hat einen unangenehmen Fluss:EDIT2: Ich empfehle den obigen Ansatz nur, wenn die Funktion von mehreren Stellen aus aufgerufen wird. Wenn die Funktion stattdessen nur an einer Stelle aufgerufen wird, ist es besser, Folgendes zu verwenden
nonlocal
:quelle
try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
X not in Y
anstelle vonnot X in Y
(oder empfehlen Sie die Verwendung, wenn Sie es nur für einen ähnlicheren Vergleich zwischen diesem und verwendenhasattr
)def fn(): if not hasattr(fn, 'c'): fn.c = 0
fn.c += 1 return fn.c
Python hat keine statischen Variablen, aber Sie können es fälschen, indem Sie ein aufrufbares Klassenobjekt definieren und es dann als Funktion verwenden. Siehe auch diese Antwort .
Beachten Sie,
__call__
dass eine Instanz einer Klasse (eines Objekts) unter ihrem eigenen Namen aufgerufen werden kann. Aus diesem Grund ruft derfoo()
obige Aufruf die__call__
Methode der Klasse auf . Aus der Dokumentation :quelle
foo
die als Singleton bereitgestellte Instanz nicht verwenden .Verwenden Sie eine Generatorfunktion, um einen Iterator zu generieren.
Dann benutze es wie
Wenn Sie eine Obergrenze wünschen:
Wenn der Iterator beendet wird (wie im obigen Beispiel), können Sie ihn auch direkt durchlaufen, z
In diesen einfachen Fällen ist es natürlich besser, xrange zu verwenden :)
Hier ist die Dokumentation zur Renditeerklärung .
quelle
Andere Lösungen fügen der Funktion ein Zählerattribut hinzu, normalerweise mit verschlungener Logik, um die Initialisierung zu handhaben. Dies ist für neuen Code ungeeignet.
In Python 3 ist der richtige Weg, eine
nonlocal
Anweisung zu verwenden:Siehe PEP 3104 für die Spezifikation der
nonlocal
Anweisung.Wenn der Zähler für das Modul privat sein soll, sollte er
_counter
stattdessen benannt werden.quelle
global counter
Anweisung anstelle von tunnonlocal counter
(Sie könnennonlocal
nur in einer verschachtelten Funktion in den Abschlusszustand schreiben). Der Grund, warum Benutzer der Funktion ein Attribut hinzufügen, besteht darin, zu vermeiden, dass der globale Namespace für den funktionsspezifischen Status verschmutzt wird, sodass Sie keine noch härteren Aufgaben ausführen müssen, wenn zwei Funktionen unabhängigecounter
s benötigen . Diese Lösung lässt sich nicht skalieren. Attribute auf der Funktion tun. Die Antwort von kdb lautet, wienonlocal
man helfen kann, aber es erhöht die Komplexität.nonlocal
über empfehle,global
ist genau der, auf den Sie hinweisen - es funktioniert unter genau mehr Umständen.Die Verwendung eines Attributs einer Funktion als statische Variable weist einige potenzielle Nachteile auf:
Idiomatisches Python für das zweite Problem würde wahrscheinlich die Variable mit einem führenden Unterstrich benennen, um zu signalisieren, dass nicht darauf zugegriffen werden soll, während sie nachträglich zugänglich bleibt.
Eine Alternative wäre ein Muster mit lexikalischen Abschlüssen, die mit dem
nonlocal
Schlüsselwort in Python 3 unterstützt werden.Leider kenne ich keine Möglichkeit, diese Lösung in einen Dekorateur zu verkapseln.
quelle
Ähnlich wie der obige Code von vincent wird dieser als Funktionsdekorator verwendet, und auf statische Variablen muss mit dem Funktionsnamen als Präfix zugegriffen werden. Der Vorteil dieses Codes (obwohl zugegebenermaßen jeder klug genug ist, es herauszufinden) besteht darin, dass Sie mehrere statische Variablen haben und diese auf konventionellere Weise initialisieren können.
quelle
Ein bisschen lesbarer, aber ausführlicher (Zen of Python: explizit ist besser als implizit):
Sehen Sie hier für eine Erklärung, wie das funktioniert.
quelle
foo()
sollte das Wörterbuch auf den in der Funktionsdefinition angegebenen Wert neu initialisieren (also mit der Zählertaste mit dem Wert 0). Warum nicht?Python verwendet normalerweise Unterstriche, um private Variablen anzugeben. Der einzige Grund in C, die statische Variable innerhalb der Funktion zu deklarieren, besteht darin, sie außerhalb der Funktion auszublenden, was nicht wirklich idiomatisch für Python ist.
quelle
Nachdem ich mehrere Ansätze ausprobiert habe, verwende ich eine verbesserte Version von @ warvariucs Antwort:
quelle
Die idiomatische Methode besteht darin, eine Klasse zu verwenden , die Attribute haben kann. Wenn Instanzen nicht getrennt sein sollen, verwenden Sie einen Singleton.
Es gibt eine Reihe von Möglichkeiten, wie Sie "statische" Variablen in Python fälschen oder mischen können (eine, die bisher nicht erwähnt wurde, ist ein veränderbares Standardargument), aber dies ist nicht die pythonische, idiomatische Methode, dies zu tun. Verwenden Sie einfach eine Klasse.
Oder möglicherweise ein Generator, wenn Ihr Nutzungsmuster passt.
quelle
default
Argument das eleganteste.Auf diese Frage hin möchte ich eine andere Alternative vorstellen, die möglicherweise etwas besser zu verwenden ist und für Methoden und Funktionen gleich aussieht:
Wenn Ihnen die Verwendung gefällt, finden Sie hier die Implementierung:
quelle
Eine andere (nicht empfohlene!) Änderung des aufrufbaren Objekts wie https://stackoverflow.com/a/279598/916373 , wenn Sie nichts dagegen haben, eine funky Anrufsignatur zu verwenden, wäre zu tun
quelle
Anstatt eine Funktion mit einer statischen lokalen Variablen zu erstellen, können Sie jederzeit ein sogenanntes "Funktionsobjekt" erstellen und ihm eine Standard-Mitgliedsvariable (nicht statisch) zuweisen.
Da Sie ein Beispiel für C ++ angegeben haben, werde ich zunächst erklären, was ein "Funktionsobjekt" in C ++ ist. Ein "Funktionsobjekt" ist einfach eine beliebige Klasse mit einer Überladung
operator()
. Instanzen der Klasse verhalten sich wie Funktionen. Sie können beispielsweiseint x = square(5);
auch dann schreiben , wennsquare
es sich um ein Objekt (mit Überladungoperator()
) handelt und technisch gesehen keine "Funktion". Sie können einem Funktionsobjekt alle Funktionen zuweisen, die Sie einem Klassenobjekt geben könnten.In Python können wir auch überladen,
operator()
außer dass die Methode stattdessen den Namen hat__call__
:Hier ist eine Klassendefinition:
Hier ist ein Beispiel für die verwendete Klasse:
Die auf der Konsole gedruckte Ausgabe lautet:
Wenn Ihre Funktion Eingabeargumente annehmen soll, können Sie diese auch hinzufügen
__call__
:quelle
Soulution n + = 1
quelle
Eine globale Deklaration bietet diese Funktionalität. Im folgenden Beispiel (Python 3.5 oder höher, um das "f" zu verwenden) wird die Zählervariable außerhalb der Funktion definiert. Wenn Sie es in der Funktion als global definieren, bedeutet dies, dass die "globale" Version außerhalb der Funktion der Funktion zur Verfügung gestellt werden soll. Bei jeder Ausführung der Funktion wird der Wert außerhalb der Funktion geändert und über die Funktion hinaus beibehalten.
quelle
Eine statische Variable innerhalb einer Python-Methode
quelle
Ich persönlich bevorzuge Folgendes gegenüber Dekorateuren. Jedem das Seine.
quelle
Diese Antwort baut auf der Antwort von @claudiu auf.
Ich stellte fest, dass mein Code weniger klar wurde, wenn ich immer den Funktionsnamen voranstellen musste, wenn ich auf eine statische Variable zugreifen wollte.
In meinem Funktionscode würde ich nämlich lieber schreiben:
anstatt
Meine Lösung lautet also:
statics
der Funktion ein Attribut hinzustatics
als Alias hinzumy_function.statics
Anmerkung
Meine Methode verwendet eine Klasse namens
Bunch
, ein Wörterbuch, das den Zugriff im Attributstil a la JavaScript unterstützt (siehe den Originalartikel darüber, um 2000).Es kann über installiert werden
pip install bunch
Es kann auch so handgeschrieben werden:
quelle
types.SimpleNamespace
(verfügbar seit 3.3) unterstützt dieses Verhalten sofort (und ist in C unter CPython implementiert, sodass es ungefähr so schnell wie möglich ist).Aufbauend auf Daniels Antwort (Ergänzungen):
Der Grund, warum ich diesen Teil hinzufügen wollte, ist, dass statische Variablen nicht nur zum Inkrementieren um einen bestimmten Wert verwendet werden, sondern auch prüfen, ob die statische Variable einem Beispiel entspricht, als Beispiel aus dem wirklichen Leben.
Die statische Variable ist weiterhin geschützt und wird nur im Rahmen der Funktion use_foo () verwendet.
In diesem Beispiel funktioniert der Aufruf von foo () genauso wie (in Bezug auf das entsprechende c ++ - Äquivalent):
Wenn die Klasse Foo restriktiv als Singleton-Klasse definiert ist, wäre dies ideal. Dies würde es pythonischer machen.
quelle
Sicher, das ist eine alte Frage, aber ich denke, ich könnte ein Update bereitstellen.
Es scheint, dass das Leistungsargument überholt ist. Dieselbe Testsuite scheint ähnliche Ergebnisse für siInt_try und isInt_re2 zu liefern. Natürlich variieren die Ergebnisse, aber dies ist eine Sitzung auf meinem Computer mit Python 3.4.4 auf Kernel 4.3.01 mit Xeon W3550. Ich habe es mehrmals ausgeführt und die Ergebnisse scheinen ähnlich zu sein. Ich habe den globalen regulären Ausdruck in die statische Funktion verschoben, aber der Leistungsunterschied ist vernachlässigbar.
Wenn Leistungsprobleme aus dem Weg geräumt sind, scheint Try / Catch den zukunfts- und eckensichersten Code zu produzieren. Wickeln Sie ihn also einfach in die Funktion ein
quelle