Python 面向对象编程与列表
我刚接触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 个回答
当你以以下方式定义属性时:
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
这个问题解释了为什么这种行为可能会很有用
把根节点和数据放到 __init__
的定义里。现在你把它们定义成了类属性,这样的话,所有的 Tree 类的实例都会共享这些属性。当你创建两个树(tree
和 tree2
)的时候,它们会共享同一个列表,通过 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.data
和 self.data[:]
可能包含相同的元素。我觉得你的代码不需要复制这些元素,但如果需要的话,你就得递归地复制 self.data
。
def getRight(self):
data = self.data[:]
data.reverse()
return data
问题在于你把 data
声明成了一个类变量,这样这个类的所有实例都会共享同一个列表。正确的做法是在你的 __init__
方法里写 self.data = []
。
另外,去掉那些分号吧。它们没有必要,会让你的代码看起来很乱。