聊聊python的变量问题

Python的参数作用范围

我们看看下面两个例子:

a = 'hello'
def fun(a):
    a = 'lol'
fun(a)
print a  # 'hello'

a=[]
def fun(a):
    a.append(1)
fun(a)
print a  # [1]

为什么第一个例子print出来的是’hello’而不是’lol 呢?
为什么第二个例子print出来却是[1] 呢?

我们通过id() 来查看内存的引用:

a = 'hello'
def fun(a):
    print "point",id(a)   # point 213224723696912
    a = 2
    print "re-point",id(a), id(2)   # re-point 2132244845844160 2132244845844160
print "point",id(a), id(1)  # point 213224723696912 213224723696912
fun(a)
print a  # 'hello'

可以看到,在执行完a=’hello’之后,a的引用中保存的值,即内存地址发生变化,由原来’hello’对象的所在的地址变成了’lol这个实体对象的内存地址。

而第2个例子a引用保存的内存值就不会发生变化:

a = []
def fun(a):
    print "point",id(a)  # point 53629256
    a.append(1)
print "point the same",id(a)     # point the same 53629256
fun(a)
print a  # [1]

这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。

当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.
而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.

在类变量和实例变量中也有类似的例子:

class Person:
    name="aaa"

p1=Person()
p2=Person()
p1.name="bbb"
print p1.name  # bbb
print p2.name  # aaa
print Person.name  # aaa

类变量就是供类使用的变量,实例变量就是供实例使用的.

这里p1.name=”bbb”是实例调用了类变量,这其实和上面第一个问题一样,就是函数传参的问题,p1.name一开始是指向的类变量name=”aaa”,
但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了.

可以看看下面的例子:

class Person:
    name=[]

p1=Person()
p2=Person()
p1.name.append(1)
print p1.name  # [1]
print p2.name  # [1]
print Person.name  # [1]