Страницы

Поиск по вопросам

понедельник, 23 декабря 2019 г.

Имя переменной, переданной в функцию

#python #python_3x #python_internals


Можно ли как-то узнать имя переменной, которую передали в функцию:

def f(x):
    ...

y = 1
z = 2
f(y) # напеччатет y
f(z) # напеччатет z

    


Ответы

Ответ 1



Это можно сделать в CPython, но не рекомендуется -- в зависимости от вашей конкретной задачи, могут более удачные решения существовать, чем пытаться имя объекта из вызываемого окружения определить: >>> import inspect >>> def f(x): ... caller_locals = inspect.currentframe().f_back.f_locals ... print(*[name for name, value in caller_locals.items() if x is value]) >>> y = 1 >>> z = 2 >>> f(y) y >>> f(z) z В зависимости от того как REPL реализована, можно различить f(y) от f(z) вызовы, даже если y = z = 1: >>> import dis >>> import inspect >>> def f(x): ... caller_frame = inspect.currentframe().f_back ... dis.dis(caller_frame.f_code) ... >>> y = z = 1 >>> f(y) 1 0 LOAD_NAME 0 (f) 2 LOAD_NAME 1 (y) 4 CALL_FUNCTION 1 6 PRINT_EXPR 8 LOAD_CONST 0 (None) 10 RETURN_VALUE >>> f(z) 1 0 LOAD_NAME 0 (f) 2 LOAD_NAME 1 (z) 4 CALL_FUNCTION 1 6 PRINT_EXPR 8 LOAD_CONST 0 (None) 10 RETURN_VALUE >>> f(1) 1 0 LOAD_NAME 0 (f) 2 LOAD_CONST 0 (1) 4 CALL_FUNCTION 1 6 PRINT_EXPR 8 LOAD_CONST 1 (None) 10 RETURN_VALUE Имя переданной переменной присутствует в байт-коде вызывающего frame. Программно можно найти вызов функции (CALL_FUNCTION инструкция) и посмотреть какое именно имя передано (если вообще имя было использовано) из caller_frame.f_code.co_names списка. К примеру в CPython 2: >>> import inspect >>> import byteplay # $ pip install byteplay >>> def f(x): ... bc = byteplay.Code.from_code(inspect.currentframe().f_back.f_code) ... i = next(i for i, (opcode, _) in enumerate(bc.code) if opcode == byteplay.CALL_FUNCTION) ... print bc.code[i-1][1] ... >>> y = z = 1 >>> f(y) y >>> f(z) z >>> f(1) 1 Disclaimer: ответ для образовательных целей, не для использования в рабочем коде.

Ответ 2



можно грязным хаком :) def f(x, g=globals()): print([n for n in g if id(g[n]) == id(x)]) y = 1 z = 2 f(y) # напеччатет y f(z) # напеччатет z >>> ['y'] >>> ['z']

Ответ 3



Я как-то нашёл это решение на каком-то сайте заморском. Там говорили, что оно нестабильно, и это действительно было так. Но я более менее разобрался в нём и даже немного модифицировал, но до сих пор считаю это магией и не до конца понимаю как это работает. Единственное что, попробовал я сейчас сделать декораторы и запросить имя переменной через них. К сожалению, результат совсем не тот. И дело в том, что строка при вызове декораторов не показывает ничего, кроме названия самой функции в ней import traceback import re def get_name(__x, level=0): # 0 - global, 1 - local, может быть есть и 2... name = traceback.extract_stack()[level][3] name = re.sub(r'.*get_name\(', '', name) name = re.search(r'[^,|()]*', name).group(0) return name def foo(level=1): a = 2 var_1 = 4 print(get_name(a, level)) print(get_name(var_1, level)) def goo(): foo() def dec(): goo() a = 1 b = 3 var1 = 2 var2 = 4 print(get_name(a)) print(get_name(b)) print(get_name(var1)) print(get_name(var2)) dec() # goo() - после преобразования - goo goo() # foo() - после преобразования - foo foo() # а тут уже внутри переменные

Ответ 4



Еще можно так: print([name for name, value in locals().items() if value is x]) Правда, как и в примерах выше, при x = y = 1 вернет ['x', 'y']

Комментариев нет:

Отправить комментарий