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
можно ли сделать выше?
Ответы
Ответ 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.