Abrufen des Namens einer Variablen als Zeichenfolge

173

In diesem Thread wird erläutert, wie Sie den Namen einer Funktion als Zeichenfolge in Python abrufen: So erhalten Sie einen Funktionsnamen als Zeichenfolge.

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

Mit anderen Worten, wenn ich eine Variable habe wie:

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

Ich suche nach einer Funktion / einem Attribut, z. B. retrieve_name()um aus dieser Liste einen DataFrame in Pandas zu erstellen , wobei die Spaltennamen durch die Namen der tatsächlichen Wörterbücher angegeben werden:

# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [retrieve_name(d) for d in list_of_dicts] 
Amelio Vazquez-Reina
quelle

Antworten:

17

Mit dem python-varnamePaket können Sie den Namen der Variablen einfach abrufen

https://github.com/pwwang/python-varname

In Ihrem Fall können Sie Folgendes tun:

from varname import Wrapper

foo = Wrapper(dict())

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

Sie können auch versuchen, den Variablennamen direkt abzurufen:

from varname import nameof

foo = dict()

fooname = nameof(foo)
# fooname == 'foo'

Ich bin der Autor dieses Pakets. Bitte lassen Sie mich wissen, wenn Sie Fragen haben oder Probleme auf Github einreichen können.

Panwen Wang
quelle
2
OK, endlich verstanden! Installiert mit den folgenden pip3 install python-varname==0.1.5; importiert mitfrom varname import nameof
enter_display_name_here
106

Die einzigen Objekte in Python, die kanonische Namen haben, sind Module, Funktionen und Klassen, und natürlich gibt es keine Garantie dafür, dass dieser kanonische Name in einem Namespace eine Bedeutung hat, nachdem die Funktion oder Klasse definiert oder das Modul importiert wurde. Diese Namen können auch nach dem Erstellen der Objekte geändert werden, sodass sie möglicherweise nicht immer besonders vertrauenswürdig sind.

Was Sie tun möchten, ist nicht möglich, ohne rekursiv über den Baum benannter Objekte zu gehen . Ein Name ist eine Einwegreferenz auf ein Objekt. Ein allgemeines Python-Objekt oder ein Python-Objekt mit Gartenvielfalt enthält keine Verweise auf seine Namen. Stellen Sie sich vor, jede ganze Zahl, jedes Diktat, jede Liste, jeder Boolesche Wert müsste eine Liste von Zeichenfolgen führen, die Namen darstellen, die darauf verweisen! Es wäre ein Implementierungs-Albtraum, der für den Programmierer wenig von Vorteil ist.

irgendwie
quelle
7
Vielen Dank. Aber warum macht Python das dann für Funktionen? (dh eine Art von Python-Objekten)
Amelio Vazquez-Reina
1
@kindall: Module nicht vergessen: Sie haben auch kanonische Namen. Vergessen Sie auch nicht, dass __name__dies immer geändert werden kann, was bedeutet, dass der "kanonische" Name möglicherweise kein Name ist, der zum Abrufen des Objekts verwendet werden kann (z. B. beim dynamischen Erstellen von Klassen).
Nneonneo
8
Ihre Aussage "es ist einfach nicht möglich" ist falsch, wie @ scohe001 gezeigt hat . Die Variablennamendatenbank von Python ist wie jede andere relationale Datenbank. Sie können jederzeit in "reverse" nach verwandten Objekten suchen und den zuerst gefundenen oder den gesamten Satz gültiger Variablennamen für eine bestimmte Variable zurückgeben.
Kochfelder
3
@hobs Du bist technisch korrekt ... die beste Art von richtig. In der Praxis gibt es jedoch so viele mögliche Namen für ein Objekt, dass es mehr Probleme gibt, als es sich lohnt, zu versuchen, sie zu erhalten.
Kindall
1
@kindall Ich denke, Sie haben Recht, wenn Ihre "Wert" -Schwelle O (1) ist. Die Schleife von scohe001 wäre O (N).
Kochfelder
82

Auch wenn Variablenwerte nicht auf den Namen verweisen, haben Sie Zugriff auf die Liste aller zugewiesenen Variablen und deren Wert. Ich bin daher erstaunt, dass nur eine Person vorgeschlagen hat, sich dort durchzuschleifen, um nach Ihrem Variablennamen zu suchen.

