0

After reading these:
Special use of args / kwargs
https://www.python.org/dev/peps/pep-3102/
What does ** (double star) and * (star) do for parameters?
https://docs.python.org/3.5/library/string.html#formatstrings

I want to be sure I understand well: str.format(*args, **kwargs)

if uniques is a list of two integers such as [0, 0], which I would use for example in a loop with a condition to increment the first element and no condition to increment the second, with:

 print('Task finished. {0!s} retrieved out of {1!s} tested'.format(*uniques))

I do have to use *uniques because instead uniques would be passed as a tuple to format. But if I do use

 print('Task finished. {0.[0]} retrieved out of {0.[1]} tested'.format(uniques))

It raises value error: ValueError: Empty attribute in format string. Using brackets around uniques doesn't help. I don't really get why? Could someone please clarify?

In the first case, is the list unpacked then converted to a tuple, while in the second, it is not because the list can't be converted by format as a tuple right away, the same way format(uniques[0], uniques[1]) would? If I am right, why is that the case, since there's is a tuple(list) function to do that and so, it is very simple?

Community
  • 1
  • 1
Ando Jurai
  • 1,003
  • 2
  • 14
  • 29

2 Answers2

4

You are mixing attribute and subscription syntax. 0.[0] tells the format to look for an attribute by the name [0]. However, the formal grammar only allows for a valid identifier to follow the dot (meaning a word formed of letters, digits and underscores, starting with a letter or underscore), and [0] doesn't match that rule, so you get an error that there is no attribute name at all.

Drop the dots:

print('Task finished. {0[0]} retrieved out of {0[1]} tested'.format(uniques))

This now works, because now the format parser correctly sees subscriptions:

>>> uniques = [0, 0]
>>> print('Task finished. {0[0]} retrieved out of {0[1]} tested'.format(uniques))
Task finished. 0 retrieved out of 0 tested
>>> uniques = [42, 81]
>>> print('Task finished. {0[0]} retrieved out of {0[1]} tested'.format(uniques))
Task finished. 42 retrieved out of 81 tested

The 0[0] specifier tells the str.format parser to use the first positional argument (here provided by uniques) and then threat that as a sequence by subscribing to [0], so the first element.

When you use '...'.format(*uniques), any iterable will do, be it a list or a tuple or a different kind of iterable (including generators); Python iterates over it to produce separate arguments. For [42, 81] that means Python will call the method as if you called it with '...'.format(42, 81) either way. In the string format, you then have to address these separate arguments with {0} and {1}.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I guess there's no answer here to how can I be so dumb, but you're awesome! I guess anyway, *list is more pythonic and formal, but I wanted to be sure to understand the mechanics even if I would not use it. Just for elation and peace of the mind. – Ando Jurai Mar 17 '16 at 11:58
  • 1
    @AndoJurai: it depends on what you want to do; if you have a variable number of templates, using named slots with attributes or subscriptions can a be very powerful way to make those templates more flexible, applying a whole namespace mapping to them with `template.format(**mapping)`. – Martijn Pieters Mar 17 '16 at 12:29
1

You have a typo in your format string:

print('Task finished. {0[0]} retrieved out of {0[1]} tested'.format(uniques))
Schore
  • 885
  • 6
  • 16