Экземпляры Python и атрибуты: действительно ли это - ошибка, или я получил ее полностью неправильно?

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

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]

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

Спасибо

5
задан Mirko Rossini 8 March 2010 в 16:37
поделиться

7 ответов

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

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

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

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

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

14
ответ дан 18 December 2019 в 06:34
поделиться

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

class intlist:
    def __init__(self, l=None):
        if l is None:
            self.l = []
        else:
            self.l = l
4
ответ дан 18 December 2019 в 06:34
поделиться

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

def __init__(self,l = []):

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

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

3
ответ дан 18 December 2019 в 06:34
поделиться

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

0
ответ дан 18 December 2019 в 06:34
поделиться

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

1
ответ дан 18 December 2019 в 06:34
поделиться

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

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

1
ответ дан 18 December 2019 в 06:34
поделиться

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

Попробуйте:

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

EDIT: Используйте is not, согласно SilentGhost

1
ответ дан 18 December 2019 в 06:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: