Why does using a list as a string formatting parameter, even with no %s identifier, return the original string?

前端 未结 1 643
清歌不尽
清歌不尽 2021-01-11 13:21
>>> \'string with no string formatting markers\' % [\'string\']
\'string with no string formatting markers\'
>>> \'string with no string formatting         


        
相关标签:
1条回答
  • 2021-01-11 13:47

    Reading carefully, the documentation states that:

    If format requires a single argument, values may be a single non-tuple object. Otherwise, values must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary).

    Now, in this case the format does not require a single argument and thus the documentation tells us that you should use a tuple or a mapping as argument; other cases fall in "undefined behaviour"(which is what is happening: the behaviour is not consistent in all cases).

    This should probably be considered the final answer to the question: if the string does not have any format specifier, using a list(or any kind different from tuple or a mapping) should simply be considered a bug by itself leading to undefined behaviour.

    From this follows that you ought to always use a tuple or dict as argument, otherwise you have to check for format specifiers by hand or handle odd behaviours.

    In your case you can probably fix the problem using (['string'], ) instead of ['string'].


    Possible "explanation" of why the resultant behaviour seems to be so random:

    It seems like there was a buggy check in the original implementation of PyString_Format/PyUnicode_Format, instead of using PyMappingCheck on this line:

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

    It was used this code:

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

    which is not equivalent. For example set does not have tp_as_mapping set(at least in the Python2.7.3 source code that I have downloaded some weeks ago), while list does set it.

    This might be the reason why list(and possibly other objects) do not raise the TypeError while, set, int and many others do.

    As I stated before in this same answer I do get TypeError even with 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
    

    This probably shows that the above issue is not the only one here.

    Looking at the source code I agree that, in theory, the number of arguments is not checked if the argument is not a tuple, but this would imply 'some string' % 5 -> 'some string' and not a TypeError, so there must be something fishy in that code.

    0 讨论(0)
提交回复
热议问题