1. ホーム
  2. パイソン

[解決済み】変数名を文字列で取得する方法

2022-04-18 06:26:59

質問

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

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

つまり、次のような変数があった場合。

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

私は関数/属性を探しています、例えば retrieve_name() にするために このリストからPandasで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] 

解決方法は?

TL;DR

を使用します。 Wrapper ヘルパーは python-varname :

from varname.helpers import Wrapper

foo = Wrapper(dict())

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

リスト内包の部分は、こうすればいい。

n_jobs = Wrapper(<original_value>) 
users = Wrapper(<original_value>) 
queues = Wrapper(<original_value>) 
priorities = Wrapper(<original_value>) 

list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# REMEMBER that you have to access the <original_value> by d.value

の作者です。 python-varname パッケージで提供されます。ご不明な点がありましたら、ご連絡ください。

長い答え

それは可能なのか?

YesとNoです。

実行時に変数名を取得するため、前のフレームにアクセスして変数名を取得できるような関数が必要です。そのために Wrapper そこで その関数では、実行時に、前のフレームのソースコード/ASTノードを解析して、正確な変数名を取得しています。

しかし、前のフレームにあるソースコード/ASTノードが常に利用できるとは限りませんし、他の環境によって変更されている可能性もあります(例えば pytest 's assert ステートメント)。簡単な例としては、コードが exec() . それでもバイトコードから何らかの情報を取り出すことはできますが、手間がかかりすぎるし、エラーが発生しやすくなります。

どうすればいい?

まず、変数がどのフレームに与えられているかを特定する必要があります。単純に直前のフレームとは限りません。例えば、関数のラッパーが別にある場合もあります。

from varname import varname

def func():
  return varname()

def wrapped():
  return func()

x = wrapped()

上記の例では、フレームをスキップして wrapped 正しいフレームに到達するために x = wrapped() の位置を特定できるように x . 引数 frameignorevarname を使えば、これらの中間フレームのいくつかをスキップすることができます。詳しくはREADMEファイルやパッケージのAPIドキュメントをご覧ください。

次に、ASTノードを解析して、変数に値が割り当てられている(関数呼び出し)場所を特定する必要があります。単純な代入とは限りません。時には、例えば、複雑なASTノードが存在する可能性もあります。 x = [wrapped()] . ASTツリーを走査して、正しい割り当てを特定する必要がある。

信頼性は?

割り当てノードを特定すれば、それは信頼できるものです。

varname はすべて executing パッケージでノードを探します。実行時に検出されるノードは正しいものであることが保証されます ( これ ).

pytest, ipython, macropy, birdseye, reticulate with Rなど、他のASTマジックが適用される環境でも部分的に動作します。executingもvarnameもそれらの環境では100%動作しません。

パッケージが必要なのか?

さて、イエスとノー、もう一度。

シナリオが単純であれば、@juan Isaza や @scohe001 が提供しているコードで、直前のフレームで変数が定義されていて、AST ノードが単純な代入であるようなケースでは、おそらく十分に動作すると思われます。1フレーム前に戻って、そこで情報を取得すればいいだけです。

しかし、シナリオが複雑になったり、異なるアプリケーションシナリオを採用する必要がある場合、おそらく以下のようなパッケージが必要になります。 python-varname を、処理することができます。これらのシナリオは、に含まれるかもしれません。

  1. ソースコードが利用できない場合やASTノードにアクセスできない場合に、より親切なメッセージを表示します。
  2. 中間フレームをスキップする(他の中間フレームで関数をラップしたり呼び出したりすることができる)
  3. は、組み込み関数やライブラリからの呼び出しを自動的に無視します。例えば x = str(func())
  4. 代入の左側にある複数の変数名を取得する
  5. その他

はどうでしょうか。 f-string ?

Aivar Paalbergが提供した答えのように。確かに高速で信頼性が高いです。しかし、これは実行時ではなく、つまり、それが foo を出力する前に、その名前を出力してください。しかし varname を使えば、その変数が来ることを知らなくてもいいのです。

from varname import varname

def func():
  return varname()

# In external uses
x = func() # 'x'
y = func() # 'y'

最後に

python-varname は、代入から変数名を検出できるだけでなく。

  • 変数名を直接取得するには nameof
  • 次の直属の属性名を検出します。 will
  • 関数に渡された引数名/ソースを取得するには、次のようにします。 argname

詳しくはドキュメントをご覧ください。

しかし、最後に言いたいのは、そのことです。 なるべく使わないようにしましょう。

なぜなら、クライアントコードがソースノードが利用可能な環境、あるいはASTノードにアクセス可能な環境で実行されるかどうかは、確認できないからです。そしてもちろん、ソースコードを解析し、環境を特定し、ASTノードを取得し、必要なときにそれを評価するためにはリソースがかかります。