Jemand hat in dieser Antwort erwähnt, dass Sie möglicherweise den Stapel durchlaufen und alle Einheimischen und Globalen überprüfen müssen, um sie zu finden foo. Wenn foosie jedoch in dem Bereich zugewiesen sind, in dem Sie diese retrieve_nameFunktion aufrufen , können Sie mit inspect's current framealle diese lokalen Variablen abrufen .

Meine Erklärung mag etwas zu wortreich sein (vielleicht hätte ich ein "foo" weniger Wörter verwenden sollen), aber so würde es im Code aussehen ( Beachten Sie, dass Sie es tun werden, wenn mehr als eine Variable demselben Wert zugewiesen ist beide Variablennamen abrufen ):

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, können Sie Folgendes tun:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

Und Sie möchten das bazstatt bar, Sie müssen nur einen Bereich weiter zurückgehen. Dies kann durch Hinzufügen eines zusätzlichen .f_backin der caller_local_varsInitialisierung erfolgen.

Sehen Sie hier ein Beispiel: ideone

scohe001
quelle
1
@theodox Kann ich nur zustimmen, da dies wird wahrscheinlich bis handeln mit import hooks, ironpythonundjython
scohe001
5
Das wird nicht funktionieren. Atomvariablen haben ihren Namen nicht als Attribut. Also , wenn Sie haben a, b = 2, 2, retrieve_name(a)und retrieve_name(b)werden beide Rückkehr ['a', 'b']oder['b', 'a']
tomas
1
@tomas Tatsächlich ist dies ein Implementierungsdetail einer Optimierung von cPython, bei der Ganzzahlen unter 255 im Grunde genommen Singletons sind, sodass alle Variablen, denen diese Werte zugewiesen wurden, effektiv truezu einem isVergleich zurückkehren
Toote
1
Gibt es eine Modifikation davon, um den Namen einer übergebenen Var zu erhalten? zB def foo(bar): retrieve_name(bar)wird immer bar zurück, aber was ist, wenn Sie wollen , foo(baz)um Rückkehr baz) statt bar?
SumNeuron
1
@SumNeuron Sie müssten nur die Zeile ändern, die zugewiesen wird callers_local_vars, um einen Bereich weiter zurück zu gehen, damit der Bereich des Aufrufs angezeigt wird foo. Ändern Sie die Zeile in inspect.currentframe().f_back.f_back.f_locals.items()(beachten Sie das Extra f_back).
scohe001
36

Mit Python 3.8 kann man einfach die F-String-Debugging-Funktion verwenden:

>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo' 
Aivar Paalberg
quelle
Nett ! Und nur wenn Sie den Namen einer Immobilie anstelle eines Objekts erhalten möchten, können Sief'{foo=}'.split('=')[0].split('.')[-1]
Mickael V.
30

Unter python3 erhält diese Funktion den äußersten Namen im Stapel:

import inspect


def retrieve_name(var):
        """
        Gets the name of var. Does it from the out most frame inner-wards.
        :param var: variable to get name from.
        :return: string
        """
        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. Durchläuft den umgekehrten Stapel und sucht nach dem ersten Match.

Juan Isaza
quelle
Gut gemacht! Obwohl ich es versuche retrieve_name(SomeClass.some_attribute), funktioniert es nicht. Können Sie uns dabei weiterhelfen?
Nam G VU
Dies kämpft mit booleschen Variablen. Ich ende mitstop_on_error
SumNeuron
26

Ich glaube nicht, dass das möglich ist. Betrachten Sie das folgende Beispiel:

>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664

Das aund zeigt bauf dasselbe Objekt, aber das Objekt kann nicht wissen, welche Variablen darauf zeigen.

korylprince
quelle
2
Sicherlich kann die Beziehung durch Erweitern der Referenzzählung in beide Richtungen hergestellt werden . Diese Antwort (und einige andere) bietet sogar eine Implementierung.
Shayaan
17
def name(**variables):
    return [x for x in variables]

Es wird so verwendet:

name(variable=variable)
fdvfcges
quelle
12
Dies gibt nicht den Namen der gewünschten Variablen zurück, sondern "Variablen". Zum Beispiel - using name(variable=variable)wird ausgegeben ['variable']und using name(variable=another_variable)wird nicht ausgegeben ['another_variable'], sondern ['variable'].
DalyaG
1
Eigentlich funktioniert es wie erwartet. Sie müssen nur beide «Variablen» durch Ihre Variablen ersetzen. Es wird eine Liste mit einem Element mit einer Zeichenfolge des Namens der ersten Variablen zurückgegeben. Zum Beispiel: >>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']`
Alguem Meugla
8

