2011-08-19 34 views
5

Recientemente he estado haciendo un montón de programación basada en la música, y como tal encuentro haciendo este tipo de cosas mucho que tratar con falta de metadatos en algunas canciones:formato de cadenas en Python

default = {'title': 'Unknown title', 'artist': 'unknown Artist'} 
default.update(song) 
print '{title} - {artist}'.format(**default) 

¿Hay una forma más limpia de hacer esto? He intentado preponderantes con __missing__ como tal pero falta claves todavía lanzan una KeyError:

class Song(dict): 
    # dictionary with some nice stuff to make it nicer 

    def __missing__(self, key): 
     return 'Unknown {key}'.format(key = key) 

Edit: Lo siento que debería haber sido más claro, básicamente, lo siguiente debe trabajar.

s = Song() 
print '{notAKey}'.format(s) 

Un par de personas han señalado que los ** no son necesarios.

Editar 2: He "resuelto" mi problema, al menos para mi satisfacción. Es discutible si esto es más limpio o no, pero funciona para mí.

from string import Formatter 

class DefaultFormatter(Formatter): 

    def get_value(self, key, args, kwargs): 
     # Try standard formatting, then return 'unknown key' 

     try: 
      return Formatter.get_value(self, key, args, kwargs) 
     except KeyError: 
      return kwargs.get(key, 'Unknown {0}'.format(key)) 

class Song(dict): 

    def format(self, formatString): 
     # Convenience method, create a DefaultFormatter and use it 

     f = DefaultFormatter() 
     return f.format(formatString, **self) 

Así que el siguiente volverá 'notAKey Desconocido'

k = Song() 
print k.format('{notAKey}') 
+0

Solo una pequeña recomendación: Parece que está haciendo una aplicación para uso diario, y quizás la distribuya. Si lo desea, entonces debería comenzar a usar [gettext] (http://docs.python.org/library/gettext.html); se ahorrará muchos problemas más adelante. – nagisa

Respuesta

-1

prefiero la versión de Jared, pero si realmente desea implementar por sí mismo, aquí es una manera:

>>> class Song(dict): 
...  def __getitem__(self, key): 
...    if key in self.keys(): return super(Song, self).__getitem__(key) 
...    return 'Unknown {key}'.format(key=key) 
... 
>>> s = Song() 
>>> s['foo'] 
'Unknown foo' 
>>> s['foo'] = 1 
>>> s['foo'] 
1 
+0

+1 para mostrar cómo implementarlo usted mismo, si necesita más personalización. –

+0

'clave en self.keys()' convierte una operación O (1) en O (n). Además, las nuevas líneas son gratuitas. – habnabit

+0

Esto ** no funciona ** porque '** foo' convierte lo pasado en un dict normal. ¿Lo probaste en el caso real dado? –

3

Warning: This solution does not actually work to solve the original question regarding '{title} - {artist}'.format(**default)

¿Ha intentado importar defaultdict de collections?

from collections import defaultdict 

class Song(defaultdict): 
    def __missing__(self, key): 
     return 'Unknown {key}'.format(key = key) 

Aquí hay una pregunta similar: How to make a python dictionary that returns key for keys missing from the dictionary instead of raising KeyError?:

+0

Esto ** no funciona ** por el mismo motivo porque el segundo fragmento de OP no funciona. No sé por qué crees que funcionaría. ¿Ya lo has probado al pasar '** your_instance'? –

-1

Bueno, una opción que se me ocurre está utilizando un método que inherentemente le puede proporcionar valores por defecto, es decir, getattr. Si desea transformar su dict en un objeto hacer girar las llaves en atributos, se puede usar la caja fuerte getattr es posible recuperar los valores:

class AttributeDict(): 
    def __init__(self, source_dict): 
     self._ATTRIBUTE_DICT_source_dict = source_dict 
     for key in source_dict.keys(): 
      setattr(self, str(key), source_dict[key]) 

Ahora simplemente puede utilizar:

song = AttributeDict(song) 
artist = getattr(song, 'artist', 'Unknown Artist') 
6

Resumen Ejecutivo: encapsular la canción en una clase.

Me parece que una canción es un tipo de datos primario en su código y es digno de tener una clase dedicada para representarla. Por supuesto, use un dict interno a la clase para almacenar los datos pero intente moverse a un nivel de abstracción más alto que un dict.

Debería apuntar a esta clase para proporcionar todos los servicios necesarios para apoyar las operaciones que realiza en las canciones. Aloja todo el código de formato de la clase para que no esté disperso sobre tu código que usa los elementos de la canción. De hecho, encontrará todo tipo de operaciones que quieran convertirse en métodos de esta clase.

En cuanto a la implementación del formateo, no importa si es un poco largo, ya que lo escribe en un solo lugar.

1

Desafortunadamente, cuando llamas a un método con la sintaxis **foo, está convirtiendo foo solo a un dict regular.La solución más limpia es probablemente la que publicaste en tu primer fragmento.

+0

Buen punto. Yo prefiero la clase uno. – leoluk

Cuestiones relacionadas