LoginSignup
0
0

More than 1 year has passed since last update.

関数から参照されているグローバル変数の名前を取得する

Last updated at Posted at 2021-03-05

CPythonの関数が参照している名前(ローカル変数と自由変数を除く)は、その関数のコードオブジェクトのco_namesに保存されていることは以前書きましたco_namesは、グローバル変数名だけでなく、例えば次の例ではyも含んでいます。

def foo():
    return x.y

foo.__code__.co_names # => ('x', 'y')

ではグローバル変数の名前だけを取り出したい場合はどうするかについてです。
結論から言うと、dis標準ライブラリで、その関数をディスアセンブリし、LOAD_GLOABLインストラクションの引数を取り出す、という方法があるようです。 

import dis

def global_names(func):

    insts = list(dis.get_instructions(func.__code__))

    names = []
    for inst in insts:
        if inst.opname == "LOAD_GLOBAL" and inst.argval not in names:
            names.append(inst.argval)

    return tuple(names)

ただこの方法だと、ネストした関数内で参照されているグローバル変数名は取得できないようです。

def foo():
    def bar():
        return x
    return bar()

global_names(foo)  # => ()

参考: https://stackoverflow.com/questions/60639406/how-to-extract-names-of-global-variables-referenced-in-a-function

2021/7/11 追記

ジェネレータ表記やネストした関数で参照されているグローバル変数名も取得するには、関数のコードオブジェクトのco_constsの要素でコードオブジェクトである要素を再びディスアセンブルし、LOAD_GLOBALのインストラクションの引数を取り出せばよいようです。

import dis
from types import CodeType

def global_names(func):
    return tuple(_extract_globals(func.__code__))

def _extract_globals(codeobj):

    insts = list(dis.get_instructions(codeobj))

    names = []
    for inst in insts:
        if inst.opname == "LOAD_GLOBAL" and inst.argval not in names:
            names.append(inst.argval)

    # Extract globals in generators and nested functions
    for co in codeobj.co_consts:
        if isinstance(co, CodeType):
            names.extend(_extract_globals(co))

    return names
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0