2010-05-24 21 views
6

Duplicar posible:
least astonishment in python: the mutable default argumentPython constructor hace cosas raras con los parámetros opcionales

Quiero entender el comportamiento y las implicaciones de la pitón __init__ constructor. Parece que cuando hay un parámetro opcional e intenta establecer un objeto existente para un nuevo objeto, el valor opcional del objeto existente se conserva y se copia.

vistazo a un ejemplo:

En el siguiente código que estoy tratando de hacer una estructura de árbol con nodos y posiblemente muchos niños. En la primera clase NodeBad, el constructor tiene dos parámetros, el valor y los posibles elementos secundarios. La segunda clase NodeGood solo toma el valor del nodo como parámetro. Ambos tienen un método addchild para agregar un elemento secundario a un nodo.

Al crear un árbol con la clase NodeGood, funciona como se esperaba. Sin embargo, al hacer lo mismo con la clase NodeBad, parece como si un niño solo pudiera agregarse una vez.

El código de abajo resultará en la siguiente salida:

Good Tree 
1 
2 
3 
[<3>] 
Bad Tree 
1 
2 
2 
[<2>, <3>] 

Que Pasa?

Aquí es el ejemplo:

#!/usr/bin/python 
class NodeBad: 
    def __init__(self, value, c=[]): 
    self.value = value 
    self.children = c 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 


class NodeGood: 
    def __init__(self, value): 
    self.value = value 
    self.children = [] 
    def addchild(self, node): 
    self.children.append(node) 
    def __str__(self): 
    return '< %s >' % self.value 
    def __repr__(self): 
    return '< %s >' % self.value 

if __name__ == '__main__': 
    print 'Good Tree' 
    ng = NodeGood(1) # Root Node 
    rootgood = ng 
    ng.addchild(NodeGood(2)) # 1nd Child 
    ng = ng.children[0] 
    ng.addchild(NodeGood(3)) # 2nd Child 

    print rootgood.value 
    print rootgood.children[0].value 
    print rootgood.children[0].children[0].value 
    print rootgood.children[0].children 

    print 'Bad Tree' 
    nb = NodeBad(1) # Root Node 
    rootbad = nb 
    nb.addchild(NodeBad(2)) # 1st Child 
    nb = nb.children[0] 
    nb.addchild(NodeBad(3)) # 2nd Child 

    print rootbad.value 
    print rootbad.children[0].value 
    print rootbad.children[0].children[0].value 
    print rootbad.children[0].children 
+0

duplicado de http://stackoverflow.com/questions/1132941/least-astonishment-in-python -the-mutable-default-argument – sth

Respuesta

12

El problema es, el valor predeterminado de un argumento opcional es una única instancia. Entonces, por ejemplo, si dices def __init__(self, value, c=[]):, esa misma lista [] se pasará al método cada vez que el código de llamada use un argumento opcional.

Por lo tanto, solo debe usar tipos de fecha inmutables como None para el valor predeterminado de un argumento opcional. Por ejemplo:

def __init__(self, value, c=None): 

A continuación, sólo podría crear una nueva lista en el cuerpo del método:

if c == None: 
    c = [] 
+3

El problema solo surge si usa los tipos de datos ** mutables ** como argumentos predeterminados. No hay problema con ningún tipo ** inmutable ** (entero, cadena, tupla, etc.) (no lo nombraría * valor constante *). –

+0

Buen punto, acabo de actualizar mi respuesta. ¡Gracias! –

+0

Merece la pena aclarar que esto no es específico de '__init__', o incluso de las clases en absoluto, pero es cierto para cualquier función. –

Cuestiones relacionadas