419 Stimmen

Den Namen einer Variablen als Zeichenfolge erhalten

Ich habe bereits Wie bekomme ich den Namen einer Funktion als Zeichenfolge? gelesen.

Wie kann ich das Gleiche für eine Variable tun? Im Gegensatz zu Funktionen haben Python-Variablen nicht das Attribut __name__.

Anders ausgedrückt, wenn ich eine Variable wie diese habe:

foo = dict()
foo['bar'] = 2

Suche ich nach einer Funktion/einem Attribut, z.B. retrieve_name(), um ein DataFrame in Pandas aus dieser Liste zu erstellen, wobei die Spaltennamen anhand der Namen der tatsächlichen Dictionaries angegeben sind:

# Liste von Dictionaries für mein DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [retrieve_name(d) for d in list_of_dicts]

260voto

Aivar Paalberg Punkte 3868

Mit Python 3.8 kann man einfach die f-string-Debugging-Funktion verwenden:

>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo' 

Ein Nachteil dieser Methode ist, dass man 'foo' nur gedruckt bekommt, wenn man f'{foo=}' selbst hinzufügt. Mit anderen Worten: Man muss bereits den Namen der Variablen kennen. Mit anderen Worten entspricht der obige Codeausschnitt genau dem

>>> 'foo'

162voto

scohe001 Punkte 14570

Auch wenn die Variablenwerte nicht auf den Namen zurückverweisen, haben Sie Zugriff auf die Liste jeder zugewiesenen Variablen und ihres Werts, daher bin ich erstaunt, dass nur eine Person vorgeschlagen hat, dort durchzulaufen, um nach Ihrem Variablennamen zu suchen.

Jemand hat in dieser Antwort erwähnt, dass Sie möglicherweise den Stapel durchgehen und die lokalen und globalen Variablen von jedem überprüfen müssen, um foo zu finden, aber wenn foo im Bereich zugewiesen ist, in dem Sie diese retrieve_name Funktion aufrufen, können Sie inspect current frame verwenden, um alle diese lokalen Variablen zu erhalten.

Meine Erklärung ist vielleicht etwas zu umfangreich (vielleicht hätte ich ein "foo" weniger Wörter verwenden sollen), aber so würde es im Code aussehen (Beachten Sie, dass wenn mehrere Variablen demselben Wert zugewiesen sind, Sie beide Variablennamen erhalten werden):

import inspect

x, y, z = 1, 2, 3

def retrieve_name(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    return [var_name for var_name, var_val in callers_local_vars if var_val is var]

print(retrieve_name(y))

Wenn Sie diese Funktion von einer anderen Funktion aus aufrufen, etwas wie:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

Und Sie möchten das baz anstelle von bar, müssen Sie einfach einen Schritt weiter zurückgehen. Dies kann durch Hinzufügen eines zusätzlichen .f_back bei der Initialisierung von caller_local_vars erfolgen.

Sehen Sie hier ein Beispiel: ideone

137voto

kindall Punkte 167554

Die einzigen Objekte in Python, die kanonische Namen haben, sind Module, Funktionen und Klassen, und natürlich gibt es keine Garantie, dass dieser kanonische Name in einem beliebigen Namensraum nach der Definition der Funktion oder Klasse oder dem Import des Moduls eine Bedeutung hat. Diese Namen können auch nach der Erstellung der Objekte geändert werden, sodass sie nicht immer besonders vertrauenswürdig sind.

Was Sie tun möchten, ist nicht möglich ohne rekursives Durchlaufen des Baums benannter Objekte; ein Name ist eine Einwegreferenz zu einem Objekt. Ein gewöhnliches Python-Objekt enthält keine Verweise auf seine Namen. Stellen Sie sich vor, jedes ganze Zahl, jede Dict, jeder Liste, jeder Boolean müsste eine Liste von Strings pflegen, die Namen darstellten, die sich auf sie bezogen! Es wäre ein Albtraum in der Implementierung, mit wenig Nutzen für den Programmierer.

99voto

Panwen Wang Punkte 3078

Zusammenfassung

Verwenden Sie den Wrapper-Helfer von python-varname:

from varname.helpers import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2

Für den Teil der List Comprehension können Sie folgendes tun:

n_jobs = Wrapper() 
users = Wrapper() 
queues = Wrapper() 
priorities = Wrapper() 

list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# BITTE BEACHTEN Sie, dass Sie auf den  durch d.value zugreifen müssen

Ich bin der Autor des python-varname Pakets. Lassen Sie mich wissen, wenn Sie Fragen haben oder Probleme auf Github melden können.

Die ausführliche Antwort

Ist es überhaupt möglich?

Ja und Nein.

Wir rufen die Variablennamen zur Laufzeit ab, daher benötigen wir eine Funktion, die aufgerufen werden muss, um uns den Zugriff auf die vorherigen Frames zu ermöglichen, um die Variablennamen abzurufen. Deshalb benötigen wir dort einen Wrapper. In dieser Funktion parsen wir zur Laufzeit den Quellcode/AST-Knoten in den vorherigen Frames, um den genauen Variablennamen zu erhalten.

Allerdings sind die Quellcode/AST-Knoten in den vorherigen Frames nicht immer verfügbar, oder sie könnten von anderen Umgebungen modifiziert werden (z.B.: mit der assert-Anweisung von pytest). Ein einfaches Beispiel ist, dass der Code über exec() ausgeführt wird. Obwohl wir immer noch einige Informationen aus dem Bytecode abrufen können, erfordert dies zu viel Aufwand und ist auch fehleranfällig.

Wie kann man es machen?

Zunächst müssen wir identifizieren, in welchem Frame die Variable gegeben ist. Es ist nicht immer einfach der direkte vorherige Frame. Zum Beispiel können wir einen anderen Wrapper für die Funktion haben:

from varname import varname

def func():
  return varname()

def wrapped():
  return func()

x = wrapped()

In obigem Beispiel müssen wir den Frame im Inneren von wrapped überspringen, um zum richtigen Frame x = wrapped() zu gelangen, um das x zu lokalisieren. Die Argumente frame und ignore von varname ermöglichen es uns, einige dieser Zwischenframes zu überspringen. Weitere Details finden Sie in der README-Datei und der API-Dokumentation des Pakets.

Dann müssen wir den AST-Knoten parsen, um zu lokalisieren, wo der Variablen ein Wert zugewiesen wird (Funktionsaufruf). Es ist nicht immer nur eine einfache Zuweisung. Manchmal können komplexe AST-Knoten vorhanden sein, zum Beispiel x = [wrapped()]. Wir müssen durch den AST-Baum navigieren, um die richtige Zuweisung zu identifizieren.

Wie zuverlässig ist es?

Sobald wir den Zuweisungsknoten identifiziert haben, ist es zuverlässig.

varname hängt alles vom executing Paket ab, um den Knoten zu finden. Der von executing erkannte Knoten ist garantiert der richtige (siehe auch dies).

Es funktioniert teilweise in Umgebungen, in denen andere AST-Tricks angewendet werden, einschließlich pytest, ipython, macropy, birdseye, reticulate mit R, etc. Weder executing noch varname funktionieren zu 100% mit diesen Umgebungen.

Brauchen wir ein Paket dafür?

Nun, wieder ja und nein.

Wenn Ihr Szenario einfach ist, ist der von @juan Isaza oder @scohe001 bereitgestellte Code wahrscheinlich ausreichend, um mit dem Fall umzugehen, in dem eine Variable im direkten vorherigen Frame definiert ist und der AST-Knoten eine einfache Zuweisung ist. Sie müssen einfach einen Frame zurückgehen und die Informationen dort abrufen.

Wenn das Szenario jedoch komplizierter wird oder wir verschiedene Anwendungsszenarien anpassen müssen, benötigen Sie wahrscheinlich ein Paket wie python-varname, um damit umzugehen. Diese Szenarien könnten folgendes umfassen:

  1. freundlichere Meldungen präsentieren, wenn der Quellcode nicht verfügbar ist oder AST-Knoten nicht zugänglich sind
  2. Zwischenframes überspringen (ermöglicht es, dass die Funktion in anderen Zwischenframes gewickelt oder aufgerufen wird)
  3. Aufrufe von built-in Funktionen oder Bibliotheken automatisch ignorieren. Zum Beispiel: x = str(func())
  4. mehrere Variablennamen auf der linken Seite der Zuweisung abrufen
  5. usw.

Was ist mit dem f-string?

Wie die Antwort von @Aivar Paalberg zeigt. Es ist definitiv schnell und zuverlässig. Es wird jedoch nicht zur Laufzeit ausgeführt, das bedeutet, dass Sie wissen müssen, dass es sich um foo handelt, bevor Sie den Namen ausgeben. Mit varname müssen Sie jedoch nicht wissen, welcher Variablenname kommt:

from varname import varname

def func():
  return varname()

# In externen Verwendungen
x = func() # 'x'
y = func() # 'y'

Zum Abschluss

python-varname kann nicht nur den Variablennamen aus einer Zuweisung erkennen, sondern auch:

  • Variablennamen direkt abrufen, mit nameof
  • das nächste sofortige Attribut finden, mit will
  • Argumentnamen/Quellen, die an eine Funktion übergeben wurden, mit argname

Lesen Sie mehr in der Dokumentation.

Allerdings möchte ich abschließend sagen, versuchen Sie, es zu vermeiden, es zu verwenden, wann immer Sie können.

Denn Sie können nicht sicher sein, dass der Client-Code in einer Umgebung ausgeführt wird, in der der Quellknoten verfügbar ist oder der AST-Knoten zugänglich ist. Und natürlich kostet es Ressourcen, den Quellcode zu parsen, die Umgebung zu identifizieren, die AST-Knoten abzurufen und sie bei Bedarf auszuwerten.

50voto

juan Isaza Punkte 3023

Auf Python3 wird diese Funktion den äußersten Namen im Stapel erhalten:

import inspect

def retrieve_name(var):
        """
        Ruft den Namen von var ab. Macht dies vom äußersten Frame aus nach innen.
        :param var: Variable, deren Namen abgerufen werden soll.
        :return: Zeichenfolge
        """
        for fi in reversed(inspect.stack()):
            names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
            if len(names) > 0:
                return names[0]

Es ist überall im Code nützlich. Durchsucht den umgekehrten Stapel auf der Suche nach der ersten Übereinstimmung.

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