Python类范围和列表

Rus*_*uss 10 python

我仍然是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
Run Code Online (Sandbox Code Playgroud)

运行此结果会产生以下输出:

['Hey!']
1
['Hey!', 'Hey!']
1
Run Code Online (Sandbox Code Playgroud)

很明显,我希望类变量能够得到完全相同的数据,并且输出完全相同......我无法在任何地方找到的是什么使得列表与字符串或数字不同,为什么是列表从后续的第一个实例中引用相同的列表?很明显,我可能误解了某种范围机制或列表创建机制.

Con*_*ion 8

tlayton的答案是故事的一部分,但并不能解释所有事情.

添加一个

print MyClass.mynum
Run Code Online (Sandbox Code Playgroud)

变得更加困惑:).它将打印'0'.为什么?因为行

self.mynum += 1
Run Code Online (Sandbox Code Playgroud)

创建一个实例变量,然后增加它.它不会增加类变量.

mylist的故事是不同的.

self.mylist.append("Hey!")
Run Code Online (Sandbox Code Playgroud)

不会创建列表.它期望存在具有"追加"功能的变量.由于实例没有这样的变量,它最终会引用类中的一个,它确实存在,因为你初始化它.就像在Java中一样,实例可以"隐式地"引用类变量.像"类字段应该由类引用,而不是由实例引用"(或类似的东西;我在Java中看到它已经有一段时间)的警告将是有序的.添加一行

print MyClass.mylist
Run Code Online (Sandbox Code Playgroud)

验证这个答案:).

简而言之:您正在初始化类变量并更新实例变量.实例可以引用类变量,但某些"更新"语句将自动为您创建实例变量.


tla*_*ton 5

你在这里做的不仅仅是创建一个类变量.在Python中,类体中定义的变量既会导致类变量("MyClass.mylist"),也会导致实例变量("a.mylist").这些是单独的变量,而不仅仅是单个变量的不同名称.

但是,当以这种方式初始化变量时,初始值仅被计算一次并传递给每个实例的变量.这意味着,在您的代码中,MyClass的每个实例的mylist变量都指向一个列表对象.

在这种情况下,列表和数字之间的区别在于,与Java一样,当从一个变量传递到另一个变量时,会复制诸如数字之类的原始值.这会导致您看到的行为; 即使变量初始化仅被评估一次,但是当将0传递给每个实例的变量时,也会复制0.但是,作为一个对象,列表没有这样的东西,所以你的append()调用都来自同一个列表.试试这个:

class MyClass():
    def __init__(self):
        self.mylist = ["Hey"]
        self.mynum = 1
Run Code Online (Sandbox Code Playgroud)

这将导致每次创建实例时单独评估值.与Java不同,您不需要在此片段中附带类体声明; __init __()中的赋值用作所需的所有声明.


Ken*_*Ken 5

我相信不同之处在于它+=是一个赋值(与=and 相同+),同时append就地更改了一个对象.

    mylist = []
    mynum = 0
Run Code Online (Sandbox Code Playgroud)

这会在类定义时分配一些类变量.

    self.mylist.append("Hey!")
Run Code Online (Sandbox Code Playgroud)

这会MyClass.mylist通过附加字符串来更改值.

    self.mynum += 1
Run Code Online (Sandbox Code Playgroud)

这是相同的self.mynum = self.mynum + 1,即它分配self.mynum(实例成员).从self.mynum瀑布读取到类成员,因为那时没有该名称的实例成员.