Hier ist ein Ansatz. Ich würde dies für nichts Wichtiges empfehlen, da es ziemlich spröde sein wird. Aber es kann getan werden.

Erstellen Sie eine Funktion, die das inspectModul verwendet, um den Quellcode zu finden, der es aufgerufen hat. Anschließend können Sie den Quellcode analysieren, um die Variablennamen zu identifizieren, die Sie abrufen möchten. Hier ist beispielsweise eine aufgerufene Funktion autodict, die eine Liste von Variablen verwendet und ein Wörterbuch zurückgibt, das Variablennamen ihren Werten zuordnet. Z.B:

x = 'foo'
y = 'bar'
d = autodict(x, y)
print d

Würde geben:

{'x': 'foo', 'y': 'bar'}

Das Überprüfen des Quellcodes selbst ist besser als das Durchsuchen des locals()oder globals()weil der letztere Ansatz Ihnen nicht sagt, welche der Variablen die gewünschten sind.

Hier ist auf jeden Fall der Code:

def autodict(*args):
    get_rid_of = ['autodict(', ',', ')', '\n']
    calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
    calling_code = calling_code[calling_code.index('autodict'):]
    for garbage in get_rid_of:
        calling_code = calling_code.replace(garbage, '')
    var_names, var_values = calling_code.split(), args
    dyn_dict = {var_name: var_value for var_name, var_value in
                zip(var_names, var_values)}
    return dyn_dict

Die Aktion erfolgt in der Zeile mit inspect.getouterframes, die die Zeichenfolge innerhalb des aufgerufenen Codes zurückgibt autodict.

Der offensichtliche Nachteil dieser Art von Magie besteht darin, dass Annahmen darüber getroffen werden, wie der Quellcode strukturiert ist. Und natürlich funktioniert es überhaupt nicht, wenn es im Interpreter ausgeführt wird.

Zachary Ernst
quelle
Hallo, ich habe nur drei Fragen: 1. Warum "1"? 2. Warum "4"? 3. Warum "0"? :)
Piotr Wasilewicz
7

Ich habe das Paket Zauberei geschrieben , um diese Art von Magie robust zu machen. Du kannst schreiben:

from sorcery import dict_of

columns = dict_of(n_jobs, users, queues, priorities)

und übergeben Sie das an den Datenrahmenkonstruktor. Es ist äquivalent zu:

columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)
Alex Hall
quelle
6
>>> locals()['foo']
{}
>>> globals()['foo']
{}

Wenn Sie Ihre eigene Funktion schreiben möchten, können Sie dies so tun, dass Sie nach einer in Einheimischen definierten Variablen suchen und dann die globalen Werte überprüfen. Wenn nichts gefunden wird, können Sie mit id () vergleichen, um festzustellen, ob die Variable auf dieselbe Position im Speicher verweist.

Wenn sich Ihre Variable in einer Klasse befindet, können Sie className verwenden. dict .keys () oder Vars (Selbst-) zu sehen , ob Ihre Variable definiert wurde.

Blakev
quelle
Was ist, wenn sich der Name in einem Anruferrahmen befindet? Dann müssten Sie dumme Dinge tun, wie über den Stapel gehen und alle überprüfen localsund globals... und Sie riskieren, den Namen falsch zu verstehen, wenn keiner tatsächlich existiert. Es ist eine Menge Arbeit ohne wirklichen nützlichen Gewinn.
Nneonneo
Die ganze Frage ist albern ... aber wenn es etwas ist, was er tun wollte, ist es möglich. Um die Existenz zu überprüfen, können Sie Globals (). Setdefault (var, <neues Objekt vom Typ (var)) verwenden, um etwas zu erstellen, wenn nichts vorhanden ist. Ich bin mir nicht ganz sicher, wofür er es will, aber vielleicht könnte er etwas Besseres herausfinden, wenn er lernt, dass Variablen nach Umfang gehalten werden.
Blakev
3

