Подтвердить что ты не робот

Python string format() с dict с целыми ключами

Я хотел бы использовать строку Python format(), чтобы действовать как быстрый и грязный шаблон. Тем не менее, dict, который я хотел бы использовать, имеет ключи, которые представляют собой (строковые представления) целых чисел. следует упрощенный пример:

s = 'hello there {5}'
d = {'5': 'you'}
s.format(**d)

приведенный выше код вызывает следующую ошибку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: tuple index out of range

можно ли сделать выше?

4b9b3361

Ответ 1

Мы установили, что это не сработает, но как насчет решения:

Хотя str.format не будет работать в этом случае, достаточно забавно будет старое форматирование %. Это не рекомендуется, но вы запросили быстрый и грязный шаблон.

>>> 'hello there %(5)s' % {'5': 'you'}
'hello there you'

Обратите внимание, что это не будет работать для целых ключей.

>>> 'hello there %(5)s' % {5: 'you'}

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    'hello there %(5)s' % {5: 'you'}
KeyError: '5'

Ответ 2

Мне нравится идея расширения Formatter, чтобы он позволял произвольные имена полей (целые числа, имена полей с двоеточием и т.д.). Реализация может выглядеть так:

import string, re

class QuFormatter(string.Formatter):
    def _quote(self, m):
        if not hasattr(self, 'quoted'):
            self.quoted = {}
        key = '__q__' + str(len(self.quoted))
        self.quoted[key] = m.group(2)
        return '{' + m.group(1) + key + m.group(3) + '}'

    def parse(self, format_string):
        return string.Formatter.parse(self,
            re.sub(r'{([^}`]*)`([^}`]*)`([^}]*)}', self._quote, format_string))

    def get_value(self, key, args, kwargs):
        if key.startswith('__q__'):
            key = self.quoted[key]
        return string.Formatter.get_value(self, key, args, kwargs)

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

d = {'5': 'you', '6': 'me', "okay":1, "weird:thing!": 123456}
print QuFormatter().format(
     'hello there {`5`} {`6`:20s}--{okay}--{`weird:thing!`:20,d}', 
     **d)

Таким образом, поля в обратном направлении обрабатываются буквально.

Ответ 3

См. этот пост для ответов на ваши проблемы. Кажется, что вы не можете использовать строки, состоящие из чисел, как словарные ключи в строках формата (docs link).

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

my_string='hello there {spam:s}'
d={'spam': 'you'}
print my_string.format(**d) # Returns "hello there you"

Ответ 4

От PEP 3101

Встроенный строковый класс (а также класс Unicode в версии 2.6) будет     получить новый метод "формат", который принимает произвольное количество     позиционные и ключевые слова:

"The story of {0}, {1}, and {c}".format(a, b, c=d)

В строке формата определяется каждый позиционный аргумент     с номером, начиная с нуля, поэтому в приведенном выше примере "a"     аргумент 0 и 'b' - это аргумент 1. Каждый аргумент ключевого слова     идентифицируется по его ключевому слову, поэтому в приведенном выше примере "c"     используется для обозначения третьего аргумента.

Числовые значения, используемые в str.format, являются позиционными аргументами. Поэтому вы не можете этого сделать.

Вы можете добраться до PEP 3101 от здесь. Связанный раздел находится в String Methods

Как упоминалось в @Volatility, для этого можно использовать формат %.

Ответ 5

Вы можете сделать что-то с get_value в пользовательском string.Formatter, чтобы попробовать заменить поля в качестве клавиш для словаря, прежде чем возвращаться к индексу в ключи arg - обратите внимание на возможный конфликт приоритетов и намерений здесь... так что это не совсем рекомендуется, но представление о том, что возможно:

import string

class MyFormatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        try:
            return kwargs[str(key)]
        except KeyError:
            return super(MyFormatter, self).get_value(key, args, kwargs)

s = 'hello there {5} - you are number {0}'
d = {'5': 'you'}
print MyFormatter().format(s, 1, 2, 3, **d)
# hello there you - you are number 1

Ответ 6

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

>>> def myformat(s, d):
...   return s.format(*[d.get(str(n)) for n in range(1 + int(max(d, key=int)))])
... 
>>> d = {'5': 'you'}
>>> myformat('hello {5}', d)
'hello you'
>>> myformat('hello {5} {5}', d)
'hello you you'
>>> d['2'] = 'Oh!!'
>>> myformat('{2} hello {5} {5}', d)
'Oh!! hello you you'

К сожалению, в python вам не разрешено исправлять его на str, вы получите TypeError: can't set attributes of built-in/extension type 'str'.

Если у вас есть контроль над dict d, то я думаю, что гораздо лучшее решение состоит в том, чтобы просто поместить числовые строки с подчеркиваниями или чем-то kiss

Ответ 7

Фактически возможно, используя тот факт, что {k} ищет аргумент (k+1) th positional.

def populate_list(d):
   """ Return a list l verifying l[k] = d[str(k)] for each natural k """
   return [d.get(str(k)) for k in range(1 + max(map(int, d)))] if d else []

def format_with_int_keys(s,d):
   """ Replace each {k} in s by d[str(k)] """
   return s.format(*populate_list(d))

s = 'hello there {5}'
d = {'5': 'you'}
print (format_with_int_keys(s,d))

Изменить. Это фактически подробная версия решения @wim.