2010-09-10 20 views
5

estoy encontrando a mí mismo escribiendo un montón de clases con constructores como este:Mejorar __init__ donde args se asignan directamente a los miembros

class MyClass(object): 
    def __init__(self, foo, bar, foobar=1, anotherfoo=None): 
     self.foo = foo 
     self.bar = bar 
     self.foobar = foobar 
     self.anotherfoo = anotherfoo 

Es un mal olor código? ¿Python ofrece una forma más elegante de manejar esto?

Mis clases e incluso algunos de los constructores son más que solo lo que he mostrado, pero normalmente tengo una lista de argumentos pasados ​​al constructor que acaban siendo asignados a miembros con nombres similares. Hice algunos de los argumentos opcionales para señalar el problema de hacer algo como:

class MyClass(object): 
    def __init__(self, arg_dict): 
     self.__dict__ = arg_dict 
+0

A menos que prevea que la lista de argumentos crece mucho más tiempo que eso o necesita un conjunto arbitrario de argumentos siempre convertidos a attribs (en cuyo caso haré cumplir el uso de kwargs), no veo nada de malo en eso. Hago lo mismo en la mayoría de los casos, se lee bien y es una prerrogativa prevista de un __init__ de todos modos. No creo que huele en absoluto. –

+0

Ver también http://stackoverflow.com/questions/3652851. – FMc

Respuesta

8

Si son kwargs, que podría hacer algo como esto:

def __init__(self, **kwargs): 
    for kw,arg in kwargs.iteritems(): 
     setattr(self, kw, arg) 

posargs son un poco más complicado, ya no obtienes información para nombrar de una manera agradable.

Si desea proporcionar valores por defecto, puede hacerlo de esta manera:

def __init__(self, **kwargs): 
    arg_vals = { 
     'param1': 'default1', 
     # ... 
    } 
    arg_vals.update(kwargs) 
    for kw,arg in arg_vals.iteritems(): 
     setattr(self, kw, arg) 
0

Esto es horrible, código.

class MyClass(object): 
    def __init__(self, foo, bar, spam, eggs): 
     for arg in self.__init__.func_code.co_varnames: 
      setattr(self, arg, locals()[arg]) 

A continuación, puede hacer algo como:

myobj = MyClass(1, 0, "hello", "world") 
myletter = myobj.spam[myobj.bar] 
+4

Tenga en cuenta que para cosas como esta, usar kwargs es probablemente mucho más legible para alguien que intenta mantener el código más tarde - es realmente molesto leer código y ver algo como 'MyClass (1, 4, 6, 2)' – Amber

1

se podría hacer algo como esto:

def Struct(name): 
    def __init__(self, **fields): 
     self.__dict__.update(fields) 
    cls = type(name, (object,), {'__init__', __init__}) 
    return cls 

Usted podría utilizarlo como:

MyClass = Struct('MyClass') 
t = MyClass(a=1, b=2) 

Si quieres argumentos posicionales bien, luego usa esto:

def Struct(name, fields): 
    fields = fields.split() 
    def __init__(self, *args, **kwargs): 
     for field, value in zip(fields, args):  
      self.__dict__[field] = value 
     self.__dict__.update(kwargs) 
    cls = type(name, (object,), {'__init__': __init__}) 
    return cls 

Es entonces utilizado como

MyClass = Struct('MyClass', 'foo bar foobar anotherfoo') 
a = MyClass(1, 2, foobar=3, anotherfoo=4) 

Esto es similar a la namedtuple de collections Esto le ahorra lleva mucho más trabajo de lo que define esencialmente la misma__init__ método una y otra vez y no lo hace requiere que enturbie su árbol de herencia solo para obtener el mismo método sin volver a escribirlo.

Si es necesario agregar otros métodos, a continuación, puedes crear una base de

MyClassBase = Struct('MyClassBase', 'foo bar') 
class MyClass(MyClassBase): 
    def other_method(self): 
     pass 
0

No hay nada malo con el patrón. Es fácil de leer y fácil de entender (mucho más que muchas otras respuestas aquí).

estoy encontrando a mí mismo escribiendo un montón de clases con constructores como esto

Si usted está recibiendo de escritura de hasta aburridos estos constructores, entonces la solución es hacer que el ordenador lo haga por usted.Por ejemplo, si quieres pasar a ser la codificación con pydev, puede pulsar Ctrl + 1 , Un a make the editor do it for you.

Esta es una solución mucho mejor que pasar el tiempo escribiendo y depurando el código mágico que ofusca lo que realmente está tratando de hacer, que es asignar valores a algunas variables de instancia.

+0

a) el código no es tan mágico b) la programación de copiar y pegar es terrible (casi vale la pena -1) c) si el código para resolver este problema necesita ser depurado, entonces hacerlo es un buen ejercicio (es decir, debería ser capaz de hacerlo bien la primera vez). – aaronasterling

2

Personalmente, me quedaría con la forma en que lo está haciendo actualmente, ya que es mucho menos frágil.

considere el siguiente código con un error tipográfico:

myobject = MyClass(foo=1,bar=2,fobar=3) 

Si utiliza su enfoque original que obtendrá la siguiente comportamiento deseable cuando se intenta crear el objeto:

TypeError: __init__() got an unexpected keyword argument 'fobar' 

Con el kwargs enfoque esto sucede:

>>> myobject.fobar 
3 

Esto parece m e la fuente del tipo de errores que son muy difíciles de encontrar.

Puede validar la lista kwargs para asegurarse de que solo tiene los valores esperados, pero para cuando haya hecho eso y el trabajo para agregar valores predeterminados, creo que será más complejo que su enfoque original.

Cuestiones relacionadas