In Python, die defund classbinden Schlüsselwörter einen bestimmten Namen für das Objekt sie (Funktion oder Klasse) definieren. In ähnlicher Weise erhalten Module einen Namen, weil sie im Dateisystem als etwas Bestimmtes bezeichnet werden. In allen drei Fällen gibt es eine offensichtliche Möglichkeit, dem betreffenden Objekt einen "kanonischen" Namen zuzuweisen.

Für andere Arten von Objekten kann ein solcher kanonischer Name jedoch einfach nicht existieren . Betrachten Sie beispielsweise die Elemente einer Liste. Die Elemente in der Liste sind nicht einzeln benannt, und es ist durchaus möglich, dass Sie in einem Programm nur auf Listenindizes in der enthaltenen Liste verweisen können. Wenn eine solche Liste von Objekten an Ihre Funktion übergeben wurde, konnten Sie den Werten möglicherweise keine aussagekräftigen Bezeichner zuweisen.

Python speichert den Namen auf der linken Seite einer Zuweisung nicht im zugewiesenen Objekt, weil:

  1. Es müsste herausgefunden werden, welcher Name unter mehreren widersprüchlichen Objekten "kanonisch" ist.
  2. Es würde keinen Sinn machen für Objekte, die niemals einem expliziten Variablennamen zugewiesen werden.
  3. Es wäre äußerst ineffizient,
  4. Das macht buchstäblich keine andere existierende Sprache.

So haben beispielsweise Funktionen, die mit definiert wurden, lambdaimmer den "Namen" <lambda>und nicht einen bestimmten Funktionsnamen.

Der beste Ansatz wäre einfach, den Anrufer zu bitten, eine (optionale) Liste von Namen einzugeben. Wenn die Eingabe von '...','...'zu umständlich ist, können Sie beispielsweise eine einzelne Zeichenfolge akzeptieren, die eine durch Kommas getrennte Liste von Namen enthält (wie dies auch der namedtupleFall ist).

nneonneo
quelle
3
>> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v == my_var][0]
>> my_var_name 
'my_var'

local () - Gibt ein Wörterbuch zurück, das die lokalen Variablen des aktuellen Bereichs enthält. Durch Durchlaufen dieses Wörterbuchs können wir den Schlüssel überprüfen, dessen Wert der definierten Variablen entspricht. Wenn Sie nur den Schlüssel extrahieren, erhalten Sie den Text der Variablen im Zeichenfolgenformat.

von (nach ein wenig Änderungen) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python

Anshu Pandey
quelle
2

Ich denke, es ist so schwierig, dies in Python zu tun, weil Sie den Namen der Variablen, die Sie verwenden, nie kennen. In seinem Beispiel könnten Sie also Folgendes tun:

Anstatt:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}
Joe Camel
quelle
2

Nur eine andere Möglichkeit, dies basierend auf dem Inhalt der Eingabevariablen zu tun:

(Es gibt den Namen der ersten Variablen zurück, die mit der Eingabevariablen übereinstimmt, andernfalls keine. Sie können ihn ändern, um alle Variablennamen zu erhalten, die denselben Inhalt wie die Eingabevariable haben.)

def retrieve_name(x, Vars=vars()):
    for k in Vars:
        if type(x) == type(Vars[k]):
            if x is Vars[k]:
                return k
    return None
ses
quelle
2

Diese Funktion druckt den Variablennamen mit seinem Wert:

import inspect

def print_this(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
***Input & Function call:***
my_var = 10

print_this(my_var)

***Output**:*
my_var: 10
iDilip
quelle
2

Ich habe eine Methode, und obwohl sie nicht die effizienteste ist ... funktioniert sie! (und es beinhaltet keine ausgefallenen Module).

Grundsätzlich wird die ID Ihrer Variablen mit den IDs von globals () -Variablen verglichen und anschließend der Name der Übereinstimmung zurückgegeben.

def getVariableName(variable, globalVariables=globals().copy()):
    """ Get Variable Name as String by comparing its ID to globals() Variables' IDs

        args:
            variable(var): Variable to find name for (Obviously this variable has to exist)

        kwargs:
            globalVariables(dict): Copy of the globals() dict (Adding to Kwargs allows this function to work properly when imported from another .py)
    """
    for globalVariable in globalVariables:
        if id(variable) == id(globalVariables[globalVariable]): # If our Variable's ID matches this Global Variable's ID...
            return globalVariable # Return its name from the Globals() dict
Amr Sharaki
quelle
1

Wenn Sie Ihre Variablen im Auge behalten möchten, können Sie eine einfache Funktion schreiben, die die Variable beschriftet und ihren Wert und Typ zurückgibt. Angenommen, i_f = 3.01 und Sie runden es auf eine Ganzzahl namens i_n, um es in einem Code zu verwenden, und benötigen dann eine Zeichenfolge i_s, die in einen Bericht aufgenommen wird.

def whatis(string, x):
    print(string+' value=',repr(x),type(x))
    return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)

## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)

Dies wird bei jedem Aufruf zu Debugging-Zwecken im Fenster gedruckt und liefert auch eine Zeichenfolge für den schriftlichen Bericht. Der einzige Nachteil ist, dass Sie die Variable bei jedem Aufruf der Funktion zweimal eingeben müssen.

Ich bin ein Python-Neuling und fand diese sehr nützliche Möglichkeit, meine Bemühungen zu protokollieren, während ich programmiere und versuche, mit allen Objekten in Python umzugehen. Ein Fehler ist, dass whatis () fehlschlägt, wenn es eine Funktion aufruft, die außerhalb der Prozedur beschrieben wird, in der es verwendet wird. Zum Beispiel war int (i_f) nur deshalb ein gültiger Funktionsaufruf, weil die int- Funktion Python bekannt ist. Sie können whatis () mit int (i_f ** 2) aufrufen, aber wenn Sie aus irgendeinem seltsamen Grund eine Funktion namens int_squared definieren, muss diese in der Prozedur deklariert werden, in der whatis () verwendet wird.

Guy Vandegrift
quelle
1

Vielleicht könnte dies nützlich sein:

def Retriever(bar):
    return (list(globals().keys()))[list(map(lambda x: id(x), list(globals().values()))).index(id(bar))]

Die Funktion durchsucht die Liste der IDs von Werten aus dem globalen Bereich (der Namespace könnte bearbeitet werden), ermittelt den Index der gewünschten / erforderlichen Variablen oder Funktion anhand ihrer ID und gibt dann den Namen aus der Liste der globalen Namen zurück auf dem erworbenen Index.

Reitsch-Z2
quelle
1

Die folgende Methode gibt nicht den Namen der Variablen zurück. Mit dieser Methode können Sie jedoch problemlos einen Datenrahmen erstellen, wenn die Variable im globalen Bereich verfügbar ist.

class CustomDict(dict):
    def __add__(self, other):
        return CustomDict({**self, **other})

class GlobalBase(type):
    def __getattr__(cls, key):
        return CustomDict({key: globals()[key]})

    def __getitem__(cls, keys):
        return CustomDict({key: globals()[key] for key in keys})

class G(metaclass=GlobalBase):
    pass

x, y, z = 0, 1, 2

print('method 1:', G['x', 'y', 'z']) # Outcome: method 1: {'x': 0, 'y': 1, 'z': 2}
print('method 2:', G.x + G.y + G.z) # Outcome: method 2: {'x': 0, 'y': 1, 'z': 2}

A = [0, 1]
B = [1, 2]
pd.DataFrame(G.A + G.B) # It will return a data frame with A and B columns
user5828964
quelle
0

Für Konstanten können Sie eine Aufzählung verwenden, die das Abrufen des Namens unterstützt.

Dana
quelle
0

Ich versuche, einen Namen von Inspect-Einheimischen zu erhalten, aber es kann var wie a [1], b.val nicht verarbeiten. Danach kam mir eine neue Idee - hol den Variablennamen aus dem Code und versuche es erfolgreich! Code wie unten:

#direct get from called function code
def retrieve_name_ex(var):
    stacks = inspect.stack()
    try:
        func = stacks[0].function
        code = stacks[1].code_context[0]
        s = code.index(func)
        s = code.index("(", s + len(func)) + 1
        e = code.index(")", s)
        return code[s:e].strip()
    except:
        return ""
Ryan
quelle
0

Sie können Folgendes versuchen, um den Namen einer von Ihnen definierten Funktion abzurufen (funktioniert jedoch nicht für integrierte Funktionen):

import re
def retrieve_name(func):
    return re.match("<function\s+(\w+)\s+at.*", str(func)).group(1)

def foo(x):
    return x**2

print(retrieve_name(foo))
# foo
Sandipan Dey
quelle