Псевдоним функции в Python

Я хочу найти имя функции, как она была вызвана... т.е. имя переменной, вызвавшей функцию. Использование базовых рецептов, то есть с __name__, func_name, или проверка базового стека у меня не работает. Например

def somefunc():
    print "My name is: %s" % inspect.stack()[1][3]

a = somefunc
a()
# would output: out: "My name is: somefunc"
# whereas I want it to output: "My name is: a"

Моя интуиция говорит, что я могу это сделать, но я не могу понять. Есть гуру питона?


person Pykler    schedule 18.07.2012    source источник
comment
Я сомневаюсь, что это возможно. Какая-то конкретная причина, по которой вы хотите это сделать?   -  person mgilson    schedule 18.07.2012
comment
Обычно, когда люди спрашивают, как сделать что-то подобное, они делают что-то действительно ужасное. Можете ли вы уточнить, для какой цели это будет служить?   -  person cdhowie    schedule 18.07.2012
comment
Вы можете изменить название части, но я не понимаю, почему вы хотите это сделать. Кроме того, посмотрите на документы декоратора.   -  person sean    schedule 18.07.2012
comment
func_name определяется при создании функции. a.func_name это somefunc.   -  person Michael Hoffman    schedule 18.07.2012
comment
Это невозможно. Функция имеет одно и только одно каноническое имя, имя, используемое для ее создания (как указано в операторе def и сохранено в ее атрибутах func_name и __name__). У вас может быть любое количество других идентификаторов, указывающих на него, но нет способа узнать изнутри функции, которая используется для его вызова.   -  person kindall    schedule 18.07.2012
comment
что касается вопросов о его полезности, это просто что-то крутое и демонстрирующее, насколько динамичен python. Выбранный ответ показывает, как можно вызвать вызывающий код, проанализировать его и дополнительно исследовать ... разве Python не крут! То, для чего я его использую, — это попытка очень запутанного приложения Hello World :)   -  person Pykler    schedule 19.07.2012


Ответы (2)


Вероятно, невозможно сделать это на 100% правильно, но вы можете попробовать следующее:

import inspect
import parser

# this flatten function is by mike c fletcher
def flatten(l, ltypes=(list, tuple)):
    ltype = type(l)
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        i += 1
    return ltype(l)

# function we're interested in
def a():
    current_func = eval(inspect.stack()[0][3])
    last_frame = inspect.stack()[1]
    calling_code = last_frame[4][0]
    syntax_tree = parser.expr(calling_code)
    syntax_tree_tuple = parser.st2tuple(syntax_tree)
    flat_syntax_tree_tuple = flatten(syntax_tree_tuple)
    list_of_strings = filter(lambda s: type(s)==str,flat_syntax_tree_tuple)
    list_of_valid_strings = []
    for string in list_of_strings:
        try:
            st = parser.expr(string)
            list_of_valid_strings.append(string)
        except:
            pass
    list_of_candidates = filter(lambda s: eval(s)==current_func, list_of_valid_strings)
    print list_of_candidates

# other function
def c():
    pass

a()
b=a
a(),b(),c()
a(),c()
c(),b()

Это напечатает:

['a']
['a', 'b']
['a', 'b']
['a']
['b']

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

person Tar    schedule 18.07.2012
comment
Мне это нравится, это суть того, что я пытался написать ... как вы сказали, это сложно. И я действительно не могу присвоить возвращаемое значение, поэтому я могу использовать его для функции a(), поскольку синтаксический анализатор выдает синтаксическую ошибку. - person Pykler; 19.07.2012
comment
Чтобы использовать возвращаемое значение, я сделал list_of_candidates глобальным. Хотя следующей задачей было бы более простое решение, ваш ответ работает для того, что я хотел сделать! Спасибо! - person Pykler; 19.07.2012

Проблема здесь в косвенности. Вероятно, вы могли бы сделать что-то сложное, например, проверить стек, получить код модуля, вызвавшего функцию, проанализировать номер строки из стека, чтобы найти метку, используемую для вызова функции в локальном контексте, а затем использовать это, но это в любом случае не обязательно даст вам то, что вы хотите. Рассмотреть возможность:

def func(x):
    print get_label_function_called_with()

def func_wrapper(func_in_func_wrapper):
    return func_in_func_wrapper

func_label = func
func_from_func_wrapper = func_wrapper(func_label)
func_from_func_wrapper()

Должно ли это печатать func, func_in_func_wrapper, func_label или func_from_func_wrapper? Сначала это может показаться очевидным ответом, но, учитывая, что вы никогда не знаете, какой вид косвенности происходит внутри кода, который вы вызываете, вы действительно не можете знать наверняка.

person Silas Ray    schedule 18.07.2012
comment
это сложная ситуация, в моем случае меня не интересуют обертки, но другие могут быть. - person Pykler; 19.07.2012
comment
Дело в том, что нет простого или даже реально осуществимого способа сделать это. - person Silas Ray; 19.07.2012
comment
Может быть, это не быстро/осуществимо, но мой вопрос касался того, возможно ли это... и я уверен, что есть способ еще более надежный, чем выбранный ответ в python, поскольку python действительно динамичен. - person Pykler; 19.07.2012