问题
Long story short, I want to call format with arbitrarily named arguments, which will preform a lookup.
'{Thing1} and {other_thing}'.format(**my_mapping)
I've tried implementing my_mapping like this:
class Mapping(object):
def __getitem__(self, key):
return 'Proxied: %s' % key
my_mapping = Mapping()
Which works as expected when calling my_mapping['anything']
. But when passed to format() as shown above I get:
TypeError: format() argument after ** must be a mapping, not Mapping
I tried subclassing dict
instead of object
, but now calling format()
as shown raises KeyError
. I even implemented __contains__
as return True
, but still KeyError
.
So it seems that **
is not just calling __getitem__
on the object passed in. Does anyone know how to get around this?
回答1:
In Python 2 you can do this using string.Formatter
class.
>>> class Mapping(object):
... def __getitem__(self, key):
... return 'Proxied: %s' % key
...
>>> my_mapping = Mapping()
>>> from string import Formatter
>>> Formatter().vformat('{Thing1} and {other_thing}', (), my_mapping)
'Proxied: Thing1 and Proxied: other_thing'
>>>
vformat
takes 3 args: the format string, sequence of positional fields and mapping of keyword fields. Since positional fields weren't needed, I used an empty tuple ()
.
回答2:
Python 3.2+:
'{Thing1} and {other_thing}'.format_map(my_mapping)
回答3:
This may be a bit of necromancy, but I recently came across this problem, and this SO question was the first result. I wasn't happy with using string.Formatter
, and wanted it to Just Work (TM).
If you implement a keys()
function for your class as well as __getitem__()
, then **my_mapping
will work.
I.e:
class Mapping(object):
def __getitem__(self, key):
return 'Proxied: %s' % key
def keys(self):
return proxy.keys()
where
>>> my_mapping = Mapping()
>>> my_mapping.keys()
['Thing1','other_thing',...,'Thing2']
will result in a successful mapping that will work with .format
.
Apparently (though I haven't actually looked at the source for str.format
), it appears to use keys()
to get a list of keys, then map the identifiers given in the string to those keys, then use __getitem__()
to retrieve the specified values.
Hope this helps.
EDIT:
If you are in @aaron-mcmillin's position, and the key set is large, then a possible approach is to not generate a full set of keys, but generate a smaller subset. This only works of course if you know you will only need to format a small subset.
I.e:
class Mapping(object):
...
def keys(self):
return ['Thing1','other_thing', 'Thing2']
回答4:
This is the best I could come up with:
If you have a custom mapping object that you want to pass to a func taking key-word arguments, then it must have a set of keys (which may be dynamically generated, but it must be a finite set), and it must be able to map those keys somehow. So, if you can assume that it will have an __iter__
to get the keys, and a __getitem__
that will succeed for each of those keys, e.g.:
class Me(object):
def __init__(self):
pass
def __iter__(self):
return iter(['a', 'b', 'c'])
def __getitem__(self, key):
return 12
Say the function is:
def myfunc(**kwargs):
print kwargs, type(kwargs)
Then we can pass it along by making a dict:
m = Me()
myfunc(**dict((k, m[k]) for k in m))
Resulting in:
{'a': 12, 'c': 12, 'b': 12} <type 'dict'>
Apparently this must be the way it's done... even if you pass in an object derived from dict
, the function will still have a dict
for the kwargs:
class Me(dict): pass
m = Me()
print type(m) #prints <class '__Main__.Me'>
def myfunc(**kwargs):
print type(kwargs)
myfunc(**m) #prints <type 'dict'>
Since it sounds like you wanted to do something like return a value based on what the key was, without having a particular set of keys in mind, it seems like you can't use the format
function.
来源:https://stackoverflow.com/questions/8218652/can-i-use-a-dynamic-mapping-to-unpack-keyword-arguments-in-python