2011-08-16 8 views
13

Al escribir una clase recientemente, inicialmente incluía un método __repr__ lo largo de las siguientes líneas:Incluyendo un formato iterable como parte de una cadena con formato grande

return "{}({!r}, {!r}, {!r})".format(
       self.__class__.__name__, 
       self.arg1, 
       self.arg2, 
       self.arg3) 

Repitiendo el '{r!}' Fragmento de código como el que se siente mal y sería tedioso mantener si agrego más argumentos a esta clase. Sin embargo, las alternativas más robustas que se me ocurrieron no van a ganar ningún premio por elegancia tampoco.

La construcción de la cadena de formato mediante programación:

fmt = "{}(%s)" % ", ".join(["{!r}"]*3) 
return fmt.format(self.__class__.__name__, 
        self.arg1, 
        self.arg2, 
        self.arg3) 

formato a los argumentos por separado con str.join:

args = ", ".join(map(repr, [self.arg1, self.arg2, self.arg3])) 
return "{}({})".format(self.__class__.__name__, args) 

he implementado actualmente la clase utilizando el ejemplo anterior, pero estoy interesado en sugerencias para enfoques alternativos (ya que no estoy particularmente contento con ninguna de las opciones anteriores).

Actualización: Inspirado por la respuesta de Ned, he ahora añade la siguiente función de utilidad a un módulo de ayuda:

def format_iter(iterable, fmt='{!r}', sep=', '): 
    return sep.join(fmt.format(x) for x in iterable) 

>>> format_iter(range(10)) 
'0, 1, 2, 3, 4, 5, 6, 7, 8, 9' 
>>> format_iter(range(10), sep='|') 
'0|1|2|3|4|5|6|7|8|9' 
>>> format_iter(range(10), fmt='{:04b}', sep='|') 
'0000|0001|0010|0011|0100|0101|0110|0111|1000|1001' 
>>> format_iter(range(10), fmt='{0.real}+{0.imag}j') 
'0+0j, 1+0j, 2+0j, 3+0j, 4+0j, 5+0j, 6+0j, 7+0j, 8+0j, 9+0j' 

Update2: Terminé la adición de una segunda función de utilidad, casi idéntica a la de la respuesta de AGF:

def call_repr(name, *args): 
    return "{}({})".format(name, format_iter(args)) 

Así que la función __repr__ originalmente ofender ahora queda como:

def __repr__(self): 
    return call_repr(self.__class__.__name__, 
        self.arg1, 
        self.arg2) 

(Sí, uno de los argumentos de constructor originales se fueron el día de hoy.)

Respuesta

8

Si esto es un patrón que se va a repetir, yo probablemente haría uso:

# This is a static method or module-level function 
def argrepr(name, *args): 
    return '{}({})'.format(name, ', '.join(repr(arg) for arg in args)) 

def __repr__(self): 
    return argrepr(self.__name__, self.arg1, self.arg2, self.arg3) 

o

# This is a static method or module-level function 
def argrepr(*args): 
    return '(' + ', '.join(repr(arg) for arg in args) + ')' 

def __repr__(self): 
    return repr(self.__name__) + argrepr(self.arg1, self.arg2, self.arg3) 
+0

Al final resultó que, terminé haciendo esto, solo con una llamada '' format_iter (args) '' en lugar del Inv en línea ocación de '' str.join'' – ncoghlan

8

Mi inclinación sería, si no te gusta el código, se esconden en una función:

def repr_all(*args): 
    return ", ".join(repr(a) for a in args) 


def __repr__(self): 
    args = repr_all(self.arg1, self.arg2, self.arg3) 
    return "{}({})".format(self.__class__.__name__, args) 
+0

aceptado, ya que esta fue la inspiración directa para lo que terminé haciendo (véase la actualización) – ncoghlan

+0

Bueno, fue la respuesta aceptada por un tiempo, hasta que mi solución evolucionó para parecerse más a la sugerencia de agf :) – ncoghlan

0

podría hacerlo gusta:

def __repr__(self): 
    return "{0}({1})".format(
     type(self).__name__, 
     ", ".join(repr(arg) for arg in (self.arg1, self.arg2, self.arg3))) 

o:

_init_getter = operator.attrgetter('arg1', 'arg2', 'arg3') 
def __repr__(self): 
    return "{0}({1})".format(
     type(self).__name__, 
     ", ".join(repr(arg) for arg in self._init_getter(self)))