Как избежать ключевой ошибки при форматировании строк? [Дубликат]

Строки в Java неизменяемы. Это означает, что всякий раз, когда вы пытаетесь изменить / изменить строку, вы получаете новый экземпляр. Вы не можете изменить исходную строку. Это сделано для того, чтобы эти экземпляры строк могли кэшироваться. Типичная программа содержит множество ссылок на строки и кеширование этих экземпляров, что может уменьшить объем памяти и увеличить производительность программы.

При использовании оператора == для сравнения строк вы не сравниваете содержимое строки , но фактически сравнивают адрес памяти. Если они равны, в противном случае они вернут true и false. Если значение равно в строке, сравнивает содержимое строки.

Итак, вопрос в том, что все строки кэшируются в системе, как получается == возвращает false, тогда как equals возвращает true? Ну, это возможно. Если вы создадите новую строку, например String str = new String("Testing"), вы создадите новую строку в кеше, даже если в кеше уже содержится строка с тем же содержимым. Короче говоря, "MyString" == new String("MyString") всегда будет возвращать false.

Java также говорит о функции intern (), которая может использоваться в строке, чтобы сделать ее частью кеша, поэтому "MyString" == new String("MyString").intern() вернет true.

Примечание: == оператор намного быстрее, чем равен только потому, что вы сравниваете два адреса памяти, но вы должны быть уверены, что код не создает новые экземпляры String в коде. В противном случае вы столкнетесь с ошибками.

17
задан dreftymac 29 October 2017 в 10:57
поделиться

5 ответов

Вы можете следовать рекомендациям в PEP 3101 и использовать подкласс Formatter:

import string

class BlankFormatter(string.Formatter):
    def __init__(self, default=''):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default)
        else:
            return string.Formatter.get_value(key, args, kwds)

kwargs = {"name": "mark", "adj": "mad"}     
fmt=BlankFormatter()
print fmt.format("My name is {name} and I'm really {adj}.", **kwargs)
# My name is mark and I'm really mad.
print fmt.format("My name is {name} and I'm really {adjective}.", **kwargs)
# My name is mark and I'm really .  

Начиная с Python 3.2 вы можете использовать .format_map в качестве альтернативы:

class Default(dict):
    def __missing__(self, key):
        return '{'+key+'}'

kwargs = {"name": "mark"}

print("My name is {name} and I'm really {adjective}.".format_map(Default(kwargs)))

, который печатает:

My name is mark and I'm really {adjective}.
18
ответ дан Jonathan Komar 15 August 2018 в 16:29
поделиться
  • 1
    Хм. Это интересно. Думаю, я поеду по этому маршруту. Я все еще не поклонник уродливого индексации, который использует другой ответ. Спасибо за предложение! – marky1991 6 November 2013 в 03:35
  • 2
    Я также добавил бы or isinstance(key, unicode) – Nimo 4 June 2014 в 13:22
  • 3
    Я немного смущен. Должно ли Formatter быть string.Formatter? Может ли эта else часть быть достигнута? – Elias Zamaria 1 November 2016 в 18:11
  • 4
    @EliasZamaria: Да, это должно было быть return string.Formatter, и это было исправлено. else достигается, если вы используете форматирование индексации кортежа, например "one {0} two {1}".format(1,2), вместо доступа к ключевому слову. Спасибо за комментарий. Исправлена ​​ошибка в течение трех лет! – dawg 8 March 2017 в 17:33

Вот один из вариантов, который использует collections.defaultdict :

>>> from collections import defaultdict
>>> kwargs = {"name": "mark"}
>>> template = "My name is {0[name]} and I'm really {0[adjective]}."
>>> template.format(defaultdict(str, kwargs))
"My name is mark and I'm really ."

Обратите внимание, что мы не используем **, чтобы больше распаковать словарь в аргументы ключевых слов и спецификатор формата использует {0[name]} и {0[adjective]}, что указывает, что мы должны выполнить ключевой поиск по первому аргументу в format(), используя "name" и "adjective" соответственно. С помощью defaultdict отсутствующий ключ приведет к пустой строке вместо повышения KeyError.

10
ответ дан Andrew Clark 15 August 2018 в 16:29
поделиться
  • 1
    Индексирующее решение не (эстетически) красиво, но вы правы, оно работает так, как я хочу. – marky1991 6 November 2013 в 00:00

Для записи:

s = "My name is {name} and I'm really {adjective}."
kwargs = dict((x[1], '') for x in s._formatter_parser())
# Now we have: `kwargs = {'name':'', 'adjective':''}`.
kwargs.update(name='mark')
print s.format(**kwargs)  # My name is mark and I'm really .
0
ответ дан feqwix 15 August 2018 в 16:29
поделиться

Хотелось добавить довольно простое решение для замены любых значений по умолчанию.

import string

class SafeDict(dict):
    def __init__(self, missing='#', empty='', *args, **kwargs):
        super(SafeDict, self).__init__(*args, **kwargs)
        self.missing = missing
        self.empty = empty
    def __getitem__(self, item):
        return super(SafeDict, self).__getitem__(item) or self.empty
    def __missing__(self, key):
        return self.missing

values = SafeDict(a=None, c=1})
string.Formatter().vformat('{a} {c} {d}', (), values)
# ' 1 #'
0
ответ дан jackotonye 15 August 2018 в 16:29
поделиться

Способ избежать ключевой ошибки состоит в том, чтобы включить в dict, но оставить его пустым:

kwargs = {"name": "mark", "adjective": ""}
"My name is {name} and I'm really {adjective}.".format(**kwargs)

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

"My name is {0} and I'm really {1}.".format("mark")

Печать: «Меня зовут Марк, и я на самом деле». В то время как

"My name is {0} and I'm really {1}.".format("mark","black")

Печатает «Меня зовут Марк, и я действительно черный».

Кроме того, вы можете поймать ValueError.

-1
ответ дан Tommy 15 August 2018 в 16:29
поделиться
  • 1
    В моем конкретном случае это решение не будет работать. Я намерен заполнить значения, которые я собираюсь использовать. Я просто пытаюсь защитить себя, чтобы, если я (или кто-то другой) попадает в поле и забывает положить его в dict, мы не просто бросаем исключение и отказываемся от всей строки. – marky1991 5 November 2013 в 23:57
  • 2
    Ну, вы все равно должны ловить ошибки. Вы также можете сделать assert раньше времени, чтобы убедиться, что прилагательное по умолчанию инициализируется пустым в словаре. – Tommy 5 November 2013 в 23:59
  • 3
    Решение выше меня by @Rob такое же, как мое только более подробное: очевидно, обновите прилагательное позже, если значение найдено – Tommy 6 November 2013 в 00:01
  • 4
    Нет, я не статически знаю, какая строка будет называться форматом (и с каким kwargs). В этом вся проблема. I am ловить исключение (поэтому он не разбивает все приложение), но я не хочу полностью сбой, когда вместо этого могу получить частично заполненную строку. – marky1991 6 November 2013 в 00:01
Другие вопросы по тегам:

Похожие вопросы: