Есть ли способ получить имена переданных аргументов функции в python?

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

определение функции:

def show(x):
  print(x)

использование:

a = 10
show(a)

это печатает 10. Но мне нравится печатать "a = 10". Возможно ли это в python?

Ответ 1

Нет, вы не можете знать, какое имя принадлежит локальной переменной, используемой для передачи значения вашей функции.

В любом случае это невозможно. Каким будет имя переменной в следующем примере?

arguments = ('a', 1, 10)
somefunction(*(arguments[:2] + [10]))

Здесь мы передаем 3 аргумента, два взяты из кортежа, который мы определили ранее, и одно буквальное значение, а все три переданы с использованием синтаксиса списка аргументов переменных.

Ответ 2

Не совсем так. Однако вы можете добиться чего-то подобного:

def show(**kwargs):
  print(', '.join('%s=%s' % kv for kv in kwargs.items()))

show(a=20)

Ответ 3

Мне нравится ответ на этот вопрос, который содержится в FAQ по программированию на Python, цитируя Фредрика Лунда:

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

.... и не удивляйтесь, если вы обнаружите, что его знают многие имена или вообще не имеют имени!

Ответ 4

Я предупреждаю, что следующее решение получит несколько критических замечаний

def show(*x):
    for el in x:
        fl = None
        for gname,gobj in globals().iteritems():
            if el==gobj:
                print '%s == %r' % (gname,el)
                fl = True
        if not fl:
            print 'There is no identifier assigned to %r in the global namespace' % el

un = 1
y = 'a'
a = 12
b = c = 45
arguments = ('a', 1, 10)
lolo = [45,'a',a,'heat']

print '============================================'
show(12)
show(a)
print '============================================'
show(45)
print
show(b)
print '============================================'
show(arguments)
print
show(('a', 1, 10))
print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
show(*arguments)
print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
show(*(arguments[1:3] + (b,)))

результат

============================================
a == 12
a == 12
============================================
c == 45
b == 45

c == 45
b == 45
============================================
arguments == ('a', 1, 10)

arguments == ('a', 1, 10)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
y == 'a'
un == 1
There is no identifier assigned to 10 in the global namespace
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
un == 1
There is no identifier assigned to 10 in the global namespace
c == 45
b == 45

Ответ 5

Кажется, что это невозможно в Python, но на самом деле это возможно в С++.

#define show(x)   std::cout << #x << " = " << x << std::endl

Ответ 6

Новое решение Использование readline

Если вы находитесь в интерактивном сеансе, здесь крайне наивное решение, которое обычно работает:

def show(x):
    from readline import get_current_history_length, get_history_item
    print(get_history_item(get_current_history_length()).strip()[5:-1] + ' = ' + str(x))

Все, что он делает, это прочитать последний ввод строки в буфере интерактивного сеанса, удалить все ведущие или завершающие пробелы, а затем дать вам все, кроме первых пяти символов (надеюсь, show() и последнего символа (надеюсь, ))), таким образом оставляя вас с тем, что было передано.

Пример:

>>> a = 10
>>> show(a)
a = 10
>>> b = 10
>>> show(b)
b = 10
>>> show(10)
10 = 10
>>> show([10]*10)
[10]*10 = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
>>> show('Hello' + 'World'.rjust(10))
'Hello' + 'World'.rjust(10) = Hello     World

Если вы используете OS X, используя версию Python, которая поставляется вместе с ней, у вас нет readline по умолчанию, но вы можете установить его через pip. Если вы находитесь в Windows, readline для вас не существует... вы могли бы использовать pyreadline из pip, но я никогда не пробовал его, поэтому не могу сказать, является ли это приемлемым заменителем или нет.

Я оставляю этот код более пуленепробиваемым как упражнение для читателя. Следует рассмотреть, как заставить его обрабатывать такие вещи:

show(show(show(10)))
show(
10
)

Если вы хотите, чтобы такие вещи отображали имена переменных из script, вы можете изучить использование и получение исходного кода вызывающего фрейма. Но учитывая, что я не могу придумать, почему вы когда-либо хотели бы использовать show() в script или почему вы усложнили бы эту функцию, просто для того, чтобы обращаться с людьми, намеренно привинчивающимися к ней, как я сделал выше, я не собираюсь тратить мое время прямо сейчас выясняет это.

Исходное решение с использованием inspect

Здесь мое оригинальное решение, которое сложнее и имеет более вопиющий набор предостережений, но более переносимо, поскольку оно использует только inspect, а не readline, поэтому работает на всех платформах и находится ли вы в интерактивный сеанс или в script:

def show(x):

    from inspect import currentframe

    # Using inspect, figure out what the calling environment looked like by merging
    #   what was available from builtin, globals, and locals.
    # Do it in this order to emulate shadowing variables
    #   (locals shadow globals shadow builtins).

    callingFrame = currentframe().f_back
    callingEnv = callingFrame.f_builtins.copy()
    callingEnv.update(callingFrame.f_globals)
    callingEnv.update(callingFrame.f_locals)

    # Get the variables in the calling environment equal to what was passed in.
    possibleRoots = [item[0] for item in callingEnv.items() if item[1] == x]

    # If there are none, whatever you were given was more than just an identifier.
    if not possibleRoots:
        root = '<unnamed>'
    else:
        # If there is exactly one identifier equal to it,
        #   that probably the one you want.
        # This assumption could be wrong - you may have been given
        #   something more than just an identifier.
        if len(possibleRoots) == 1:
            root = str(possibleRoots[0])
        else:
            # More than one possibility? List them all.
            # Again, though, it could actually be unnamed.
            root = '<'
            for possibleRoot in possibleRoots[:-1]:
                root += str(possibleRoot) + ', '
            root += 'or ' + str(possibleRoots[-1]) + '>'

    print(root + ' = ' + str(x))

Здесь случай, когда он работает отлично (один из вопроса):

>>> a = 10
>>> show(a)
a = 10

Вот еще один забавный случай:

>>> show(quit)
quit = Use quit() or Ctrl-Z plus Return to exit

Теперь вы знаете, как эта функциональность была реализована в интерпретаторе Python - quit является встроенным идентификатором для str, в котором говорится, как правильно выйти.

Здесь несколько случаев, когда это меньше, чем вы могли бы хотеть, но... приемлемо?

>>> b = 10
>>> show(b)
<a, or b> = 10
>>> show(11)
<unnamed> = 11
>>> show([a])
<unnamed> = [10]

И вот случай, когда он печатает истинное утверждение, но определенно не то, что вы искали:

>>> show(10)
<a, or b> = 10