new string formatting with local variables

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jun 6 23:57:59 EDT 2011


On Tue, 07 Jun 2011 10:11:01 +1000, Ben Finney wrote:

> Chris Rebert <clp2 at rebertia.com> writes:
> 
>> print "{solo} was captured by {jabba}".format(**locals()) # RIGHT
> 
> I tend to use ‘u"foo {bar} baz".format(**vars())’, since ‘vars’ can also
> take the namespace of an object. I only need to remember one “give me
> the namespace” function for formatting.
> 
>> You must use prefix-** in the call to unpack the mapping as keyword
>> arguments. Note that using locals() like this isn't best-practice.
> 
> Who says so, and do you find their argument convincing? Do you have a
> reference for that so we can see why?

It's a code smell. Why is this code messing with locals() instead of 
using names explicitly? Is it possible that this code will attempt to 
modify locals()? I need to look twice to be sure its safe. Why do you 
need to pass *all* of the locals if only two names are used?

Seeing an arbitrary large number of arguments passed to a piece of code 
that only requires two makes me feel hinky. It's not that it's 
*necessarily* bad, in and of itself, but it should make you take a 
second, closer look at it.

Where possible, I'd rather be explicit about which names I want:

solo = "Han Solo"
jabba = "Jabba the Hutt"

"{hero} was captured by {villain}.".format(hero=solo, villain=jabba)


It also strikes me as potentially wasteful to unpack an arbitrarily large 
dict into keyword arguments, and then (presumably) have the format method 
pack them back into a dict again. Again, this might be trivial... but it 
might not be. No way of knowing just by reading that line of code, hence 
a code smell.


Oh, and there's even a failure mode for this **locals() or **vars() 
pattern, at least for CPython. If you do this in production code, I hate 
you, but it can happen:


>>> globals()[42] = "spam spam spam"  # Ouch!
>>> vars()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': 
'__main__', 42: 'spam spam spam', '__doc__': None, '__package__': None}
>>> def f(**kwargs):
...     print kwargs
...
>>>
>>> f(**vars())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() keywords must be strings




-- 
Steven



More information about the Python-list mailing list