変数の名前を文字列として取得する


173

このスレッドでは、関数名をPythonで文字列として取得する方法について説明し ます。関数名を文字列として取得する方法は?

変数に対して同じことをするにはどうすればよいですか?関数とは対照的に、Python変数には__name__属性がありません。

つまり、次のような変数があるとします。

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

私は、例えば、機能/属性を探していますretrieve_name()するために、このリストからパンダでDATAFRAME作成カラム名は、実際の辞書の名前で与えられました:

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

回答:


19

python-varnameパッケージを使用すると、変数の名前を簡単に取得できます

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

あなたの場合、あなたは次のことができます:

from varname import Wrapper

foo = Wrapper(dict())

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

または、変数名を直接取得することもできます。

from varname import nameof

foo = dict()

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

私はこのパッケージの作者です。ご不明な点がある場合や、Githubで問題を送信できる場合はお知らせください。


2
はい、やっとできました!以下を使用してインストールされますpip3 install python-varname==0.1.5。使用してインポートfrom varname import nameof
enter_display_name_here

どういうわけか、この関数はループで機能しませんtest = {} print(varname.nameof(test)) for i in [0]: print(varname.nameof(test))。最初の出力testは、ループ内の出力は上がるVarnameRetrievingError: Callee's node cannot be detected.
Tillus

1
@Tillus Fixed atv0.2.0
Panwen Wang

107

正規名を持つPythonの唯一のオブジェクトはモジュール、関数、およびクラスです。もちろん、関数またはクラスが定義された後、またはモジュールがインポートされた後、この正規名がすべてのネームスペースで何らかの意味を持つという保証はありません。これらの名前は、オブジェクトの作成後に変更することもできるため、必ずしも特に信頼できるとは限りません。

名前付きオブジェクトのツリーを再帰的にウォークしないと、実行したいことはできません。名前は、オブジェクトへの一方向の参照です。一般的または園芸品種のPythonオブジェクトには、その名前への参照が含まれていません。すべての整数、すべての辞書、すべてのリスト、すべてのブール値が、それを参照する名前を表す文字列のリストを維持する必要があると想像してみてください。これは、プログラマにとってほとんどメリットのない実装の悪夢になります。


7
ありがとう。しかし、なぜPythonは関数に対してこれを行うのですか?(つまり、Pythonオブジェクトの1つのタイプ)
Amelio Vazquez-Reina

1
@kindall:モジュールを忘れないでください:モジュールにも正規名があります。また、__name__常に変更できることを忘れないでください。つまり、「標準」名は、オブジェクトを取得するために使用できる名前ではない可能性があります(たとえば、クラスを動的に作成する場合)。
nneonneo 2013

8
@ scohe001が示したように、「それは単に不可能です」という文はFalse です。Pythonの変数名データベースは、他のリレーショナルDBと同じように、常に「逆」で関連オブジェクトを検索し、特定の変数に対して最初に見つかった、または有効な変数名のセット全体を返すことができます。
ホブ2013年

3
@hobsあなたは技術的に正しいです...最高の種類の正しい。ただし、実際には、オブジェクトには非常に多くの潜在的な名前があるため、取得しようとする価値よりも問題が多くなります。
キンダル2016

1
@kindall「価値がある」しきい値がO(1)であれば、私はあなたが正しいと思います。scohe001のループはO(N)になります。
ホブ2016

82

変数の値が名前をポイントしない場合でも、割り当てられたすべての変数とその値のリストにアクセスできるため、var名を探すためにそこをループすることを提案したのはたった1人だけです。

誰かがあなたがスタックを歩いて見つけることがすべての人の地元の人やグローバルをチェックする必要がある場合がありますことをその答えに言及したfooが、あればfoo、あなたがこの呼んでいるスコープに割り当てられているretrieve_name機能を、あなたが使用できるinspectcurrent frameそれらのローカル変数のすべてのあなたを得るために。

私の説明は少し言葉が多すぎるかもしれません(多分私は "foo"の少ない単語を使用すべきだったかもしれません)が、コードでそれがどのように見えるかです(同じ値に複数の変数が割り当てられている場合、これらの変数名を両方とも取得します):

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)

この関数を別の関数から呼び出す場合は、次のようになります。

def foo(bar):
    return retrieve_name(bar)

foo(baz)

そして、あなたはのbaz代わりにbar、スコープをさらに戻す必要があるだけです。これは.f_backcaller_local_vars初期化でエクストラを追加することで実行できます。

こちらの例をご覧ください:ideone


1
@theodox私は完全に同意します。これはおそらくimport hooksironpythonjython
、、、

