Создание объектов в al oop Python - загадочное поведение - PullRequest
1 голос
/ 16 июня 2020

Я просмотрел решения вопросов ( здесь , здесь и здесь ), которые выглядят похожими, но не похожими. Я до сих пор не могу понять, что здесь происходит.

class Page:
    def __init__(self, l = []):
        self.lines = l

    def __repr__(self):
        return str(self.lines)

class Line:
    def __init__(self, string=None):
        self.str = string

    def __repr__(self):
        return str(self.str)


if __name__ == '__main__':
    data = [[1, 2, 3], [4, 5, 6]]
    pages = []
    for row in data:
        page = Page()
        #print(page)
        #print(id(page))
        for x in row:
            line = Line(str(x))
            page.lines.append(line)
        pages.append(page)
print('Pages: ', pages)

Ожидаемый ответ:

Pages: [1, 2, 3], [4, 5, 6]

Вместо этого я получаю

Pages: [[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]]

Я распечатал переменную page и вижу, что она уже заполнена когда самый внешний l oop находится на второй итерации. Но как? Разве мне не следует получить новый пустой объект?

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

Спасибо!

Ответы [ 2 ]

2 голосов
/ 16 июня 2020

def __init__(self, l = []): вроде создает глобальное значение по умолчанию l, которое вы измените позже (page.lines относится к этому глобальному массиву, он не воссоздается при каждом вызове).

Чуть лучшая реализация:

class Page:
    def __init__(self, l = None):
        self.lines = l if l else []
0 голосов
/ 16 июня 2020

Могу я предложить вместо этого что-то вроде следующего?

def main():
    data = [[1, 2, 3], [4, 5, 6]]
    pages = []
    for row in data:
        page = Page()
        print(page)
        print(id(page))
        for x in row:
            line = Line(str(x))
            page.append(line)
        pages.append(page)
    print('Pages:', pages)


class Page:
    def __init__(self, lines=None):
        if lines is None:
            lines = []
        if not isinstance(lines, list):
            raise TypeError('argument must by of type list')
        if not all(isinstance(item, Line) for item in lines):
            raise TypeError('list must only contain items of type Line')
        self.__lines = lines

    def __repr__(self):
        return f'{type(self).__name__!s}({self.__lines!r})'

    def append(self, line):
        if not isinstance(line, Line):
            raise TypeError('argument must be of type Line')
        self.__lines.append(line)


class Line:
    def __init__(self, text=None):
        if text is None:
            text = ''
        if not isinstance(text, str):
            raise TypeError('argument must be of type str')
        self.__text = text

    def __repr__(self):
        return f'{type(self).__name__!s}({self.__text!r})'


if __name__ == '__main__':
    main()

Проблема, как указывает Green_Wizard , заключается в том, что ваш инициализатор для вашего класса Page разделяет список между всеми экземплярами это создает. Один из способов исправить это - потребовать от вызывающего класса всегда передавать список для хранения ваших страниц. В противном случае вы можете сделать что-то похожее на то, что показано в приведенном выше коде.

...