Métodos de Python: los valores predeterminados de los parámetros se evalúan UNA VEZ

He encontrado un problema extraño con las subclasificaciones y actualizaciones de diccionarios en las Clases de Nuevo Estilo:

Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on win32 >>> class a(object): ... def __init__(self, props={}): ... self.props = props ... >>> class b(a): ... def __init__(self, val = None): ... super(b, self).__init__() ... self.props.update({'arg': val}) ... >>> class c(b): ... def __init__(self, val): ... super(c, self).__init__(val) ... >>> b_inst = b(2) >>> b_inst.props {'arg': 2} >>> c_inst = c(3) >>> c_inst.props {'arg': 3} >>> b_inst.props {'arg': 3} >>> 

En la depuración, en la segunda llamada ( c(3) ) puede ver que dentro de a constructor self.props ya es igual a {'arg': 2} , y cuando se llama b constructor después de eso, se convierte en {'arg': 3} para ambos objetos!

Además, el orden de los constructores que llaman es:

  a, b # for b(2) c, a, b # for c(3) 

Si cambia self.props.update() con self.props = {'arg': val} en b constructor, todo estará bien y actuará como se espera.

Pero realmente necesito actualizar esta propiedad, no reemplazar

props no deberían tener un valor predeterminado como ese. Haga esto en su lugar:

 class a(object): def __init__(self, props=None): if props is None: props = {} self.props = props 

Este es un python común “gotcha” .

Su problema está en esta línea:

 def __init__(self, props={}): 

{} es un tipo mutable. Y en python, los valores predeterminados de los argumentos solo se evalúan una vez. ¡Eso significa que todas tus instancias están compartiendo el mismo objeto de diccionario!

Para arreglar esto, cambiarlo a:

 class a(object): def __init__(self, props=None): if is None: props = {} self.props = props 

La versión corta: Haz esto:

 class a(object): def __init__(self, props=None): self.props = props if props is not None else {} class b(a): def __init__(self, val = None): super(b, self).__init__() self.props.update({'arg': val}) class c(b): def __init__(self, val): super(c, self).__init__(val) 

La versión larga:

La definición de la función se evalúa exactamente una vez, por lo que cada vez que la llama se usa el mismo argumento predeterminado. Para que esto funcione como esperaba, los argumentos predeterminados deberían evaluarse cada vez que se llama a una función. Pero, en cambio, Python genera un objeto de función una vez y agrega los valores predeterminados al objeto (como func_obj.func_defaults )