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