Dienstag, Januar 21, 2020

In Python 2, wie kann ich schreiben auf Variablen im übergeordneten Bereich?

Ich habe den folgenden code innerhalb einer Funktion:

stored_blocks = {}
def replace_blocks(m):
    block = m.group(0)
    block_hash = sha1(block)
    stored_blocks[block_hash] = block
    return '{{{%s}}}' % block_hash

num_converted = 0
def convert_variables(m):
    name = m.group(1)
    num_converted += 1
    return '<%%= %s %%>' % name

fixed = MATCH_DECLARE_NEW.sub('', template)
fixed = MATCH_PYTHON_BLOCK.sub(replace_blocks, fixed)
fixed = MATCH_FORMAT.sub(convert_variables, fixed)

Hinzufügen von Elementen zu stored_blocks funktioniert gut, aber ich kann nicht zu einer Erhöhung num_converted in der zweiten Teilfunktion:

UnboundLocalError: lokale variable ’num_converted‘ referenced before assignment

Könnte ich global aber Globale Variablen sind hässlich und ich weiß wirklich nicht brauchen, dass variable global an alle.

So, ich bin gespannt, wie ich schreiben kann, um eine variable in der übergeordneten Funktion scope.
nonlocal num_converted würde wahrscheinlich den job zu erledigen, aber ich brauche eine Lösung, die funktioniert mit Python 2.x.

  • Im Gegensatz zu etwas landläufigen Meinung (Beurteilung durch diese Art der Fragen) def ist nicht das einzige Schlüsselwort, definiert eine namespace: es gibt auch class.
InformationsquelleAutor ThiefMaster | 2011-01-31

