Pytanie Dlaczego użycie listy jako parametru formatowania ciągu, nawet bez identyfikatora% s, zwraca oryginalny ciąg znaków?


>>> '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

Spodziewam się, że oba przypadki podniosą a TypeError, ale tak nie jest. Dlaczego nie?

The Dokumentacja w języku Python na ten temat mówi o łańcuchach, krotkach i słownikach, ale nie mówi nic o listach. Jestem nieco zdezorientowany tym zachowaniem. Udało mi się go powielić w Pythonie 2.7 i 3.2.


12
2017-12-13 16:48


pochodzenie


Wygląda mi na nieudokumentowaną "cechę" (błąd). - mgilson
FWIW, jython2.2.1  robi podnieść a TypeError - mgilson


Odpowiedzi:


Czytając uważnie, dokumentacja stwierdza, że:

Jeśli format wymaga pojedynczego argumentu, wartości mogą być pojedynczymtuple   obiekt. W przeciwnym razie wartości muszą być a tuple z dokładnie taką liczbą   elementów określonych przez ciąg formatu lub pojedynczy obiekt odwzorowania   (na przykład słownik).

Teraz, w tym przypadku format nie wymaga pojedynczego argumentu, a zatem dokumentacja mówi nam, że powinieneś użyć tuple lub mapowanie jako argument; inne przypadki należą do "niezdefiniowanego zachowania" (co się dzieje: zachowanie nie jest spójne we wszystkich przypadkach).

Prawdopodobnie powinno to zostać uznane za ostateczną odpowiedź na pytanie: jeśli ciąg znaków nie ma żadnego specyfikatora formatu, użyj znaku a list(lub każdy inny niż tuple lub mapowanie) powinno po prostu zostać uznane za błąd sam w sobie prowadzący do niezdefiniowanego zachowania.

Z tego wynika, że ​​powinieneś zawsze użyć tuple lub dict jako argument, w przeciwnym razie musisz ręcznie sprawdzić specyfikatory formatu lub obsłużyć dziwne zachowania.

W twoim przypadku prawdopodobnie możesz rozwiązać problem za pomocą (['string'], ) zamiast ['string'].


Możliwe "wyjaśnienie" tego, dlaczego wynikowe zachowanie wydaje się takie przypadkowe:

Wygląda na to, że był buggy czek w pierwotnej implementacji PyString_Format/PyUnicode_Format, zamiast używać PyMappingCheck na to linia:

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

Użyto tego kodu:

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

który nie jest równoważny. Na przykład set nie ma tp_as_mapping zestaw (przynajmniej w kodzie źródłowym Python2.7.3, który pobrałem kilka tygodni temu), podczas gdy list ustawia to.

To może być powód list(i ewentualnie inne przedmioty) nie podnosić TypeError podczas, set, int i wielu innych.

Jak już wcześniej wspomniałem w tej samej odpowiedzi, otrzymuję TypeError nawet z 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

To prawdopodobnie pokazuje, że powyższy problem nie jest tutaj jedyny.

Patrząc na kod źródłowy zgadzam się, że W teorii, liczba argumentów nie jest sprawdzana, jeśli argument nie jest krotką, ale to implikowałoby 'some string' % 5 -> 'some string' a nie a TypeError, więc w tym kodzie musi być coś podejrzanego.


6
2017-12-13 16:58



Jakiej wersji python 2 próbujesz? Nie mogę tego powtórzyć w 2.5.4, 2.6.1 lub 2.7.2 - geoffspear
To interesujące, podnosi a TypeError dla ciebie w Pythonie 2? To nie dla mnie: 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' - Fredrick Brennan
Nie rozumiem TypeError które pokazujesz (i od pytania, nie sądzę, że OP to albo), to jest "foobar"%['duck']szczęśliwie wraca "foobar". Używam Python2.7.3, 2.6.4, 3.2.3 na OS-X 10.5.8 ... Nie ma błędu w Pythonie2.7.3, 2.6.5, 3.2.0 na Ubuntu - mgilson