Construcción del objeto Python 3: ¿cuál es la forma más pythonica / aceptada?

Teniendo un fondo en Java, que es muy detallado y estricto, creo que la capacidad de mutar objetos de Python para darles campos distintos a los presentados al constructor es realmente “fea”.

Tratando de acostumbrarme a una forma de pensar pythonica, me pregunto cómo debería permitir que se construyan mis objetos .

Mi instinto es tener que pasar los campos en tiempo de construcción , tales como:

def __init__(self, foo, bar, baz=None): self.foo = foo self.bar = bar self.baz = baz 

Pero eso puede volverse demasiado verboso y confuso con muchos campos para pasar. Para superar esto, asumo que el mejor método es pasar un diccionario al constructor, del cual se extraen los campos :

 def __init__(self, field_map): self.foo = field_map["foo"] self.bar = field_map["bar"] self.baz = field_map["baz"] if baz in field_map else None 

El otro mecanismo que se me ocurre es tener campos agregados en otros lugares , como:

 class Blah(object): def __init__(self): pass ... blah = Blah() blah.foo = var1 

Pero como eso se siente demasiado suelto para mí.

(Supongo que el problema en mi cabeza es cómo trato con las interfaces en Python …)

Entonces, para reiterar la pregunta: ¿Cómo debo construir mis objetos en Python? ¿Hay una convención aceptada?

Lo primero que describas es muy común. Algunos usan los más cortos

 class Foo: def __init__(self, foo, bar): self.foo, self.bar = foo, bar 

Tu segundo enfoque no es común, pero una versión similar es esta:

 class Thing: def __init__(self, **kwargs): self.something = kwargs['something'] #.. 

lo que permite crear objetos como

 t = Thing(something=1) 

Esto puede ser modificado para

 class Thing: def __init__(self, **kwargs): self.__dict__.update(kwargs) 

permitiendo

 t = Thing(a=1, b=2, c=3) print ta, tb, tc # prints 1, 2, 3 

Como señala Debilski en los comentarios, el último método es un poco inseguro, puede agregar una lista de parámetros aceptados como este:

 class Thing: keywords = 'foo', 'bar', 'snafu', 'fnord' def __init__(self, **kwargs): for kw in self.keywords: setattr(self, kw, kwargs[kw]) 

Hay muchas variaciones, no hay una norma común que yo sepa.

No he visto muchos de tus field_map en la vida real. Creo que eso solo tendría sentido si también field_map el field_map en otro lugar de tu código.

Con respecto a su tercer ejemplo: aunque no necesita asignarlos (aparte de None ), es una práctica común declarar explícitamente los atributos en el método __init__ , de modo que podrá ver fácilmente qué propiedades tiene su objeto.

Así que lo siguiente es mejor que simplemente tener un método __init__ vacío (también obtendrás una puntuación de pylint más alta para eso):

 class Blah(object): def __init__(self): self.foo = None self.bar = None blah = Blah() blah.foo = var1 

El problema con este enfoque es que su objeto podría estar en un estado no bien definido después de la inicialización, porque aún no ha definido todas las propiedades de su objeto. Esto depende de la lógica de su objeto (la lógica en el código y el significado) y cómo funciona su objeto. Sin embargo, si es el caso, le aconsejo que no lo haga de esta manera. Si su objeto se basa en que foo y bar se definan de manera significativa, realmente debería ponerlos dentro de su método __init__ .

Sin embargo, si las propiedades foo y bar no son obligatorias, puede definirlas posteriormente.

Si la legibilidad de las listas de argumentos es un problema para usted: use argumentos de palabras clave.