[Python-3000] Open Issues for string.format PEP
Ian Bicking
ianb at colorstudy.com
Mon Apr 24 18:21:11 CEST 2006
Talin wrote:
>>>4) Should there be a way to pass in a dict argument without flattening it via
>>>**args?
>>
>>I think it might not be ambiguous in practice if the first argument
>>could be a dictionary. Because you get:
>>
>> "{foo}".format(a_dict)
>>
>>Since 'foo' isn't an integer and *no* keyword arguments were passed, you
>>assume a_dict is a dictionary. Already "{0}" is going to look for
>>positional arguments even if there is a dictionary with a '0' key, and I
>>think that's just a reasonable limitation. Then there's the case:
>>
>> "{0} is {result}".format(a_dict)
>>
>>Is this an error? I'm not sure; it doesn't really need to be, it'd just
>>evaluate to "{'result': 5} is 5" or something. Potentially this would
>>be a problem, though:
>>
>> "{0} is {result}".format(value, **vars)
>>
>>When vars was {}... statically it would be clear that you intended
>>{result} to come from vars, but at runtime you couldn't detect that.
>>But I can't really imagine where that would happen, especially if
>>there's no support for KeyError.
>
>
> Waaaayy too much voodoo. I think that this kind of implicit deduction of "what
> the programmer meant" is what people are trying to avoid. (And, if you'll
> forgive me for saying so, it's rather Perl-ish.)
I don't think it's hard to tell in any specific circumstance what is
happening; a programmer can statically determine what is happening
easily given the actual method call and the template.
> I want to go back for a moment to the original question. Whenever someone asks
> me "how should we solve this problem?" the first thing I always ask is "do we
> really need to?" Once we've answered that, we can then go on to the issue of
> coming up with a solution.
Without this all dictionaries must be copied. I think maybe Guido
suggested something involving a shortcut that would not covert the
dictionary, but that shortcut isn't available to Python so the behavior
could be different for custom formatters implemented in Python. Also,
only dictionaries are allowed after **; that could be changed (maybe
that's part of the signature changes?), but that doesn't change the fact
that only a dictionary can be passed into a function with **.
In other words, avoiding a dictionary copy when using ** is something
that requires terrible hacks and special cases, and *that* seems
incredibly Perlish to me.
So there's some overhead. That overhead could be considerable depending
on the size of the namespace provided by the dictionary. But it's fixed.
However, it pretty much ruins wrapping as a generalized strategy. I
noted a couple here:
http://mail.python.org/pipermail/python-3000/2006-April/000358.html
Another wrapper could be a cascading dict:
class CascadingDict(object):
def __init__(self, *subdicts):
self.subdicts = subdicts
def __getitem__(self, key):
for d in self.subdicts[:-1]:
try:
return d[key]
except KeyError:
pass
return self.subdicts[-1][key]
So you could do ''.format(CascadingDict(locals(), globals())). You
could get a dictionary that doesn't require copying globals and locals,
and instead delegates to a frame determined dynamically. You could look
keys up in a database. You could look keys up in a translation
database. You could allow '.', like:
class DottedDict(object):
def __init__(self, d):
self.d = d
def __getitem__(self, item):
parts = item.split('.')
ob = self.d[parts[0]]
for attr in parts[1:]:
ob = getattr(ob, attr)
return ob
There's lots and lots of ways of using this pattern. Both % and
string.Template allow for this pattern. Python has been removing over
time the need for namespaces to be plain dictionaries; adding another
place where namespaces are constrained to plain dictionaries is a step
backward.
--
Ian Bicking / ianb at colorstudy.com / http://blog.ianbicking.org
More information about the Python-3000
mailing list