¿Puedo usar una asignación dinámica para desempaquetar argumentos de palabras clave en Python?

En pocas palabras, quiero llamar al formato con argumentos con nombres arbitrarios, que realizarán una búsqueda.

'{Thing1} and {other_thing}'.format(**my_mapping) 

He intentado implementar my_mapping de esta manera:

 class Mapping(object): def __getitem__(self, key): return 'Proxied: %s' % key my_mapping = Mapping() 

Que funciona como se esperaba cuando se llama my_mapping['anything'] . Pero cuando se pasa al formato () como se muestra arriba, obtengo:

 TypeError: format() argument after ** must be a mapping, not Mapping 

Intenté dict subclases de dict lugar de object , pero ahora al llamar a format() como se muestra, se genera KeyError . Incluso implementé __contains__ como return True , pero aún así KeyError .

Entonces, parece que ** no es solo llamar a __getitem__ en el objeto pasado. ¿Alguien sabe cómo solucionar esto?

    En Python 2 puedes hacer esto usando la clase string.Formatter .

     >>> 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 toma 3 vformat : la cadena de formato, la secuencia de campos posicionales y la asignación de campos de palabras clave. Como los campos de posición no eran necesarios, usé una tupla vacía () .

    Python 3.2+:

     '{Thing1} and {other_thing}'.format_map(my_mapping) 

    Esto puede ser un poco de nigromancia, pero recientemente me encontré con este problema, y ​​esta pregunta SO fue el primer resultado. No estaba contento con el uso de string.Formatter , y lo quería para Just Work (TM) .

    Si implementa una función keys() para su clase así como __getitem__() , entonces **my_mapping funcionará.

    Es decir:

     class Mapping(object): def __getitem__(self, key): return 'Proxied: %s' % key def keys(self): return proxy.keys() 

    dónde

     >>> my_mapping = Mapping() >>> my_mapping.keys() ['Thing1','other_thing',...,'Thing2'] 

    resultará en un mapeo exitoso que funcionará con .format .

    Aparentemente (aunque en realidad no he buscado en la fuente para str.format ), parece que usa keys() para obtener una lista de claves, luego asigna los identificadores dados en la cadena a esas claves, luego usa __getitem__() para recuperar los valores especificados.

    Espero que esto ayude.

    EDITAR :

    Si se encuentra en la posición de @ aaron-mcmillin, y el conjunto de claves es grande, un enfoque posible es no generar un conjunto completo de claves, sino generar un subconjunto más pequeño. Por supuesto, esto solo funciona si sabe que solo necesitará formatear un pequeño subconjunto.

    Es decir:

     class Mapping(object): ... def keys(self): return ['Thing1','other_thing', 'Thing2'] 

    Esto es lo mejor que se me ocurre:

    Si tiene un objeto de mapeo personalizado que quiere pasar a una función que toma argumentos de palabras clave, debe tener un conjunto de claves (que pueden generarse dinámicamente, pero debe ser un conjunto finito), y debe ser capaz de para asignar esas claves de alguna manera. Entonces, si puede asumir que tendrá un __iter__ para obtener las claves, y un __getitem__ que tendrá éxito para cada una de esas claves, por ejemplo:

     class Me(object): def __init__(self): pass def __iter__(self): return iter(['a', 'b', 'c']) def __getitem__(self, key): return 12 

    Digamos que la función es:

     def myfunc(**kwargs): print kwargs, type(kwargs) 

    Entonces podemos pasarlo haciendo un dict:

     m = Me() myfunc(**dict((k, m[k]) for k in m)) 

    Resultando en:

     {'a': 12, 'c': 12, 'b': 12}  

    Aparentemente, esta debe ser la forma en que se hace … incluso si pasas un objeto derivado de dict , la función seguirá teniendo un dict para los kwargs:

     class Me(dict): pass m = Me() print type(m) #prints  def myfunc(**kwargs): print type(kwargs) myfunc(**m) #prints  

    Como parece que quisieras hacer algo como devolver un valor basado en lo que era la clave, sin tener en cuenta un conjunto particular de claves, parece que no puedes usar la función de format .