5
これは機能しません。アトミック変数には、属性としての名前はありません。だから、あなたが持っている場合はa, b = 2, 2retrieve_name(a)retrieve_name(b)なりますリターンの両方['a', 'b']または['b', 'a']
トマス・

1
@tomas実際、これはcPythonの最適化の実装の詳細であり、255未満の整数は基本的にシングルトンなので、これらの値が割り当てられた変数trueis比較のために効果的に返されます
Toote

1
渡された変数の名前を取得するためのこのmodはありますか?たとえばdef foo(bar): retrieve_name(bar)、常にバーを返しますが、代わりに)foo(baz)を返したい場合はどうなりますか?bazbar
SumNeuron

1
@SumNeuronは、callers_local_vars1つのスコープを前に戻すために割り当てる行を変更するだけでよいので、呼び出しているもののスコープを確認できますfoo。行を次のように変更しますinspect.currentframe().f_back.f_back.f_locals.items()(追加のに注意してくださいf_back)。
scohe001

37

Python 3.8では、f-stringデバッグ機能を簡単に使用できます。

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

いいね!そして、あなたは代わりにすることができますオブジェクトのプロパティの名前を取得したいだけの場合f'{foo=}'.split('=')[0].split('.')[-1]
ミカエル・V.

30

python3では、この関数はスタックの最も外側の名前を取得します。

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]

コードのどこでも便利です。逆スタックを走査して、最初の一致を探します。


良くやった!やってみretrieve_name(SomeClass.some_attribute)てもうまくいきません。その上でさらに助けてもらえますか?
Nam G VU

これはブール変数と格闘します。結局stop_on_error
SumNeuron

26

これが可能だとは思いません。次の例を検討してください。

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

aそしてbポイント同じオブジェクトが、オブジェクト変数はそれを指すかを知ることはできません。


2
参照カウントを拡張することで、この関係を双方向にすることができます。この答え(および他のいくつか)は実装も提供します。
Shayaan

17
def name(**variables):
    return [x for x in variables]

これは次のように使用されます:

name(variable=variable)

12
これは、目的の変数の名前を返すのではなく、「変数」を返します。例えば-使用してname(variable=variable)意志の出力を['variable']、そして使用name(variable=another_variable)されますない出力を['another_variable']ではなく['variable']
DalyaG

1
実際には期待どおりに動作します。両方の«変数»を自分の変数に置き換えるだけです。最初の変数の名前の文字列を含む1要素のリストを返します。例:>>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']`
Alguem Meugla

8

これが1つのアプローチです。これは非常に壊れやすいので、重要なものにはお勧めしません。しかし、それは可能です。

inspectモジュールを使用して、それを呼び出したソースコードを見つける関数を作成します。次に、ソースコードを解析して、取得する変数名を識別できます。たとえば、次の関数autodictは、変数のリストを受け取り、変数名をその値にマッピングする辞書を返します。例えば:

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

与えるでしょう:

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

locals()またはglobals()、後者の方法ではどの変数が必要な変数であるかがわからないため、ソースコード自体の検査の方が検索よりも優れています。

とにかく、ここにコードがあります:

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

アクションは、inspect.getouterframesを呼び出したコード内の文字列を返すの行で発生しますautodict

この種の魔法の明らかな欠点は、ソースコードの構造について想定していることです。そしてもちろん、それがインタープリター内で実行された場合はまったく機能しません。


こんにちは、私は3つだけ質問があります。1.なぜ「1」なのか?2.なぜ「4」なのか?3.なぜ「0」なのか :)
Piotr Wasilewicz

7

私はこの種の魔法をロバストに行うためにパッケージソーサリーを書きました。あなたは書ける:

from sorcery import dict_of

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

そしてそれをデータフレームコンストラクタに渡します。これは次と同等です。

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

6
>>> locals()['foo']
{}
>>> globals()['foo']
{}

独自の関数を記述したい場合は、ローカルで定義された変数をチェックしてから、グローバルをチェックするようにできます。何も見つからない場合は、id()を比較して、変数がメモリ内の同じ場所を指しているかどうかを確認できます。

変数がクラス内にある場合は、classNameを使用できます。dict .keys()またはvars(self)を使用して、変数が定義されているかどうかを確認します。


名前が発信者のフレームにある場合はどうなりますか?その後、スタックを歩くとみんなのを確認するような愚かなものに持っているだろうlocalsglobals...と何が実際に存在しない場合は、名前間違っを取得危険性があります。それは、実際に有用な利益を得るための膨大な作業です。
nneonneo 2013

