Почему использование списка в качестве параметра форматирования строки, даже без идентификатора %s, возвращает исходную строку?

>>> 'string with no string formatting markers' % ['string']
'string with no string formatting markers'
>>> 'string with no string formatting markers' % ('string',)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

Я ожидал бы, что оба случая поднимут TypeError, но это не так. Почему бы и нет?

В документации Python по этому вопросу говорится о строках, кортежах и словарях. , но ничего не говорит о списках. Я немного смущен этим поведением. Мне удалось продублировать его в Python 2.7 и 3.2.


person Fredrick Brennan    schedule 13.12.2012    source источник
comment
Для меня это похоже на недокументированную функцию (ошибку).   -  person mgilson    schedule 13.12.2012
comment
FWIW, jython2.2.1 поднимает TypeError   -  person mgilson    schedule 13.12.2012


Ответы (1)


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

Если для формата требуется один аргумент, значения могут быть одним объектом, отличным от tuple. В противном случае значения должны быть tuple с числом элементов, точно указанным в строке формата, или одним объектом сопоставления (например, словарем).

Теперь в этом случае format не требует единого аргумента, и поэтому документация говорит нам, что вы должны использовать tuple или отображение в качестве аргумента; другие случаи попадают в «неопределенное поведение» (что и происходит: поведение не является последовательным во всех случаях).

Это, вероятно, следует считать окончательным ответом на вопрос: если строка не имеет спецификатора формата, использование list (или любого другого типа, отличного от tuple или сопоставления) должно просто рассматриваться как ошибка сама по себе, приводящая к неопределенному поведению.

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

В вашем случае вы, вероятно, можете решить проблему, используя (['string'], ) вместо ['string'].


Возможное «объяснение» того, почему результирующее поведение кажется таким случайным:

Похоже, что в исходной реализации PyString_Format/PyUnicode_Format вместо использования PyMappingCheck была ошибка check. в этой строке:

if (PyMapping_Check(args) && !PyTuple_Check(args) &&
     !PyObject_TypeCheck(args, &PyBaseString_Type))
    dict = args;

Был использован этот код:

if (Py_TYPE(args)->tp_as_mapping && !PyTuple_Check(args) &&
    !PyObject_TypeCheck(args, &PyBaseString_Type))
    dict = args;

что не эквивалентно. Например, set не имеет установленного tp_as_mapping (по крайней мере, в исходном коде Python2.7.3, который я скачал несколько недель назад), а list устанавливает его.

Это может быть причиной того, что list (и, возможно, другие объекты) не вызывают TypeError, в то время как set, int и многие другие делают это.

Как я уже говорил ранее в этом же ответе, я получаю TypeError даже с lists:

$ python2
Python 2.7.3 (default, Sep 26 2012, 21:53:58) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 'some string' % []
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

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

Глядя на исходный код, я согласен с тем, что теоретически количество аргументов не проверяется, если аргумент не является кортежем, но это будет означать 'some string' % 5 -> 'some string', а не TypeError, так что должно быть что-то подозрительное в этом коде.

person Bakuriu    schedule 13.12.2012
comment
Какую версию python 2 вы используете? Я не могу воспроизвести это на 2.5.4, 2.6.1 или 2.7.2. - person Wooble; 13.12.2012
comment
Интересно, у вас это вызывает TypeError в Python 2? Мне это не подходит Python 2.7.3 (default, Aug 1 2012, 05:14:39) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> 'some string' % ['a'] 'some string' - person Fredrick Brennan; 13.12.2012
comment
Я не понимаю TypeError, который вы показываете (и, судя по вопросу, я тоже не думаю, что OP), то есть "foobar"%['duck'] с радостью возвращает "foobar". Я использую Python2.7.3, 2.6.4, 3.2.3 в OS-X 10.5.8... Нет ошибок с python2.7.3, 2.6.5, 3.2.0 в Ubuntu - person mgilson; 13.12.2012