Экземпляры и атрибуты Python: это ошибка или я все понял неправильно? - PullRequest
5 голосов
/ 08 марта 2010

Предположим, у вас есть что-то вроде этого:

class intlist:
        def __init__(self,l = []):
                self.l = l
        def add(self,a):
                self.l.append(a)

def appender(a):
        obj = intlist()
        obj.add(a)
        print obj.l

if __name__ == "__main__":
        for i in range(5):
                appender(i)

Функция создает экземпляр intlist и вызывает в этом новом экземпляре метод, добавляемый к атрибуту экземпляра l.

Как получается вывод этого кода:

[0]

[0, 1]

[0, 1, 2]

[0, 1, 2, 3]

[0, 1, 2, 3, 4]

? Если я переключу

obj = intlist()

с

obj = intlist(l=[])

Я получаю желаемый вывод

[0]

[1]

[2]

[3] * * одна тысяча тридцать два

[4] * 1 034 *

Почему это происходит?

Спасибо

Ответы [ 7 ]

14 голосов
/ 08 марта 2010

Ах, вы попали в одну из распространенных ошибок Python: значения по умолчанию вычисляются один раз, а затем используются повторно. Таким образом, каждый раз, когда вызывается __init__, используется тот же список .

Это Pythonic способ делать то, что вы хотите:

def __init__(self, l=None):
    self.l = [] if l is None else l

Для получения дополнительной информации, посмотрите документы Python (особенно около трех абзацев после этого заголовка).

Редактировать : В другом ответе есть гораздо лучшее описание .

4 голосов
/ 08 марта 2010

Когда вы устанавливаете значение по умолчанию l=[] в __init__, вы фактически используете один и тот же список каждый раз.Вместо этого вы можете попробовать что-то вроде:

class intlist:
    def __init__(self, l=None):
        if l is None:
            self.l = []
        else:
            self.l = l
3 голосов
/ 08 марта 2010

Проблема в том, что когда вы говорите

def __init__(self,l = []):

Вы говорите Python использовать один и тот же список, [], для каждого вызова конструктора. Поэтому каждый раз, когда вызывается obj = intlist(), к нему добавляется один и тот же список.

Вместо этого вы должны установить для l значение по умолчанию None, которое является скаляром (поэтому ваш код будет работать так, как ожидается, если он будет использоваться несколько раз). Затем, если l равно None, инициализируйте нового члена класса как []. В противном случае просто присвойте переменную-член l.

1 голос
/ 08 марта 2010

Для получения дополнительной информации я предлагаю прочитать это: http://effbot.org/zone/default-values.htm

1 голос
/ 08 марта 2010

obj = intlist() вызывает вашу функцию __init__(), которая использует один и тот же массив для каждого экземпляра класса.

obj = intlist(l=[]) создает новый массив для каждого экземпляра.

1 голос
/ 08 марта 2010

Это происходит потому, что все вызовы вашего метода __init__ имеют один и тот же список по умолчанию.

Попробуйте:

class intlist:
        def __init__(self, l):
                self.l = l if (l is not None) else []
        def add(self,a):
                self.l.append(a)

РЕДАКТИРОВАТЬ: Используйте is not, за SilentGhost

0 голосов
/ 08 марта 2010

Будьте осторожны с параметрами по умолчанию для типов, таких как списки и диктовки. Каждый экземпляр intlist получает один и тот же объект списка из параметра по умолчанию.

...