Python类的作用域和列表
Python类的作用域和列表
我对Python还比较陌生,我的面向对象编程经验来自Java。所以我在Python中写的一些代码对我来说很不寻常,给定以下代码:
class MyClass(): mylist = [] mynum = 0 def __init__(self): # 用一些值填充列表。 self.mylist.append("Hey!") # 增加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
显然,我期望类变量得到完全相同的数据,以及相同的输出...但是我似乎找不到任何关于列表与字符串或数字有何不同的地方,为什么列表在后续实例化中引用了第一个实例化的列表?显然,我可能误解了一些作用域机制或列表创建机制...
Python类的作用域和列表问题的原因是,类体中定义的变量会产生一个类变量和一个实例变量。这些是不同的变量,而不仅仅是单个变量的不同名称。
然而,当以这种方式初始化变量时,初始值只会被评估一次,并传递给每个实例的变量。这意味着,在你的代码中,MyClass的每个实例的mylist变量都指向同一个列表对象。
在这种情况下,列表和数字的区别在于,像Java一样,从一个变量传递给另一个变量时,原始值(如数字)会被复制。这就是你看到的行为;即使变量初始化只被评估一次,当传递给每个实例的变量时,0也会被复制。然而,作为一个对象,列表不会这样做,所以你的append()调用都来自同一个列表。可以尝试使用以下代码进行改进:
class MyClass(): def __init__(self): self.mylist = ["Hey"] self.mynum = 1
这将导致每次创建实例时值被单独评估。与Java完全不同,你不需要类体声明来配合这个片段;在__init__()中的赋值就足够了。
“这些是不同的变量,而不仅仅是单个变量的不同名称。”->我不认为这是正确的。尝试执行`id(a.mylist)`和`id(MyClass.mylist)`,看看你会得到什么。
它们是不同的变量,这是对变量和值之间重要区别的描述。这两个id()调用将返回相等的结果,因为a.mylist和MyClass.mylist都指向同一个对象(在评估"mylist = []"行时创建),但它们仍然是独立的;稍后将完全不同的列表分配给其中一个变量不会改变另一个变量的值。
你是对的,这是不正确的。类中的前两行创建了类变量,只有类变量。有关正确的情况,请参阅我的回答。然而,你用来证明这一点的方法不起作用:实例可以引用类变量,并且这两个命令实际上会返回相同的id。然而,当对'mynum'变量重复执行时,它们将返回不同的id。
我明白你的意思。问题在于实例变量是随实例一起创建还是在第一次赋值时创建的。我怀疑你对此可能是正确的(在概念上更合理一些),但我想不出任何测试来确定这个区别。有什么建议吗?
经过进一步调查,我找到了一个适合的测试案例:在创建实例后更改类变量的值会导致实例变量也发生变化。我承认Confusion是正确的。
Python类的作用域和列表的操作
在Python中,类的作用域和列表的操作可能会引起一些困惑。在下面的代码示例中,我们可以看到这个问题的出现原因以及解决方法。
mylist = [] mynum = 0
上面的代码片段定义了一个空列表`mylist`和一个整数变量`mynum`。
self.mylist.append("Hey!")
这一行代码通过在`mylist`列表中添加一个字符串来改变了`MyClass.mylist`的值。
self.mynum += 1
这一行代码相当于`self.mynum = self.mynum + 1`,即给`self.mynum`(实例成员)赋值。由于在此时没有同名的实例成员,所以从`self.mynum`读取的值会从类成员中取得。
从技术上讲,`MyClass.mylist`不是一个值,而是一个变量。调用`self.mylist.append()`不会改变`MyClass.mylist`的值,而是改变了两个变量所引用的列表本身。
+1. 很好的回答。你说“值”的时候是什么意思?我认为调用`self.mylist.append("Hey!")`会改变`MyClass.mylist`的值。
根据我对这个上下文中“值”的理解,`MyClass.mylist`的值是它所引用的列表。调用`append()`不会使`mylist`引用一个不同的列表,而是改变了列表本身。不过,我可能对“值”的解释过于严谨了。
Python中的类变量和列表的作用域问题
在Python中,类变量是在整个类中共享的变量,而实例变量是每个实例独立拥有的变量。然而,在使用类变量和列表时,可能会出现一些意外的问题。
问题的出现原因:
- 在使用self.mynum += 1
时,这实际上是在创建一个实例变量并递增它,而不是递增类变量。因此,打印MyClass.mynum
时,结果为0。
- 在使用self.mylist.append("Hey!")
时,它并不会创建一个新的列表。它期望一个具有'append'函数的变量存在。由于实例没有这样的变量,它会引用类中的变量,因为在初始化时已经创建了它。
问题的解决方法:
- 如果想要递增类变量而不是实例变量,可以直接使用类名来引用类变量,而不是使用self
。
- 如果想要在实例中创建一个新的列表,可以在初始化方法中使用self.mylist = []
来创建一个实例变量,然后再使用self.mylist.append("Hey!")
来添加元素。
示例代码:
class MyClass: mynum = 0 mylist = [] def __init__(self): self.mylist = [] self.mylist.append("Hey!") MyClass.mynum += 1 def print_variables(self): print("Instance variable mynum:", self.mynum) print("Class variable mynum:", MyClass.mynum) print("Instance variable mylist:", self.mylist) print("Class variable mylist:", MyClass.mylist) obj1 = MyClass() obj1.print_variables() obj2 = MyClass() obj2.print_variables()
输出结果:
Instance variable mynum: 1 Class variable mynum: 1 Instance variable mylist: ['Hey!'] Class variable mylist: [] Instance variable mynum: 2 Class variable mynum: 2 Instance variable mylist: ['Hey!'] Class variable mylist: []
通过以上代码和解释,我们可以更好地理解Python中类变量和实例变量的作用域问题。