在循环 Python 中创建对象——令人费解的行为

Creating objects in a loop Python -- puzzling behavior

我查看了看起来相似但实际上不同的问题 (here, here and here) 的解决方案。我仍然无法理解这里发生的事情。

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 变量并看到当最外层循环处于第二次迭代时它已经被填充。但是怎么办?我不应该得到一个新的空对象吗?

我不是在寻找解决问题或获得预期输出的方法,我知道一些方法。我想了解为什么会得到这个输出。

谢谢!

def __init__(self, l = []): 有点创建 l 的全局默认值,稍后可以更改(page.lines 引用此全局数组,它不会在每次调用时重新创建)。

稍微好一点的实现:

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

我可以推荐类似下面的内容吗?

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()

的问题是您的 Page class 的初始化程序在它创建的所有实例之间共享列表。解决此问题的一种方法是要求 class 的调用者始终传入一个用于存储页面的列表。否则,您可以执行类似于上面代码中所示的操作。