質問全体はばかげています...しかし、それが彼がやりたかったことなら、それは可能です。存在を確認する限り、Globals()。setdefault(var、<new object of type(var))を使用して、何もないときに何かを作成できます。私は彼がそれを何のために望んでいるのか正確にはわかりませんが、おそらく変数がスコープによって保持されていることを学んで、彼はより良いものを理解することができます。
blakev 2013

3

Pythonでは、defおよびclassキーワードは、特定の名前を、それらが定義するオブジェクト(関数またはクラス)にバインドします。同様に、モジュールには、ファイルシステムで特定の何かと呼ばれることにより名前が付けられます。3つのケースすべてで、問題のオブジェクトに「標準」名を割り当てる明白な方法があります。

ただし、他の種類のオブジェクトの場合、そのような正規名は単に存在しない可能性があります。たとえば、リストの要素について考えます。リスト内の要素は個別に名前が付けられているわけではなく、プログラムでそれらを参照する唯一の方法は、包含リストのリストインデックスを使用することです。そのようなオブジェクトのリストが関数に渡された場合、意味のある識別子を値に割り当てることができない可能性があります。

Pythonは、割り当ての左側の名前を割り当てられたオブジェクトに保存しません。

  1. 複数の競合するオブジェクト間でどの名前が「正規」であったかを把握する必要があります。
  2. 明示的な変数名に割り当てられていないオブジェクトには意味がありません。
  3. それは非常に非効率的です
  4. 文字通り、現存する他の言語はそれを行いません。

したがって、たとえば、を使用して定義された関数は、特定の関数名ではなくlambda、常に「名前」を持ち<lambda>ます。

最善の方法は、発信者に名前の(オプションの)リストを渡すように要求することです。を入力するの'...','...'が面倒な場合は、たとえば、名前のコンマ区切りのリストを含む単一の文字列を受け入れることができます(そうするなどnamedtuple)。


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

locals()-現在のスコープのローカル変数を含む辞書を返します。このディクショナリを反復処理することにより、定義された変数と等しい値を持つキーを確認できます。キーを抽出するだけで、文字列形式の変数のテキストが得られます。

から(少し変更後) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python


2

使用している変数の名前がわからないという単純な事実のため、Pythonでこれを行うのは非常に難しいと思います。したがって、彼の例では、次のことができます。

の代わりに:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}

2

入力変数の内容に基づいてこれを行う別の方法:

(入力変数に一致する最初の変数の名前を返します。それ以外の場合はNoneを返します。入力変数と同じ内容のすべての変数名を取得するように変更できます)

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

2

この関数は、変数名とその値を出力します。

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

2

私には方法があり、最も効率的ではありませんが ... うまくいきます!(そしてそれはどんな凝ったモジュールも含みません)。

基本的には、変数のIDglobals()変数のIDと比較してから、一致の名前を返します。

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

1

変数を追跡することを目的とする場合は、変数にラベルを付けてその値と型を返す簡単な関数を作成できます。たとえば、i_f = 3.01で、コードで使用するためにi_nと呼ばれる整数に丸め、レポートに入る文字列i_sが必要だとします。

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)

これは、デバッグのために各呼び出しでウィンドウに出力され、書き込まれたレポートの文字列も生成します。唯一の欠点は、関数を呼び出すたびに変数を2回入力する必要があることです。

私はPythonの初心者です。プログラムを作成し、Pythonのすべてのオブジェクトに対処しようとするときに、この非常に便利な方法で自分の取り組みを記録できます。1つの欠点は、whatis()が、それが使用されるプロシージャの外部で記述された関数を呼び出すと失敗することです。たとえば、int(i_f)は、int関数がPythonに認識されているためにのみ有効な関数呼び出しでした。int(i_f ** 2)を使用してwhatis()を呼び出すこともできますが、何らかの奇妙な理由でint_squaredという関数を定義する場合は、whatis()を使用するプロシージャ内で宣言する必要があります。


1

多分これは役に立つかもしれません:

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

この関数は、グローバルスコープから値のIDのリストを調べ(名前空間を編集できます)、そのIDに基づいて必要な/必要なvarまたは関数のインデックスを見つけ、グローバル名のリストに基づいて名前を返します取得したインデックス。


1

次のメソッドは変数の名前を返しませんが、このメソッドを使用すると、変数がグローバルスコープで使用可能な場合にデータフレームを簡単に作成できます。

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

0

定数の場合は、名前の取得をサポートする列挙型を使用できます。


0

ローカルの検査から名前を取得しようとしましたが、a [1]、b.valなどのvarを処理できません。その後、私は新しいアイデアを得ました---コードからvar名を取得し、それを成功させます!以下のようなコード:

#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 ""

0

次のことを試して、定義した関数の名前を取得できます(ただし、組み込み関数では機能しません)。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.