Могу ли я использовать динамическое сопоставление для распаковки аргументов ключевых слов в Python?

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

'{Thing1} and {other_thing}'.format(**my_mapping)

Я пытался реализовать my_mapping следующим образом:

class Mapping(object):
  def __getitem__(self, key):
    return 'Proxied: %s' % key
my_mapping = Mapping()

Что работает, как и ожидалось, при вызове my_mapping['anything']. Но при передаче в формат(), как показано выше, я получаю:

TypeError: format() argument after ** must be a mapping, not Mapping

Я попытался создать подкласс dict вместо object, но теперь вызов format(), как показано, вызывает KeyError. Я даже реализовал __contains__ как return True, но все равно KeyError.

Таким образом, кажется, что ** не просто вызывает __getitem__ для переданного объекта. Кто-нибудь знает, как это обойти?


person Aaron McMillin    schedule 21.11.2011    source источник
comment
** не мог работать только с __getitem__. Какие элементы он получит при передаче аргументов функции, которая принимает любые аргументы ключевого слова (**kwargs)?   -  person Matti Virkkunen    schedule 22.11.2011
comment
Это может быть так же хорошо, как и для python ‹ 3.2. '{0[Thing1]} and {0[other_thing]}'.format(my_mapping) По крайней мере, это работает.   -  person Aaron McMillin    schedule 22.11.2011
comment
Как правило, нет необходимости комментировать свой собственный вопрос, например, вы можете обновить свой вопрос, чтобы указать, что вы используете Python 2.6.5, если это уместно, или опубликовать свой последний комментарий в качестве ответа (и если ничего лучшего не появится в паре дней, то примите его).   -  person jfs    schedule 22.11.2011


Ответы (4)


В Python 2 вы можете сделать это, используя класс string.Formatter.

>>> class Mapping(object):
...     def __getitem__(self, key):
...         return 'Proxied: %s' % key
...
>>> my_mapping = Mapping()
>>> from string import Formatter
>>> Formatter().vformat('{Thing1} and {other_thing}', (), my_mapping)
'Proxied: Thing1 and Proxied: other_thing'
>>>

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

person yak    schedule 21.11.2011
comment
Идеальный! Это именно то, что мне нужно. - person Aaron McMillin; 22.11.2011

Питон 3.2+:

'{Thing1} and {other_thing}'.format_map(my_mapping)
person jfs    schedule 21.11.2011
comment
Это именно то, что мне нужно, но это было добавлено в python 3.2 (только что посмотрел) - person Aaron McMillin; 22.11.2011

Это может быть немного некромантии, но я недавно столкнулся с этой проблемой, и этот ТАК вопрос был первым результатом. Мне не понравилось использование string.Formatter, и я хотел, чтобы он просто работал (TM).

Если вы реализуете функцию keys() для своего класса, а также __getitem__(), то **my_mapping будет работать.

I.e:

class Mapping(object):

  def __getitem__(self, key):
    return 'Proxied: %s' % key

  def keys(self):
    return proxy.keys()

куда

>>> my_mapping = Mapping()
>>> my_mapping.keys()
['Thing1','other_thing',...,'Thing2']

приведет к успешному отображению, которое будет работать с .format.

По-видимому (хотя я на самом деле не просматривал источник str.format), похоже, он использует keys() для получения списка ключей, затем сопоставляет идентификаторы, указанные в строке, с этими ключами, а затем использует __getitem__() для получения указанных значений.

Надеюсь это поможет.

ИЗМЕНИТЬ:

Если вы находитесь в положении @aaron-mcmillin и набор ключей велик, то возможный подход состоит в том, чтобы не генерировать полный набор ключей, а генерировать меньший подмножество. Конечно, это работает только в том случае, если вы знаете, что вам нужно будет отформатировать только небольшое подмножество.

I.e:

class Mapping(object):
  ...
  def keys(self):
    return ['Thing1','other_thing', 'Thing2']
person A.Ford    schedule 27.10.2014
comment
Это здорово, но, как сказано в моем комментарии выше, мое ключевое пространство — это все CN в моем LDAP, и количество поисков на строку будет минимальным. Поэтому я действительно не хочу генерировать все ключи. - person Aaron McMillin; 29.10.2014
comment
В этом случае решение вам не подходит, но все же это вполне верный (и правильный) ответ. Он ничего не делает, кроме как предоставляет стандартные магические функции Python, которые предоставил бы format-способный класс. Я полностью понимаю, что если у вас большой набор ключей (и если ваш LDAP-сервер подключен по сети), вы можете не захотеть тратить циклы, необходимые для генерации набора ключей. - person A.Ford; 30.10.2014

Это лучшее, что я мог придумать:

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

class Me(object):
    def __init__(self):
        pass

    def __iter__(self):
        return iter(['a', 'b', 'c'])

    def __getitem__(self, key):
        return 12

Скажем, функция:

def myfunc(**kwargs):
    print kwargs, type(kwargs)

Затем мы можем передать его, сделав диктовку:

m = Me()
myfunc(**dict((k, m[k]) for k in m))

В результате чего:

{'a': 12, 'c': 12, 'b': 12} <type 'dict'>

По-видимому, так и должно быть... даже если вы передадите объект, производный от dict, функция все равно будет иметь dict для kwargs:

class Me(dict): pass

m = Me()
print type(m) #prints <class '__Main__.Me'>

def myfunc(**kwargs):
    print type(kwargs)
myfunc(**m) #prints <type 'dict'>

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

person Claudiu    schedule 21.11.2011
comment
Ну, конкретный набор ключей — это все cn в моем LDAP, что немного излишне для извлечения в словарь для одного или двух поисков. - person Aaron McMillin; 22.11.2011
comment
@AaronCroyle: да, в основном этот ответ говорит, что вы не можете использовать функцию format для того, что хотите сделать. - person Claudiu; 22.11.2011