str.format (список) с отрицательным индексом не работает в Python - PullRequest
9 голосов
/ 25 октября 2009

Я использую отрицательный индекс в полях замены для вывода отформатированного списка, но он вызывает ошибку TypeError. Коды следующие:

>>> a=[1,2,3]
>>> a[2]
3
>>> a[-1]
3
>>> 'The last:{0[2]}'.format(a)
'The last:3'
>>> 'The last:{0[-1]}'.format(a)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: list indices must be integers, not str

Ответы [ 4 ]

13 голосов
/ 25 октября 2009

Это то, что я бы назвал сбоем дизайна в спецификациях форматной строки. За документы ,

element_index     ::=  integer | index_string

но, увы, -1 это не "целое число" - это выражение. Оператор унар-минус даже не имеет особенно высокого приоритета, так что, например, print(-2**2) испускает -4 - еще одна распространенная проблема и, возможно, сбой дизайна (оператор ** имеет более высокий приоритет, поэтому повышение до сначала происходит увеличение мощности, затем - знак изменения, запрошенный унарным приоритетом с низким приоритетом -).

Все в этой позиции в строке формата, которая не является целым числом (но, например, выражением), обрабатывается как строка для индексации аргумента dict - например:

$ python3 -c "print('The last:{0[2+2]}'.format({'2+2': 23}))"
The last:23

Не уверен, стоит ли поднимать проблему в трассировке Python, но это, безусловно, несколько удивительное поведение: - (.

1 голос
/ 25 октября 2009

Правильно, не работает. Решение:

>>> 'The last:{0}'.format(a[-1])
'The last:3'
1 голос
/ 25 октября 2009

Есть несколько проблем, когда вы начинаете копать:

Элемент, о котором идет речь, называется "element_index", который определен как целое число.

Проблема 1: если пользователи не перейдут по ссылке из "целого числа" на справочное руководство по языку, они не будут знать, что -1 считается выражением, а не целым числом. Кстати, любой, кто испытывает соблазн сказать «работает как документально», должен сначала увидеть проплем 7: -)

Предпочтительное решение: измените определение так, чтобы «element_index» мог иметь необязательный «-» перед целым числом.

Это целое число, верно? Не так быстро ... позже документы говорят, что "выражение формы '[index]' выполняет поиск по индексу, используя __getitem__()"

Проблема 3: Должен сказать «[element_index]» (индекс не определен).

Проблема 4: Не все знают, что делает __getitem__(). Нужны более четкие документы.

Так что мы можем использовать здесь как dict, так и целое число? Да, с одной или двумя проблемами:

element_index является целым числом? Да, это работает с диктовкой:

>>> "{0[2]}".format({2: 'int2'})
'int2'

Кажется, что мы также можем использовать нецелые строки, но для этого требуется более явная документация (проблема 5):

>>> "{0[foo]}".format({'foo': 'bar'})
'bar'

Но мы не можем использовать диктовку с ключом типа «2» (проблема 6):

>>> "{0[2]}".format({'2': 'str2'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 2
>>> "{0['2']}".format({'2': 'str2'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: "'2'"

Проблема 7: То, что «integer» действительно должно быть задокументировано как «decimalinteger» ... 0x22 и 0b11 рассматриваются как str, а 010 («octalinteger») рассматривается как 10, а не 8:

>>> "{0[010]}".format('0123456789abcdef')
'a'

Обновление: PEP 3101 рассказывает правдивую историю:
"" "
Правила разбора ключа элемента очень просты. Если он начинается с цифры, то он рассматривается как число, в противном случае он используется как строка.

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

0 голосов
/ 17 января 2018

Я часто принимаю строки формата Python в качестве параметров конфигурации - со строкой формата, снабженной определенным, известным списком аргументов ключевого слова. Поэтому обращение к индексам списка переменной длины вперед или назад в строке формата - это именно то, что мне в итоге нужно.

Я только что написал этот хак, чтобы заставить работать отрицательную индексацию:

string_to_tokenise = "Hello_world"
tokens = re.split(r"[^A-Z\d]+", string_to_tokenise, flags=re.I)
token_dict = {str(i) if i < 0 else i: tokens[i] for i in range(-len(tokens) + 1, len(tokens))}
print "{thing[0]} {thing[-1]}".format(thing=token_dict)

Результат:

Hello world

Итак, чтобы объяснить, вместо того, чтобы передавать список токенов, я создаю словарь со всеми необходимыми целочисленными ключами для индексации списка от 0 до len (..) - 1, а также добавляю отрицательные целочисленные ключи для индексирование с конца от -1 до - (len (..) - 1), однако эти ключи преобразуются из целых чисел в строки, так как формат будет их интерпретировать.

...