Как получить оригинальное имя переменной, переданной функции - PullRequest
42 голосов
/ 01 мая 2010

Можно ли получить исходное имя переменной, переданной функции? Э.Г.

foobar = "foo"

def func(var):
    print var.origname

Так что:

func(foobar)

Возвращает:

>>foobar

EDIT:

Все, что я пытался сделать, это создать такую ​​функцию:

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

.. и заставить функцию генерировать имя файла из имени переданной ему переменной.

Полагаю, если это невозможно, мне просто нужно каждый раз передавать переменную и имя переменной в виде строки.

Ответы [ 8 ]

50 голосов
/ 01 мая 2010

РЕДАКТИРОВАТЬ: Чтобы было ясно, я не рекомендую использовать это ВСЕ, оно сломается, это беспорядок, это не поможет вам в любом случае, но это выполнимо для развлечения / образования цели.

Вы можете взломать модуль 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']
16 голосов
/ 01 мая 2010

Вы не можете. Он оценивается перед передачей в функцию. Все, что вы можете сделать, это передать его в виде строки.

10 голосов
/ 07 октября 2014

Чтобы добавить к ответу Михаила Мрозека, вы можете извлечь точные параметры по сравнению с полным кодом:

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 голосов
/ 01 мая 2010

Похоже, Иво побил меня до 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'

Мораль: не делай этого.

5 голосов
/ 28 апреля 2012

Другой способ, которым вы можете попробовать, если вы знаете, как будет выглядеть код вызова, - это использовать traceback:

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

code будет содержать строку кода, которая использовалась для вызова func (в вашем примере это будет строка func(foobar)). Вы можете разобрать это, чтобы вытащить аргумент

3 голосов
/ 01 мая 2010

Если вы хотите, чтобы отношение «пара значений / ключ», возможно, было бы лучше использовать словарь?

... или если вы пытаетесь создать некоторую авто-документацию из своего кода, возможно, что-то вроде Doxygen (http://www.doxygen.nl/) может выполнить эту работу за вас?

1 голос
/ 18 июля 2016

@ Ivo Wetzel ответ работает в случае вызова функции в одну строку, как

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

В случае, если вызов функции находится не в одной строке, например:

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']

Но, тем не менее, это может сломаться.

0 голосов
/ 19 марта 2019

Поскольку у вас может быть несколько переменных с одинаковым содержимым, вместо передачи переменной (содержимого) может быть безопаснее (и будет проще) передать ее имя в строку и получить содержимое переменной из словаря местных кадр стека абонентов. :

def displayvar(name):
    import sys
    return name+" = "+repr(sys._getframe(1).f_locals[name])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...