2012-03-15 13 views
33

En Python, supongamos que tengo una clase, Circle, que hereda de Shape. La forma necesita coordenadas xey, y, además, el círculo necesita un radio. Quiero ser capaz de inicializar Círculo haciendo algo como,¿Es propio .__ dict __. Actualización (** kwargs) estilo bueno o pobre?

c = Circle(x=1., y=5., r=3.) 

Círculo hereda de forma, por lo que necesito utilizar argumentos con nombre de __init__, debido a diferentes clases requieren diferentes constructores. Podría configurar manualmente x, y y r.

class Shape(object): 
    def __init__(self, **kwargs): 
     self.x = kwargs['x'] 
     self.y = kwargs['y'] 

class Circle(Shape): 
    def __init__(self, **kwargs): 
     super(Circle, self).__init__(**kwargs) 
     self.r = kwargs['r'] 

o, podría tener los atributos de mi círculo establecer automáticamente self.__dict__.update(kwargs)

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

class Circle(Shape): 
    def __init__(self, **kwargs): 
     super(Circle, self).__init__(**kwargs) 

La ventaja de esto es que hay menos código y que no es necesario para mantener repetitivo como self.foo = kwargs['foo']. La desventaja es que no es obvio qué argumentos son necesarios para Circle. ¿Esto se considera un truco o es este buen estilo (siempre que la interfaz con Circle esté bien documentada)?


Gracias, a todos, por sus respuestas reflexivas. El truco self.__dict__.update(**kwargs) me ha sido útil para experimentar con la organización de mi código, pero me aseguraré de que lo reemplace con la aprobación adecuada de los argumentos explícitamente y la comprobación clara de errores en el código de producción.

+0

Creo que esto le permitiría a un llamador sobrecargar métodos ... pero si usted es la única persona que usa el código y no está preocupada por el comportamiento malicioso, probablemente esté bien. –

+0

@JoranBeasley - Sí, pero si lo has hecho, esencialmente estás incumpliendo las reglas, y es tu culpa. –

+2

Te estás perdiendo 'self'. –

Respuesta

21
class Shape(object): 
    def __init__(self, x=None, y=None): 
     self.x = x 
     self.y = y 

class Circle(Shape): 
    def __init__(self, r=None, **kwargs): 
     super(Circle, self).__init__(**kwargs) 
     self.r = r 

Y esto es todo. No use **kwargs cuando realmente no los necesite.

¿Esto se considera un truco o es este buen estilo (siempre y cuando la interfaz de Circle esté bien documentada)?

Cuando usted tiene una opción entre escribir un código simple, comprensible y código de dolor de cabeza + docstrings agradables, que en realidad no tenemos ninguna opciones, usted sólo tiene que ir y escribir auto-documentado código simple, :)

+0

hay muchas cosas perfectamente razonables veces para usar ** lo que sea especialmente cuando está sobrecargando otras clases –

+1

@JoranBeasley - Haga clic de nuevo para deshacerlo. –

+0

¿Dónde está 'self'? –

2

Si quisiera que fuera más obvio, podría hacer que Circle.__init__ realice algunos controles de cordura en los argumentos. Es de suponer que usted verificará para asegurarse de que todos los argumentos están ahí, y posiblemente plantee errores para argumentos sin sentido.

Probablemente pueda incluso hacer una función de decorador o ayudante en Shape para hacer esto por usted. Algo como esto:

class Circle(Shape): 
    def __init__(self, **kwargs): 
     self.check(kwargs, 'x', 'y', 'r') 
     super(Circle, self).__init__(**kwargs) 

.check se llevaría a cabo en Shape y esencialmente sólo verifica que todos los argumentos están en kwargs, y, posiblemente, de que no hay los extras son (lo siento, no hay código para que uno - se puede averiguarlo por su cuenta). Incluso podría tener sobrecargas de subclases para verificar argumentos opcionales, que es posible que desee manejar de forma diferente que otros argumentos (es decir, darles un valor predeterminado que de otra manera no se asignaría en Shape.__init__.

De lo contrario, si documenta su interfaz, y funciona de la forma en que está documentado, siempre está bien. Cualquier otra cosa que hagas para que funcione de la manera en que lo "esperamos" (lanzando excepciones para argumentos incorrectos) es una bonificación.

+0

¿Cómo sabes que ".check' está implementado en' Shape' ". ¿Eres el maestro de OP? –

+1

@StevenRumbalski - Sería el profesor de informática más joven de la historia si fuera así. Lo que quería decir es que el OP podría implementar 'Shape.check' y luego usarlo en todas las subclases. Voy a aclarar eso. –

16

Yo diría que el primer método es definitivamente preferible, porque explicit is better than implicit.

Considere lo que sucedería si cometiera un error tipográfico al inicializar un círculo, algo así como Circle(x=1., y=5., rr=3.). Desea ver este error de inmediato, lo que no sucedería con __dict__.update(kwargs).

8

Si desea asignar automáticamente, que sugieren el siguiente enfoque:

def __init__(self, **kwargs): 
    for key, value in kwargs.iteritems(): 
     setattr(self, key, value) 

que, en términos de estilo, está en algún lugar entre la escritura y la piratería de forma explícita por sí mismo utilizando self.__dict__.

+0

Prefiero ver 'self .__ dict __. Update (kwargs)' que este (probablemente menos eficiente) forma más larga para lograr el mismo resultado. –

+5

Esto tendría la ventaja de trabajar con descriptores. Trabajar directamente con '__dict__' es una mala idea por esa sola razón. –

+3

¿Podría ayudarme a entender por qué esto funciona mejor con los descriptores? –

Cuestiones relacionadas