Как избежать двоеточия в формате Python() при использовании kwargs?

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

Например:

>>> d = {'hello': 'world', 'with:colon': 'moo'}

>>> '{hello}'.format(**d)
'world'

>>> '{with:colon}'.format(**d)
KeyError: 'with'

>>> '{with\:colon}'.format(**d)
KeyError: 'with\\'

>>> '{with::colon}'.format(**d)
KeyError: 'with'

Ответ 1

В качестве обходного пути:

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> '{hello} {}'.format(d['with:colon'],**d)
'world moo'
>>> '{hello} {0}'.format(d['with:colon'],**d)
'world moo'

Ответ 2

Согласно документации, то, что вы просите, просто невозможно. В частности,

Поскольку arg_name не разделяется запятыми, невозможно указать произвольные словарные ключи (например, строки '10' или ':-]') в строке формата.

Ответ 3

Вы не можете - ключи должны быть синтаксически эквивалентны идентификаторам Python. См. Формат строкового синтаксиса в документации:

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
arg_name          ::=  [identifier | integer]
attribute_name    ::=  identifier

Ответ 4

Как @murgatroid99 указывает в его ответе, это невозможно.

Взаимодействие будет заключаться в замене ключей на действующие ключи:

d_sanitised = {key.replace(":", "-"): value for key, value in d.items()}

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

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> d_sanitised = {key.replace(":", "-"): value for key, value in d.items()}
>>> '{with-colon}'.format(**d_sanitised)
'moo'

Очевидно, это предполагает, что вы можете изменить строки формата, чтобы они соответствовали. В идеале просто измените оба конца, чтобы избежать двоеточия.

Ответ 5

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

format('{"with:colon"} and {hello}'

К счастью, оказалось, что легко расширить Formatter для обеспечения этого синтаксиса, здесь реализация POC:

class QuotableFormatter(string.Formatter):
    def __init__(self):
        self.super = super(QuotableFormatter, self)
        self.super.__init__()
        self.quotes = {}

    def parse(self, format_string):
        fs = ''
        for p in re.findall(r'(?:".+?")|(?:[^"]+)', format_string):
            if p[0] == '"':
                key = '_q_' + str(len(self.quotes))
                self.quotes[key] = p[1:-1]
                fs += key
            else:
                fs += p
        return self.super.parse(fs)

    def get_field(self, field_name, args, kwargs):
        if field_name.startswith('_q_'):
            field_name = self.quotes[field_name]
        return self.super.get_field(field_name, args, kwargs)

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

d = {'hello': 'world', 'with:colon': 'moo', "weird!r:~^20": 'hi'}
print QuotableFormatter().format('{"with:colon":*>20} and {hello} and {"weird!r:~^20"}', **d)
# *****************moo and world and hi

Ответ 6

Как и в случае с python 3.6, вы можете решить это с помощью нового f-string formating:

>>> d = {'hello': 'world', 'with:colon': 'moo'}
>>> print(f"with:colon is equal to {d['with:colon']}")
with:colon is equal to moo