I have a piece of code as below:
tupvalue = [(\'html\', 96), (\'css\', 115), (\'map\', 82)]
So while printing the above tuple in the desired fo
First, the easy question:
How can I use a comprehension to format all the values in
tupvalue
in the required format as in the example shown?
That's a list comprehension: ['%s:%d' % t for t in tupvalue]
Now, the harder question!
how the single value
tupvalue[0]
is recognised as a tuple of two values by the format specifier'%s:%d'
?
Your intuition that something a bit strange is going on here is correct. Tuples are special-cased in the language for use with string formatting.
>>> '%s:%d' % ('css', 115) # tuple is seen as two elements
'css:115'
>>> '%s:%d' % ['css', 115] # list is just seen as one object!
TypeError: not enough arguments for format string
The percent-style string formatting does not duck-type properly. So, if you actually wanted to format a tuple, you'll have to wrap it in another tuple, unlike any other kind of object:
>>> '%s' % []
'[]'
>>> '%s' % ((),)
'()'
>>> '%s' % ()
TypeError: not enough arguments for format string
The relevant section of the documentation is at section 4.7.2. printf-style String Formatting, where it is mentioned:
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
The odd handling of tuples is one of the quirks called out in the note at the beginning of that section of the documentation, and one of the reasons that the newer string formatting method str.format
is recommended instead.
Note that the handling of the string formatting happens at runtime†. You can verify this with the abstract syntax tree:
>>> import ast
>>> ast.dump(ast.parse('"%s" % val'))
"Module(body=[Expr(value=BinOp(left=Str(s='%s'), op=Mod(), right=Name(id='val', ctx=Load())))])"
'%s' % val
parses to a binary operation on '%s'
and val
, which is handled like str.__mod__(val)
, in CPython that's a BINARY_MODULO
opcode. This means it's usually up to the str
type to decide what to do when the val
received is incorrect*, which occurs only once the expression is evaluated, i.e. once the interpreter has reached that line. So, it doesn't really matter whether the val
is the wrong type or has too few/too many elements - that's a runtime error, not a syntax error.
† Except in some special cases where CPython's peephole optimizer is able to "constant fold" it at compile time.
* Unless val
's type subclasses str
, in which case type(val).__rmod__
should be able to control the result.