python初学,在给对象赋值时,发现当引用对象改变时,赋值对象也同时改变
对象传值
让我们先看一个栗子
a = [0, [1, 2], 3]
b = a
print("a1",a)
print("b1",b)
a[0] = 8
a[1][1] = 9
print("a2", a)
print("b2", b)
预想中print("a2", a)
输出的结果是b2 [0, [1, 2], 3]
但是,实际的输出结果是什么呢?
a1 [0, [1, 2], 3]
b1 [0, [1, 2], 3]
a2 [8, [1, 9], 3]
b2 [8, [1, 9], 3]
在 python 中赋值语句总是建立对象的引用值,而不是复制对象。因此,python 变量更像是指针,而不是数据存储区域
Python 没有「变量」,我们平时所说的变量其实只是「标签」,是引用
正确的复制嵌套元素的方法是进行「深复制」(deep copy),方法是
import copy
a = [0, [1, 2], 3]
b = copy.deepcopy(a)
print("a1",a)
print("b1", b)
a[0] = 8
a[1][1] = 9
print("a2",a)
print("b2", b)
这样输出结果是
a1 [0, [1, 2], 3]
b1 [0, [1, 2], 3]
a2 [8, [1, 9], 3]
b2 [0, [1, 2], 3]
可变&不可变对象
在python中,对象分为两种:可变对象和不可变对象
不可变指的是值的不可变
对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收
可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短
可变对象:
list,set,dict
不可变对象:
int,float,long,str,tuple
看一下栗子
a = 1 #将名字a与内存中值为1的内存绑定在一起
a = 2 #将名字a与内存中值为2的内存绑定在一起,而不是修改原来a绑定的内存中的值,这时,内存中值为1的内存地址引用计数-1,当引用计数为0时,内存地址被回收
b = a #变量b执行与a绑定的内存
b = 3 #创建一个内存值为3的内存地址与变量名字b进行绑定。这是a还是指向值为2的内存地址。
print(a,b) #2,3
具体的参数地址
str
a = 'xianglong.me'
print(id(a)) #140443303134352
a = '1saying.com'
print(id(a)) #140443303131776
# 重新赋值之后,变量a的内存地址已经变了
# 'xianglong.me'是str类型,不可变,所以赋值操作知识重新创建了str '1saying.com'对象,然后将变量a指向了它
list
a_list = [1, 2, 3]
print(id(a_list)) #140443302951680
a_list.append(4)
print(id(a_list)) #140443302951680
# list重新赋值之后,变量a_list的内存地址并未改变
# [1, 2, 3]是可变的,append操作只是改变了其value,变量a_list指向没有变
global关键字
s = 'foo'
d = {'a':1}
def f():
s = 'bar'
d['b'] = 2
f()
print s # foo
print d # {'a': 1, 'b': 2}
为什么修改字典d的值不用global关键字先声明呢?
这是因为,在s = 'bar'
这句中,它是“有歧义的“,因为它既可以是表示引用全局变量s,也可以是创建一个新的局部变量
所以在python中,默认它的行为是创建局部变量,除非显式声明global,global定义的本地变量会变成其对应全局变量的一个别名,即是同一个变量
在d[‘b’]=2这句中,它是“明确的”,因为如果把d当作是局部变量的话,它会报KeyError,所以它只能是引用全局的d,故不需要多此一举显式声明global
但是如果是这样
d = {'a':1}
def f():
d = {}
d['b'] = 2
f()
print d # {'a': 1}
在d = {}
这句,它是”有歧义的“了,所以它是创建了局部变量d,而不是引用全局变量d,所以d[‘b’]=2也是操作的局部变量
append/extend
# coding=utf-8
# 测试utf-8编码
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
list_a = []
def a():
list_a = [1] ## 语句1
a()
print list_a # []
print "======================"
list_b = []
def b():
list_b.append(1) ## 语句2
b()
print list_b # [1]
list_a = [1]
不能改变list_a的值,而list_b.append(1)
却可以,两种方法的差别在哪儿呢?
= 创建了局部变量,而 .append() 或者 .extend() 重用了全局变量