807 Stimmen

Was ist die Python-Entsprechung von statischen Variablen innerhalb einer Funktion?

Was ist die idiomatische Python-Entsprechung für diesen C/C++-Code?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

Wie implementiert man das statische Mitglied auf der Funktionsebene, im Gegensatz zur Klassenebene? Und ändert sich etwas, wenn man die Funktion in eine Klasse einordnet?

33 Stimmen

Es gibt NO Gleichwertigkeit, fürchte ich. Selbst wenn man den Decorator-Hack mit Funktionsattributen durchführt, kann man auf die Variable von außen zugreifen, was den Sinn der Sache leider zunichte macht. Außerdem müssen Sie den Funktionsnamen in der Funktion hart codieren, was sehr ärgerlich ist. Ich würde vorschlagen, stattdessen globale Variablen einer Klasse oder eines Moduls zu verwenden, mit der konventionellen _ Vorwahl.

17 Stimmen

Für Nicht-C-Programmierer, [ [stackoverflow.com/questions/5033627/](https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c#5033656](a "static variable inside of a function in c%235033656%5d(a") statische Variable innerhalb einer Funktion ist nur innerhalb des Funktionsbereichs dieser Funktion sichtbar, aber ihre Lebensdauer ist die gesamte Lebensdauer des Programms, und sie wird nur einmal initialisiert). Im Grunde genommen handelt es sich um einen dauerhaften Zähler oder eine Speichervariable, die zwischen Funktionsaufrufen lebt.

2 Stimmen

@lpapp: Irgendwie schon, es ist ein Klassenmitglied . Sie haben Recht, dass wir nicht verhindern können, dass andere Codes sie anzeigen oder ändern.

848voto

Claudiu Punkte 215027

Ein bisschen umgedreht, aber das sollte funktionieren:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
foo.counter = 0

Wenn Sie den Zählerinitialisierungscode oben statt unten haben möchten, können Sie einen Dekorator erstellen:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

Dann verwenden Sie den Code wie folgt:

@static_vars(counter=0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

Dazu müssen Sie immer noch die foo. Präfix, leider.

(Credit: @ony )

0 Stimmen

Nicht wirklich statisch (im Sinne von C++), wenn Sie den Zähler ändern, wirkt sich das auf alle anderen Instanzen von foo() aus?

31 Stimmen

Es gibt nur eine Instanz von foo - diese eine Funktion. Alle Aufrufe greifen auf dieselbe Variable zu.

154 Stimmen

Tut mir leid, dass ich das wieder ausgrabe, aber ich würde lieber if "counter" not in foo.__dict__: foo.counter = 0 wie die ersten Zeilen von foo() . Dies würde dazu beitragen, Code außerhalb der Funktion zu vermeiden. Ich bin mir aber nicht sicher, ob das 2008 schon möglich war. P.S. Ich habe diese Antwort gefunden, als ich nach der Möglichkeit suchte, statische Funktionsvariablen zu erstellen, also ist dieser Thread noch "lebendig" :)

286voto

vincent Punkte 5980

Sie können einer Funktion Attribute hinzufügen und sie als statische Variable verwenden.

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

Wenn Sie die Variable nicht außerhalb der Funktion einrichten wollen, können Sie alternativ hasattr() zur Vermeidung einer AttributeError Ausnahme:

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

Jedenfalls sind statische Variablen eher selten, und Sie sollten einen besseren Platz für diese Variable finden, höchstwahrscheinlich innerhalb einer Klasse.

9 Stimmen

Warum nicht statt der if-Anweisung versuchen?

15 Stimmen

try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1 sollten dasselbe tun und stattdessen Ausnahmen verwenden.

4 Stimmen

Ausnahmen sollten für Ausnahmesituationen verwendet werden, d. h. für Situationen, von denen der Programmierer erwartet, dass sie nicht eintreten, z. B. wenn eine Eingabedatei, die erfolgreich geöffnet wurde, plötzlich nicht mehr verfügbar ist. Dies ist eine erwartete Situation, eine if-Anweisung ist sinnvoller.

262voto

rav Punkte 3318

Man könnte auch überlegen:

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

Begründungen:

  • sehr pythonisch ("Bitte um Vergebung, nicht um Erlaubnis")
  • Ausnahme (die nur einmal ausgelöst wird) anstelle von if Zweig (denken StopIteration Ausnahme)

12 Stimmen

Ich beschäftige mich noch nicht lange mit Python, aber dies erfüllt einen der impliziten Grundsätze der Sprache: wenn es nicht (ziemlich) einfach ist, machen Sie es falsch .

2 Stimmen

Hat nicht sofort mit Klassenmethoden funktioniert, "self.foo.counter = 1" löst wieder AttributeError aus.

20 Stimmen

Dies ist die richtige Lösung und sollte die akzeptierte Antwort sein, weil der Initialisierungscode ausgeführt wird, wenn die Funktion aufgerufen wird und nicht wenn das Modul ausgeführt wird oder wenn etwas daraus importiert wird, was der Fall ist, wenn Sie den Dekorator-Ansatz aus der aktuell akzeptierten Antwort verwenden. Siehe Ausführung von Python-Dekoratorfunktionen . Wenn Sie ein großes Bibliotheksmodul haben, wird jeder Dekorator ausgeführt, einschließlich der Funktionen, die Sie nicht importieren.

77voto

Jonathan Punkte 1694

Viele haben bereits vorgeschlagen, 'hasattr' zu testen, aber es gibt eine einfachere Antwort:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

Kein try/except, kein Testen von hasattr, nur getattr mit einem Standardwert.

5 Stimmen

Achten Sie auf den dritten Parameter von getattr, wenn Sie dort eine func eingeben, z.B.: def func(): def foo(): return 1112 func.counter = getattr(func, 'counter', foo()) + 1 wenn Sie func aufrufen, wird immer foo aufgerufen!

4 Stimmen

Nur ein Aufruf von getattr jedes Mal, wenn diese Funktion aufgerufen wird. Das ist in Ordnung, wenn die Leistung nicht ein Problem ist, wenn es try/except wird Hände nach unten zu gewinnen ist.

7 Stimmen

@MarkLawrence: Zumindest auf meiner Windows x64 3.8.0 Installation ist der Leistungsunterschied zwischen dieser Antwort und Das Äquivalent von ravwojdyla try / except basierter Ansatz ist ziemlich bedeutungslos. Eine einfache ipython %%timeit Mikrobenchmark wurden die Kosten für die try / except mit 255 ns pro Aufruf, gegenüber 263 ns für die getattr basierte Lösung. Ja, die try / except ist zwar schneller, aber es ist nicht gerade ein "haushoher Sieg"; es handelt sich um eine winzige Mikro-Optimierung. Schreiben Sie den Code, der Ihnen klarer erscheint, und machen Sie sich keine Gedanken über triviale Leistungsunterschiede wie diesen.

60voto

Jeremy Punkte 1

Andere Antworten haben gezeigt, wie Sie dies tun sollten. Hier ist ein Weg, den Sie nicht gehen sollten:

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

Standardwerte werden nur initialisiert, wenn die Funktion zum ersten Mal ausgewertet wird, nicht jedes Mal, wenn sie ausgeführt wird, so dass Sie eine Liste oder ein anderes veränderbares Objekt verwenden können, um statische Werte zu speichern.

0 Stimmen

Ich habe das versucht, aber aus irgendeinem Grund initialisierte sich der Funktionsparameter auf 140 und nicht auf 0. Woran kann das liegen?

0 Stimmen

@andrewdotnich: Ich bin mir nicht sicher, warum das so ist, ich habe es unter 2.5, 2.6 und 3.0rc1 ausprobiert, und es hat in jedem Fall funktioniert. =\

0 Stimmen

@bouvard: Ja, ich versuche es generell zu vermeiden, aber für schnelle Skripte, um deren Codequalität ich mir keine großen Sorgen mache, kann es praktisch sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X