1 import sys
  2 
  3 class dummy(object):
  4     def __init__(self, val):
  5         self.val = val
  6 
  7 class myobj(object):
  8     def __init__(self, resources):
  9         self._resources = resources
 10 
 11 class ext(myobj):
 12     def __init__(self, resources=[]):
 13         #myobj.__init__(self, resources)
 14         self._resources = resources
 15 
 16 one = ext()
 17 one._resources.append(1)
 18 two = ext()
 19 
 20 print one._resources
 21 print two._resources
 22 
 23 sys.exit(0)

Esto imprimirá la referencia al objeto asignado a one._resources para los objetos one y two. Pensaría que two sería una matriz vacía, ya que está claramente configurada como tal si no está definida al crear el objeto. Descomentar myobj.__init__(self, resources) hace lo mismo. Usar super(ext, self).__init__(resources) también hace lo mismo.

La única forma en que puedo hacer que funcione es si uso lo siguiente:

two = ext(dummy(2))

No debería tener que establecer manualmente el valor predeterminado al crear el objeto para que esto funcione. O tal vez lo hago. ¿Alguna idea?

Intenté esto usando Python 2.5 y 2.6.

1
user185983 8 oct. 2009 a las 01:45

5 respuestas

La mejor respuesta

Deberías cambiar

def __init__(self, resources=[]):
    self._resources = resources

Para

def __init__(self, resources=None):
    if resources is None:
        resources = []
    self._resources = resources

Y todo será mejor Este es un detalle en la forma en que se manejan los argumentos predeterminados si son mutables. Hay más información en la sección de discusión de esta página.

7
Peter 7 oct. 2009 a las 21:51

Este es un conocido Python gotcha.

Debe evitar el uso de un objeto mutable en la llamada de una función / método.

Los objetos que proporcionan los valores predeterminados no se crean en el momento en que se llama a la función / método . Se crean en el momento en que se ejecuta la instrucción que define la función . (Consulte la discusión en Argumentos predeterminados en Python: dos errores fáciles : "Las expresiones en los argumentos predeterminados se calculan cuando se define la función, no cuando se llama". )

Este comportamiento no es una verruga en el lenguaje Python. Realmente es una característica, no un error. Hay momentos en los que realmente desea utilizar argumentos predeterminados mutables. Una cosa que pueden hacer (por ejemplo) es conservar una lista de resultados de invocaciones anteriores, algo que podría ser muy útil.

Pero para la mayoría de los programadores, especialmente los Pythonistas principiantes, este comportamiento es una trampa. Entonces, para la mayoría de los casos, adoptamos las siguientes reglas.

  • Nunca use un objeto mutable , es decir: una lista, un diccionario o una instancia de clase, como el valor predeterminado de un argumento.
  • Ignora la regla 1 solo si realmente, realmente, REALMENTE sabes lo que estás haciendo.

Entonces ... siempre planeamos seguir la regla # 1. Ahora, la pregunta es cómo hacerlo ... cómo codificar functionF para obtener el comportamiento que queremos.

Afortunadamente, la solución es sencilla. Los objetos mutables utilizados como predeterminados se reemplazan por None, y luego los argumentos se prueban para None.


Entonces, ¿cómo se puede hacer correctamente? Una solución es evitar el uso de valores predeterminados mutables para los argumentos . Pero esto no es satisfactorio, ya que de vez en cuando una nueva lista es un valor predeterminado útil. Hay algunas soluciones complejas, como definir un decorador para funciones que copia en profundidad todos los argumentos. Esta es una exageración, y el problema se puede resolver fácilmente de la siguiente manera:

class ext(myobj):
    def __init__(self, resources=None):
        if not resources:
            resources = []
        self._resources = resources
0
Community 20 jun. 2020 a las 09:12

De http://docs.python.org/3.1/tutorial/controlflow.html:

El valor predeterminado se evalúa solo una vez. Esto marca la diferencia cuando el valor predeterminado es un objeto mutable, como una lista, diccionario o instancias de la mayoría de las clases.

1
RossFabricant 7 oct. 2009 a las 21:52

Lea esta respuesta para obtener una discusión sobre cómo configurar una clase desde __init__(). Te has encontrado con una peculiaridad conocida de Python: estás intentando configurar una mutable, y tu mutable se está evaluando una vez cuando se compila __init__(). La solución estándar es:

class ext(myobj):
    def __init__(self, resources=None):
        if resources is None:
            resources = []
        #myobj.__init__(self, resources)
        self._resources = resources
2
Community 23 may. 2017 a las 12:14

Su problema es que el valor predeterminado se evalúa en el momento de la definición de la función. Esto significa que el mismo objeto de lista se comparte entre instancias. Vea la respuesta a esta pregunta para más discusión.

6
Community 23 may. 2017 a las 12:23