Защо използването на списък като параметър за форматиране на низ, дори и без идентификатор %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 на този ред:

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