Подтвердить что ты не робот

Почему использование списка в качестве параметра форматирования строки, даже без идентификатора% 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

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

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

4b9b3361

Ответ 1

Внимательно прочитав, в документации указано, что:

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

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

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

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

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


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

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

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 даже с list s:

$ 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, поэтому должно быть что-то подозрительное в этом коде.