6 Kommentare

  1. 86

    Problem: Dies ist, weil Python die scoping-Regeln sind dement. Die Anwesenheit der += Zuweisungs-operator markiert das Ziel, in num_converted, als lokal auf dem umschließenden Funktions-scope, und es ist kein Ton Art in Python 2.x, um den Zugriff nur einem scoping-Ebene aus. Nur die global Stichwort heben können variable Bezüge aus dem aktuellen Bereich, und es bringt Sie direkt an die Spitze.

    Update: Drehen num_converted in a single-element array.

    num_converted = [0]
    def convert_variables(m):
        name = m.group(1)
        num_converted[0] += 1
        return '<%%= %s %%>' % name
    • Können Sie erklären, warum das notwendig ist? Ich hätte erwartet, dass der OPs-code zu arbeiten.
    • Da Python die scoping-Regeln sind dement. Die Anwesenheit der += Zuweisungs-operator markiert das Ziel, in num_converted, als lokal auf dem umschließenden Funktions-scope, und es ist kein Ton Art in Python 2.x, um den Zugriff nur einem scoping-Ebene aus. Nur die global Stichwort heben können variable Bezüge aus dem aktuellen Bereich, und es bringt Sie direkt an die Spitze.
    • Ich habe diesen Ansatz, und es funktioniert gut.
    • Das ist nicht clever, das ist eigentlich ziemlich schlechten code. Es gibt Klassen (siehe Kommentar unter der Frage). Diese version verwendet eine Globale variable, die Sie immer vermeiden sollten. Nicht mit global bedeutet nicht, dass Sie nicht über eine Globale variable.
    • Die Variablen in Frage, die nicht global in jedem Sinne des Wortes. Die OP ist der erste Satz besagt, dass der gesamte code-block, der Sie präsentiert wird, ist innerhalb einer Funktion.
    • Oh, du hast Recht 🙂 Aber eine Klasse wäre noch besser geeignet in diesem Fall. Oder eine explizite namespace-Objekt beantwortet gerne hier, es ist also klar, was Los ist.
    • Das ist eine Menge von Gerüsten für eine variable. Wenn Sie brauchen mehr Klarheit, als meine Antwort darauf bietet (wirklich?), Ich würde zugunsten scope = {'num_converted':0} … scope['num_converted'] += 1.

  2. 29

    (siehe unten für die bearbeitete Antwort)

    Können Sie so etwas wie:

    def convert_variables(m):
        name = m.group(1)
        convert_variables.num_converted += 1
        return '<%%= %s %%>' % name
    
    convert_variables.num_converted = 0

    Diese Weise num_converted funktioniert wie ein C-wie „static“ variable, die convert_variable Methode


    (bearbeitet)

    def convert_variables(m):
        name = m.group(1)
        convert_variables.num_converted = convert_variables.__dict__.get("num_converted", 0) + 1
        return '<%%= %s %%>' % name

    Diese Weise brauchen Sie nicht initialisiert den Zähler in der main-Prozedur.

    • Recht. Und beachten Sie, dass muss erstellen der Attribut convert_variables.num_converted nachdem definieren der Funktion, aber es sieht seltsam aus, so zu tun.
    • die meisten befriedigende Antwort auf diese Frage, abgesehen von der nichtlokale im 3.x; mit veränderbarer Typ [] ist Billig Abhilfe.
  3. 9

    Mithilfe der global Schlüsselwort ist in Ordnung. Wenn Sie schreiben:

    num_converted = 0
    def convert_variables(m):
        global num_converted
        name = m.group(1)
        num_converted += 1
        return '<%%= %s %%>' % name

    num_converted nicht zu einer „globalen variable“ (also nicht sichtbar, in anderen unerwarteten Orten), es bedeutet nur, es kann geändert werden, innerhalb convert_variables. Das scheint genau das, was Sie wollen.

    Um es anders auszudrücken, num_converted ist bereits eine Globale variable. Alle global num_converted syntax teilt Python „innerhalb dieser Funktion, nicht die Erstellung einer lokalen num_converted variable, verwenden Sie stattdessen die vorhandene Globale.

    • global im 2.x arbeitet ziemlich viel, wie nonlocal hat in 3.x.
    • „Um es anders auszudrücken, num_converted ist bereits eine Globale variable“ – mein code ausgeführt wird, innerhalb einer Funktion.. so ist es derzeit nicht global.
    • Ah, ich haden nicht die Aufmerksamkeit auf die „innere Funktion“ Teil, sorry – in diesem Fall, Marcelo Länge einer Liste kann eine bessere (aber hässliche) Lösung.
  4. 7

    Was über die Verwendung von Klasse-Instanz zu halten den Staat?
    Sie instanziiert eine Klasse, und übergeben Sie die Instanz-Methoden, um subs und solche Funktionen würde einen Verweis auf sich selbst…

    • Klingt ein bisschen overkillish und wie eine Lösung aus einem Java-Programmierer. ;p
    • hey, aber es ist sauber! 😉
    • Warum ist das übertrieben? Wenn Sie möchten, den Zugang zu den übergeordneten Bereich, den Sie sollte verwenden Sie eine Klasse in Python.
    • denn in jeder anderen vernünftigen Sprache mit first-class-Funktionen unterstützen (JS, funktionale Sprachen), die Verschlüsse funktionieren.
  5. 6

    Ich habe einige Bemerkungen.

    Erste, eine Anwendung für eine solche verschachtelte Funktionen kommt beim Umgang mit raw-Rückrufe, so sind die in Bibliotheken verwendet werden, wie xml.Parser.expat. (Die Bibliothek der Autoren wählten diesen Ansatz, die bedenklich sein könnten, aber … es gibt Gründe, warum Sie es dennoch.)

    Sekunde: innerhalb einer Klasse gibt es viel schönere alternativen zum array (num_converted[0]). Ich vermute, das ist, was Sebastjan Rede.

    class MainClass:
        _num_converted = 0
        def outer_method( self ):
            def convert_variables(m):
                name = m.group(1)
                self._num_converted += 1
                return '<%%= %s %%>' % name

    Es ist immer noch seltsam genug, um Verdienst zu, einen Kommentar im code…
    Aber die variable ist zumindest lokal zu der Klasse.

    • Hey, herzlich willkommen auf Stack Overflow – aber veröffentlichen eine „Anmerkung“ als Antwort ist nicht wirklich etwas, was man hier tun kann. Wir haben die Kommentare für diesen (allerdings müssen Sie einige rep zu posten – aber keine Antworten geben, nur weil Sie nicht genügend Mitarbeiter für einen Kommentar noch)
    • Hi, bist Du auch herzlich willkommen! Ich verstehe nicht, Ihre Bemerkung oder mehrere der Begriffe, die Sie verwenden. Und ich bin ein vielbeschäftigter Mann, der nur versucht, hilfsbereit zu sein!
    • Kein problem – ein Blick auf stackoverflow.com/about obwohl. Während hilfreich, ist immer geschätzt einen Kommentar gepostet wird schließlich gelöscht werden, egal wie gut es ist.
  6. 0

    Geändert von: https://stackoverflow.com/a/40690954/819544

    Nutzen Sie das inspect Modul Zugriff auf die aufrufende Bereich globals dict und schreiben Sie in die. Das bedeutet, dass dieser trick kann auch genutzt werden, um Zugriff auf das aufrufen von Rahmen aus einer geschachtelten Funktion definiert, die in einem importierten submodule.

    import inspect 
    
    def get_globals(scope_level=0):
        return dict(inspect.getmembers(inspect.stack()[scope_level][0]))["f_globals"]
    
    num_converted = 0
    def foobar():
        get_globals(0)['num_converted'] += 1
    
    foobar()
    print(num_converted) 
    # 1

    Spielen mit der scope_level argument, wie gebraucht. Einstellung scope_level=1 arbeitet für eine definierte Funktion in ein Submodul, scope_level=2 für die innere Funktion definiert einen Dekorateur in einem Submodul, etc.

    NB: Nur weil du kann dies tun, bedeutet nicht, Sie sollten.

Kostenlose Online-Tests