Можно ли получить имя переданного параметра в Python?

В частности, я хочу быть в состоянии сделать это:

def a(name):
    print "the actual passed parameter is %s" %(???)

Что это решает ?

Поэтому, если мне нужно раздать монеты в сумках и корзинах, скажем, bag_list и basket_list, мне не нужно отправлять явный идентификатор «bag» или «basket»

Вот как я в настоящее время работаю над этим. В настоящее время я использую (глобальный) словарь для этой цели-

ID = {'bag': bag_list, 'basket': basket_list}

def a(name, id):
   dist_list = ID[id]

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

def a(name):
    name = ???get variable name as string magically???
    if name.startswith('bag'): 
         dist_list = ID['bag']
    else:
         dist_list = ID['basket']

На смоделированный вопрос дан исчерпывающий ответ в https://stackoverflow.com/a/9121991/1397945, но я ' я переиздаю это

  • на всякий случай произошли некоторые изменения и
  • возможно ли это сделать на других языках сценариев, таких как Perl или Tcl.
  • Более того, в случае, если два способа моделирования различны или могут подходить по-разному.

Спасибо.

0
Shyam Sunder 31 Янв 2013 в 10:03

4 ответа

Лучший ответ

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

def a(name):
    global_variables = globals()
    try:
        name_of_passed_in_variable = [x for x in global_variables if id(global_variables[x]) == id(name)][0]
    except Exception:
        name_of_passed_in_variable = "unknown"
    print name_of_passed_in_variable, name

my_name = "Tom"
a(my_name)

a("Mike")

second_name = my_name
a(second_name)

Выдаст вывод:

my_name Tom
unknown Mike
my_name Tom

На моих примерах вы также можете увидеть недостатки этого подхода.

  • Если вы передадите строку напрямую, не назначив переменную, вы не сможете определить имя
  • Если у вас есть несколько ссылок на одну и ту же переменную, вы никогда не узнаете, какое имя будет выбрано

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

3
Thorsten Kranz 31 Янв 2013 в 06:34

В общем, то, что вы хотите, невозможно, потому что Python не передает объекты с прикрепленными именами.

В случае вызова функции вы можете получить словарь с парами имя / значение. if пользователь всегда вызывает функцию с аргументами ключевого слова.

Таким образом, вы можете сделать это:

def a(**kwargs):
    if len(kwargs) != 1:
        raise ValueError("only pass a single keyword arg starting with 'bag' or 'basket'")
        # Above line is Python 3.x syntax; below line is Python 2.x syntax
        # raise ValueError, "only pass a single keyword arg starting with 'bag' or 'basket'"
    name, value = list(kwargs.items())[0]
    if name.startswith('bag'): 
         dist_list = ID['bag']
    else:
         dist_list = ID['basket']
    # I don't know what you want next, but let's say you want to append:
    dist_list.append(value)

Затем пользователь должен вызвать функцию следующим образом:

a(bag=3)
a(basket=5)

РЕДАКТИРОВАТЬ: код теперь корректен для Python 3.x и может быть легко изменен для Python 2.x (см. Комментарий).

РЕДАКТИРОВАТЬ: Теперь, когда я думаю об этом, мы можем обобщить это. Я собираюсь переименовать ID в _containers.

def a(**kwargs):
    for key, value in kwargs.items():
        if key in _containers:
            _containers[key].append(value)
        else:
            raise ValueError("invalid container name '%s'" % key)

Теперь вы можете добавить несколько контейнеров одним вызовом. Но это немного сложно, потому что пользователь может сделать что-то вроде этого:

a(bag=1, basket=2, bag=3)

bag=1 никогда не произойдет, потому что в словаре bag будет установлено значение 3. (Там может быть хитрый способ заменить словарь пользовательским классом с перегруженными функциями, но этот путь кроется в безумии ... Я не рекомендую такой хитрый код.)

Это не так хорошо, но если вы хотите, чтобы имена начинались с bag или что-то еще работало, это сделает это:

def a(**kwargs):
    for key, value in kwargs.items():
        found = False
        for container_name in _containers:
            if key.startswith(container_name):
                _containers[container_name].append(value)
                found = True
                break
        if not found:
            raise ValueError("no container name matched '%s'" % key)
1
steveha 31 Янв 2013 в 06:37

Я думаю, что проблема у вас в разнице в аргументах. Есть ключевые аргументы, в которых имя явно указано вызывающей стороной и простые позиционные аргументы. Вы могли бы сделать это,

def a(**kwargs):
    if 'bag' in kwargs:
         dist_list = ID['bag']
    else:
        dist_list = ID['basket']

И вам придется передавать переменные, как так,

a(bag=...)

Или если вы хотите посмотреть, можете ли вы сделать параметр startswith сумку или корзину,

def a(**kwargs):
    if any(k.startswith('bag') for k in kwargs.iterkeys()):
        dist_list = ID['bag']
    elif any(k.startswith('basket') for k in kwargs.iterkeys()):
        dist_list = ID['basket']
    else:
        raise Exception('a requires argument starting with `basket` or `bag`')

Но тогда у вас есть проблема определения, какой ключ начинается с сумки или корзины, так что я бы, вероятно, сделал,

def a(**kwargs):
    valid_key = [k for k in kwargs.iterkeys() if k.startswith('bag') or k.startswith('basket')][0]
    if valid.key.startswith('bag'):
         dist_list = ID['bag']
    elif valid.key.startswith('basket'):
        dist_list = ID['basket']
    else:
        raise Exception('a requires argument starting with `basket` or `bag`')
2
milkypostman 31 Янв 2013 в 06:16

Подойдет ли что-то подобное вашему варианту использования?

>>> class Inventory(object):
    basket = 0
    bag = 0
    def add_coins(self, **kwargs):
        if 'bag' in kwargs:
            self.bag += kwargs['bag']
        if 'basket' in kwargs:
            self.basket += kwargs['basket']


>>> i = Inventory()
>>> i.add_coins(bag=6)
>>> i.bag
6
>>> i.add_coins(bag=2)
>>> i.bag
8
>>> i.basket
0
1
Alex L 31 Янв 2013 в 06:22