Python 面向对象编程与列表

5 投票
3 回答
3952 浏览
提问于 2025-04-15 22:57

我刚接触Python和面向对象编程,遇到了一些问题。以下是我的代码:

class Tree:

    root = None;
    data = [];

    def __init__(self, equation):
        self.root = equation;

    def appendLeft(self, data):
        self.data.insert(0, data);

    def appendRight(self, data):
        self.data.append(data);

    def calculateLeft(self):
        result = [];
        for item in (self.getLeft()):
            if (type(item) == type(self)):
                data = item.calculateLeft();
            else:
                data = item;
            result.append(item);
        return result;

    def getLeft(self):
        return self.data;

    def getRight(self):
        data = self.data;
        data.reverse();
        return data;

tree2 = Tree("*");
tree2.appendRight(44);
tree2.appendLeft(20);

tree = Tree("+");
tree.appendRight(4);
tree.appendLeft(10);
tree.appendLeft(tree2);

print(tree.calculateLeft());

看起来tree2和tree在共享一个叫“data”的列表?

目前我希望输出的结果是类似于[[20,44], 10, 4],但是当我

tree.appendLeft(tree2) 

运行时却出现了RuntimeError: maximum recursion depth exceeded的错误。而且即使我不使用appendLeft(tree2),输出的结果却是[10, 20, 44, 4](!!!)。我到底漏掉了什么呢?我使用的是Portable Python 3.0.1。

谢谢

3 个回答

3

当你以以下方式定义属性时:

class Tree:
    root = None
    data = []

这个空列表对象是在你定义类的时候就创建了,而不是在你创建新实例的时候。它是一个类属性,而不是实例属性。这意味着,Tree.root在所有实例中都是同一个对象:

class Tree:
    root = None
    data = []

t1 = Tree()
t2 = Tree()

print id(t1.data) == id(t2.data) # is True, they are the same object

为了得到你期望的行为,把空列表的创建移动到__init__函数中,这个函数只有在你创建新实例时才会被调用,并且只会影响那个实例(因为它是赋值给self的):

class Tree:
    def __init__(self):
        self.root = None
        self.data = []

t1 = Tree()
t2 = Tree()

print id(t1.data) == id(t2.data) # False, they are different objects

这个问题解释了为什么这种行为可能会很有用

4

把根节点和数据放到 __init__ 的定义里。现在你把它们定义成了类属性,这样的话,所有的 Tree 类的实例都会共享这些属性。当你创建两个树(treetree2)的时候,它们会共享同一个列表,通过 self.data 来访问。为了让每个实例都有自己的属性,你需要把声明移到 __init__ 函数里。

def __init__(self, equation):
    self.root = equation
    self.data = []

另外,使用

        if isinstance(item,Tree):        # This is True if item is a subclass of Tree

而不是

        if (type(item) == type(self)):   # This is False if item is a subclass of Tree

并且把

data = self.data

改成

data = self.data[:]

getRight 函数里。当你写 data = self.data 的时候,变量名 data 指向的其实和 self.data 是同一个列表。这样一来,当你反转 data 的时候,self.data 也会被反转。如果你只想反转 data,你需要复制这个列表。self.data[:] 使用切片的方式来返回这个列表的一个副本。要注意的是,self.data 的元素可能是 Tree 对象,而 self.dataself.data[:] 可能包含相同的元素。我觉得你的代码不需要复制这些元素,但如果需要的话,你就得递归地复制 self.data

def getRight(self):
    data = self.data[:]
    data.reverse()
    return data
11

问题在于你把 data 声明成了一个类变量,这样这个类的所有实例都会共享同一个列表。正确的做法是在你的 __init__ 方法里写 self.data = []

另外,去掉那些分号吧。它们没有必要,会让你的代码看起来很乱。

撰写回答