我仍然是Python的新手,我的OO经验来自Java。所以我有一些我用Python编写的代码,对我来说非常不寻常,给出以下代码:
class MyClass():
mylist = []
mynum = 0
def __init__(self):
# populate list with some value.
self.mylist.append("Hey!")
# increment mynum.
self.mynum += 1
a = MyClass()
print a.mylist
print a.mynum
b = MyClass()
print b.mylist
print b.mynum
运行此结果会产生以下输出:
['Hey!']
1
['Hey!', 'Hey!']
1
显然,我希望类变量能够产生完全相同的数据,并且输出完全相同......我无法在任何地方找到的是使列表与字符串或数字不同的原因,为什么是从后续的第一个实例中引用相同列表的列表?显然,我可能误解了某种范围机制或列表创建机制。
答案 0 :(得分:8)
tlayton的答案是故事的一部分,但并不能解释所有事情。
添加
print MyClass.mynum
变得更加困惑:)。它将打印'0'。为什么?因为行
self.mynum += 1
创建实例变量,然后增加它。它不会增加 class 变量。
mylist的故事是不同的。
self.mylist.append("Hey!")
不会创建列表。它期望存在具有“追加”功能的变量。由于实例没有这样的变量,因此最终会引用类中的一个,确实存在,因为您已初始化它。就像在Java中一样,实例可以“隐式地”引用类变量。像“类字段应该由类引用,而不是由实例引用”(或类似的东西;自从我在Java中看到它以来已经有一段时间)的警告将是有序的。添加一行
print MyClass.mylist
验证这个答案:)。
简而言之:您正在初始化类变量并更新实例变量。实例可以引用类变量,但某些“更新”语句会自动为您创建实例变量。
答案 1 :(得分:5)
您在这里所做的不仅仅是创建一个类变量。在Python中,类体中定义的变量既导致类变量(“MyClass.mylist”),又导致实例变量(“a.mylist”)。这些是单独的变量,而不仅仅是单个变量的不同名称。
但是,当以这种方式初始化变量时,初始值仅计算一次并传递给每个实例的变量。这意味着,在您的代码中,MyClass的每个实例的mylist变量都引用一个列表对象。
在这种情况下,列表和数字之间的区别在于,与Java一样,当从一个变量传递到另一个变量时,会复制诸如数字之类的原始值。这会导致您看到的行为;即使变量初始化仅被计算一次,但是当将0传递给每个实例的变量时,也会复制0。但是,作为一个对象,列表没有这样的东西,所以你的append()调用都来自同一个列表。试试这个:
class MyClass():
def __init__(self):
self.mylist = ["Hey"]
self.mynum = 1
这将导致每次创建实例时单独评估值。与Java不同,您不需要在此片段中附带类体声明; __init __()中的赋值作为所需的所有声明。
答案 2 :(得分:5)
我认为不同之处在于+=
是一项任务(与=
和+
相同),而append
就地更改了一个对象。
mylist = []
mynum = 0
这会在类定义时分配一些类变量。
self.mylist.append("Hey!")
这会通过附加字符串来更改值MyClass.mylist
。
self.mynum += 1
这与self.mynum = self.mynum + 1
相同,即它指定self.mynum
(实例成员)。从self.mynum
读取属于类成员,因为那时没有该名称的实例成员。