函数传参-可变,不可变类型
转载请标明出处(http://blog.csdn.net/lis_12/article/details/52693333)
1.变量类型
首先要介绍下,Python变量的类型.Python一切皆为对象,一切皆为对象的引用
分类 | 类型 | 定义 |
---|---|---|
可变对象 | list,dict | 对象的内容可变 |
不可变对象 | 数字,str,tuple | 对象的内容不可变 |
同类型之间的 “+”,“+=”
operate | 地址,id(var) | 地址的值 |
---|---|---|
a =1 | id(a) | 1 |
A = 1 | id(A) | 1 |
a += 1 | id(a) | 2 |
a = a + a | id(a) | 3 |
b = [1,2] | id(b) | 4 |
B = [1,2] | id(B) | 5 |
b += [3] | id(b) | 4(地址未改变) |
b = b + b | id(b) | 6(地址改变) |
不可变对象
-
因为int为不可变对象,a,A均是对1的引用,a,A在内存中均指向一个值为1的内存地址,即a,A均指向的是同一个地址.
-
同样因为a为不可变对象,因为值得改变,a的引用也改变了,a指向了值为2的内存地址,所以a的地址改变了.
-
优点
不可变数据类型的优点就是内存中不管有多少个引用,相同的对象只占用了一块内存,但是它的缺点就是当需要对变量进行运算从而改变变量引用的对象的值时,由于是不可变数据类型,所以必须创建一个新的对象,这样就会使得一次次的改变创建了一个个新的对象,不过不再使用的内存会被垃圾回收器回收.
可变对象
- 从上表可知,b,B的值是相同的,但是其地址是不同的.所以对于可变类型来说,具有相同值得不同对象,在内存中保留了多个同样的值.
- 对可变类型进行’+='操作,不会改变b引用的地址值,此时是在地址后面又扩充了内存,改变了地址里面存放的值,所以可变数据类型的变量其值是可变的,值得变化不会新建对象,即地址是不变的.<可以这么理解,对list的扩充,相当于在原地址后面又申请了内存,但是首地址还是未改变,有点像c++里面的vector>.(对它的操作就和定位了指针地址一样,在内存里进行修改)
- 对可变类型进行’+’,’=‘操作都是新建了一个对象,引用也会随之改变.(如果’+‘不懂的话,想想c++中重载’+’,’='等操作,都是先新建一个变量存储结果,然后将其赋予原变量).
ps:以下这种操作地址不会变,append只是在原地址后面又申请了一段内存,但是首地址并未改变.
b = [1,2] a = [3,5] | id(b) = 1 |
---|---|
b.append(a) | id(b) = 1 |
设置可变/不可变类型的原因
因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误.
此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读取数据时不会有任何问题.我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象.
2.函数传参
下面开始正题,感觉看完了可变与不可变类型这里都不用看了- -!
Python函数中的参数都是引用!!!请谨记…
code
a = 1
def fun(a):
a = 2
fun(a)
print a # 1
'''fun(a)复制了一份a的引用(把fun中的参数换成b更容易理解一些..),此时函数中的a与外部的a没有半毛钱关系,只不过都引用了1而已,此时函数中的a改变了引用,丝毫影响不到外部的a'''
a = []
def fun(b):
b.append(1)
fun(a)
print a # [1]
'''这里还是换成b吧....,同理,fun中的b为外面a的一份引用,在同一个地址后面扩充了肯定会影响到外部的a啊,这里说的不好,自行体会- -'''
a = []
def fun(b):
b = [1] #b引用了新建对象[1],与外部的a没有关系喽
fun(a)
print a #[]
'''省略好多字...b为[1]的引用,丝毫影响不到a- -!'''
#贱人版
def fun(a = []):
a.append(1)
print a
a = [1,2,3]#此时a为[1,2,3]处的引用,不会影响上面的默认参数
fun()#[1]
fun()#[1,1]
'''即使是默认参数也只引用一处哦,不会引用多处哦,默认参数最好不要设置成可变类型'''
教训
下次尽量不要把可变类型的变量设为默认参数,不然怎么挂的都不知道…(尽量使用不可变类型哦)
如果偏要用的话可以参考以下:
def fun(a = []):
if len(a) > 0:
a = []
a.append(1)
print a