Because you are ignoring the other elements on the tuple that parse(s)
returns:
>>> import string
>>>
>>> tests = [
... "{} spam eggs {}",
... "{0} spam eggs {1}",
... "{0:0.2f} spam eggs {1:0.2f}",
... "{{1}} spam eggs {{2}}"
... ]
>>> for s in tests:
... print [x for x in string.Formatter().parse(s)]
...
[('', '', '', None), (' spam eggs ', '', '', None)]
[('', '0', '', None), (' spam eggs ', '1', '', None)]
[('', '0', '0.2f', None), (' spam eggs ', '1', '0.2f', None)]
[('{', None, None, None), ('1}', None, None, None), (' spam eggs {', None, None, None), ('2}', None, None, None)]
Edit: I see what you mean now. Yes, the interpretation of the parsing is not intuitive nor obvious. The length of the returned list is not for the count of placeholders but for the count of literal portions of strings, including an empty string at the start but not including the empty string at the end. And each element also contains the format of what follows. For example:
>>> list(string.Formatter().parse('{}'))
[('', '', '', None)]
This is the base case, and there is one single empty string of literal text. There are actually two empty strings, but the parser does not include the last empty string.
>>> list(string.Formatter().parse('a {}'))
[('a ', '', '', None)]
Now we have the same as before: only one literal string "a " with nothing that follows. Since there is nothing that follows the format bracket then there is no element.
>>> list(string.Formatter().parse('{} b'))
[('', '', '', None), (' b', None, None, None)]
This is the interesting case: since the format bracket is at the start, the first literal string is an empty literal string, and follows the string " b".
>>> list(string.Formatter().parse('a {1} b {2} c'))
[('a ', '1', '', None), (' b ', '2', '', None), (' c', None, None, None)]
This one is a very complete example. We have three literal string pieces: ['a ', ' b ', ' c']
. The confusing part is that the specific format information for the format brackets {} is merged with the previous literal string element.
Edit2:
>>> [x[0] for x in string.Formatter().parse('another placeholder here {} and here \"{}\"')]
['another placeholder here ', ' and here "', '"']
We follow the same logic here. The quotes are just raw literal string, we can change the quotes to something else:
>>> [x[0] for x in string.Formatter().parse('another placeholder here {} and here qqq{}www')]
['another placeholder here ', ' and here qqq', 'www']
If you only consider the 'name' from each returned tuple you only get the literal string. Between each individual element lies the format placeholder.
You need to understand the result of the parse() from the point of view of formatting the string. This result makes it simple to produce the output formatted string. For example:
>>> [x for x in string.Formatter().parse('a{}')]
[('a', '', '', None)]
>>> [x for x in string.Formatter().parse('a')]
[('a', None, None, None)]
With this logic you can count the number of placeholders in a format string like this:
>>> def count_placeholders(fmt):
... count = 0
... L = string.Formatter().parse(fmt)
... for x in L:
... if x[1] is not None:
... count += 1
... return count
...
>>> count_placeholders('')
0
>>> count_placeholders('{}')
1
>>> count_placeholders('{}{}')
2
>>> count_placeholders('a {}{}')
2
>>> count_placeholders('a {} b {}')
2
>>> count_placeholders('a {} b {} c')
2