web-dev-qa-db-ja.com

関数に渡された変数の元の変数名を取得する方法

関数に渡された変数の元の変数名を取得することは可能ですか?例えば。

foobar = "foo"

def func(var):
    print var.origname

そのため:

func(foobar)

戻り値:

>>foobar

編集:

私がやろうとしたことは、次のような関数を作成することだけでした。

def log(soup):
    f = open(varname+'.html', 'w')
    print >>f, soup.prettify()
    f.close()

..そして、関数に、渡された変数の名前からファイル名を生成させます。

それが不可能な場合は、毎回変数と変数の名前を文字列として渡す必要があると思います。

46
Acorn

できません。関数に渡される前に評価されます。できることは、それを文字列として渡すことだけです。

17
Max Shawabkeh

編集:明確にするために、これを使用することはお勧めしませんAT ALL、壊れる、混乱する、とにかく役に立たない、しかし、それは娯楽/教育目的のために実行可能です。

inspectモジュールでハックできますが、お勧めしませんが、実行できます...

import inspect

def foo(a, f, b):
    frame = inspect.currentframe()
    frame = inspect.getouterframes(frame)[1]
    string = inspect.getframeinfo(frame[0]).code_context[0].strip()
    args = string[string.find('(') + 1:-1].split(',')

    names = []
    for i in args:
        if i.find('=') != -1:
            names.append(i.split('=')[1].strip())

        else:
            names.append(i)

    print names

def main():
    e = 1
    c = 2
    foo(e, 1000, b = c)

main()

出力:

['e', '1000', 'c']
52
Ivo Wetzel

Michael Mrozekの答えに追加するには、次のようにして、完全なコードに対して正確なパラメーターを抽出できます。

import re
import traceback

def func(var):
    stack = traceback.extract_stack()
    filename, lineno, function_name, code = stack[-2]
    vars_name = re.compile(r'\((.*?)\).*$').search(code).groups()[0]
    print vars_name
    return

foobar = "foo"

func(foobar)

# PRINTS: foobar
10
propjk007

Ivoが私をinspectに打ち負かしたようですが、ここに別の実装があります:

import inspect

def varName(var):
    lcls = inspect.stack()[2][0].f_locals
    for name in lcls:
        if id(var) == id(lcls[name]):
            return name
    return None

def foo(x=None):
    lcl='not me'
    return varName(x)

def bar():
    lcl = 'hi'
    return foo(lcl)

bar()
# 'lcl'

もちろん、だまされる可能性があります。

def baz():
    lcl = 'hi'
    x='hi'
    return foo(lcl)

baz()
# 'x'

道徳:それをしないでください。

10
outis

呼び出しコードがどのように見えるかがわかっている場合に試すことができるもう1つの方法は、 traceback を使用することです。

_def func(var):
    stack = traceback.extract_stack()
    filename, lineno, function_name, code = stack[-2]
_

codeには、funcを呼び出すために使用されたコード行が含まれます(例では、文字列func(foobar)になります)。あなたはそれを解析して議論を引き出すことができます

5
Michael Mrozek

キーと値のペアの関係が必要な場合は、辞書を使用する方がよいでしょう。

...または、コードから自動ドキュメントを作成しようとしている場合、おそらくDoxygen( http://www.doxygen.nl/ )のようなものがあなたのために仕事をするでしょうか?

3
Nick Cartwright

関数呼び出しの場合の@Ivo Wetzelの回答は、次のように1行で作成されます。

e = 1 + 7
c = 3
foo(e, 100, b=c)

その関数呼び出しが1行にない場合:

e = 1 + 7
c = 3
foo(e,
    1000,
    b = c)

以下のコードは機能します:

import inspect, ast

def foo(a, f, b):
    frame = inspect.currentframe()
    frame = inspect.getouterframes(frame)[1]
    string = inspect.findsource(frame[0])[0]

    nodes = ast.parse(''.join(string))

    i_expr = -1
    for (i, node) in enumerate(nodes.body):
        if hasattr(node, 'value') and isinstance(node.value, ast.Call)
            and hasattr(node.value.func, 'id') and node.value.func.id == 'foo'  # Here goes name of the function:
            i_expr = i
            break

    i_expr_next = min(i_expr + 1, len(nodes.body)-1)  
    lineno_start = nodes.body[i_expr].lineno
    lineno_end = nodes.body[i_expr_next].lineno if i_expr_next != i_expr else len(string)

    str_func_call = ''.join([i.strip() for i in string[lineno_start - 1: lineno_end]])
    params = str_func_call[str_func_call.find('(') + 1:-1].split(',')

    print(params)

あなたは得るでしょう:

[u'e', u'1000', u'b = c']

しかし、それでも壊れる可能性があります。

1
Aeon

ソースファイルを使用せずに(つまり、Jupyter Notebookから) @ Matt Oates answer answerのように呼び出し元のパラメーターを取得する場合、このコード( @ Aeon answer から結合) (少なくともいくつかの単純なケースでは)トリックを行います:

def get_caller_params():
    # get the frame object for this function call
    thisframe = inspect.currentframe()

    # get the parent calling frames details
    frames = inspect.getouterframes(thisframe)

    # frame 0 is the frame of this function
    # frame 1 is the frame of the caller function (the one we want to inspect)
    # frame 2 is the frame of the code that calls the caller
    caller_function_name = frames[1][3]
    code_that_calls_caller = inspect.findsource(frames[2][0])[0]

    # parse code to get nodes of abstract syntact tree of the call
    nodes = ast.parse(''.join(code_that_calls_caller))

    # find the node that calls the function
    i_expr = -1
    for (i, node) in enumerate(nodes.body):
        if _node_is_our_function_call(node, caller_function_name):
            i_expr = i
            break

    # line with the call start
    idx_start = nodes.body[i_expr].lineno - 1

    # line with the end of the call
    if i_expr < len(nodes.body) - 1:
        # next expression marks the end of the call
        idx_end = nodes.body[i_expr + 1].lineno - 1
    else:
        # end of the source marks the end of the call
        idx_end = len(code_that_calls_caller)

    call_lines = code_that_calls_caller[idx_start:idx_end]
    str_func_call = ''.join([line.strip() for line in call_lines])
    str_call_params = str_func_call[str_func_call.find('(') + 1:-1]
    params = [p.strip() for p in str_call_params.split(',')]

    return params


def _node_is_our_function_call(node, our_function_name):
    node_is_call = hasattr(node, 'value') and isinstance(node.value, ast.Call)
    if not node_is_call:
        return False

    function_name_correct = hasattr(node.value.func, 'id') and node.value.func.id == our_function_name
    return function_name_correct

その後、次のように実行できます。

def test(*par_values):
    par_names = get_caller_params()
    for name, val in Zip(par_names, par_values):
        print(name, val)


a = 1
b = 2
string = 'text'
test(a, b,
  string
)

必要な出力を取得するには:

a 1
b 2
string text
0
Lukas Vrabel

変数(コンテンツ)を渡す代わりに、同じコンテンツを持つ複数の変数を使用できるため、文字列でその名前を渡し、呼び出し元スタックのローカルディクショナリから変数のコンテンツを取得する方が安全(かつ簡単)です。フレーム。 :

def displayvar(name):
    import sys
    return name+" = "+repr(sys._getframe(1).f_locals[name])
0
Camion