python中的引用
C++中的指针和引用 ,可以参考博客https://www.cnblogs.com/heyonggang/archive/2012/12/13/2815730.html问题导入?如果你使⽤Python声明了⼀个变量并同时为其赋值,即a = 20,你是否将这条语句简单理解为:1. 计算机开辟了⼀块地址别名为a的内存;2. 往这块内存存储了数据20?如果你对上述问题的回答为是,那么你对诸如“Pytho
C++中的指针和引用 ,可以参考博客
https://www.cnblogs.com/heyonggang/archive/2012/12/13/2815730.html
问题导入?
如果你使⽤Python声明了⼀个变量并同时为其赋值,即a = 20,你是否将这条语句简单理解为:1. 计算机开辟了⼀块地址别名为a的内存;2. 往这块内存存储了数据20?
如果你对上述问题的回答为是,那么你对诸如“Python是⼀门动态类型语⾔”、“Python效率远不如C语⾔等静态语言效率高”等说法也⼀定不了解其本质。问题解答:
变量由三部分组成:
标识,表示对象所储存的内存地址,使用内置函数id()获取
类型,表示对象的数据类型,使用内置函数type获取
值,表示对象所储存的具体数据,使用print打印输出。
在Python中:
1.变量和对象是分开储存的,
2.和其他编程语言一样的是,变量是对象内存地址的别名,即a代表了地址0x1002;
3.和其他编程语⾔不⼀样的是,Python中的变量和数据(对象)分开存储:变量a的地址0x1001处仅保存了数据20存储的内存地址0x1002。
4.变量a所标示的内存空间存储数据20所在内存地址的过程称为引⽤。5.变量是对象的一个引用,对对象的操作都是通过引用来完成的
6.赋值操作 = 就是给对象添加一个引用
python中一切的传递都是引用(地址),无论是赋值还是函数调用,即python中所有的变量赋值、参数传递等都是引⽤传递。
python中数值类型(int和float),布尔型bool,字符串,元组都是不可变对象,列表list,字典dict,集合set都是可变对象,自定义的类对象也是可变对象 。对于不可变对象,我们⽆法在内存中直接修改这个变量(如 100,"student"),如果我们尝试对不可变类型进⾏修改,就会断开原始的引⽤,重新分配内存地址;修改可变对象的值不会断开原始引⽤,是直接在原始值上进⾏修改,因此这些和原始值有引⽤关系的变量的“值”都被修改了。
函数调⽤时,传递给函数的参数中保存的仍然是变量的引⽤(真实值的地址),函数参数是⼀个局部变量,初始时,参数和函数外部的值指向同⼀个内存地址。
- 如果参数是不可变对象,则断开原始引⽤,重新进⾏新的引⽤,函数外部的值不受影响
- 如果参数是可变对象,直接修改原始值,函数外部的值同步变化
- 解释器对常⽤的不可变类型进⾏了优化,把常⽤的数值、短字符串赋值给某些变量时,这些变量都指向相同的内存地址
示例一:
a='123'
执行上面这个赋值语句的时候,Python解释器首先在内存中创建一个字符串"123"对象。之后在内存中创建一个名为"a"的变量,将"a"指向"123"(将"123"的地址保存到“a”中)
接着执行
b=a#指向同一个对象
因为a已经存在,所以不会创建a,会创建变量"b",并将"b"指向"a"指向的字符串"123"
然后执行
a = "456"#不可变对象,对其进行修改时会断开原来的引用
a还是存在的,Python会新分配一块内存来存储新的值,会创建字符串"456", 然后将"456"的地址赋给"a"
对于不可变对象,我们⽆法在内存中直接修改这个变量(如 100,"student"),如果我们尝试对不可变类型进⾏修改,就会断开原始的引⽤,重新分配内存地址.那么下面的代码就不难理解
a=100 print(id(a))#1888557553104 a=a+1 print(id(a))#1888557553136
对于可变对象,修改可变对象的值不会断开原始引⽤
lst=[1,2,3] print(id(lst)) lst.append(4)#2167621434240 print(id(lst))#2167621434240
给出一段代码检验是否理解
a=100 b=a a=a+1 print(a,b) print(id(a),id(b)) lst1=[1,2,3] lst2=lst1 lst1.remove(2) print(lst1,lst2) print(id(lst1),id(lst2)) ''' 运行结果 101 100 2413522146800 2413522146768 [1, 3] [1, 3] 2413527186432 2413527186432 '''
解释器对常⽤的不可变类型进⾏了优化,把常⽤的数值、短字符串赋值给某些变量时,这些变量都指向相同的内存地址,即python中的驻留机制。
a=100 b=100 c=100 print(id(a),id(b),id(c)) lst1=[1,2] lst2=[1,2] lst3=[1,2] print(id(lst1),id(lst2),id(lst3)) ''' 运行结果 1872572732880 1872572732880 1872572732880 1872577903488 1872577900416 1872579060160 '''
函数参数
参数的传递本质上是一种赋值操作,函数的参数也是一种引用传递。
def foo(arg): arg = 2 print(arg) a = 1 foo(a) # 输出:2 print(a) # 输出:1
变量 a 指向 1,调用函数 foo(a) 时,arg=a,,这时两个变量都指向 1。在函数里面 arg 重新赋值为 2 之后,不可变对象不可修改时原先引用会断开,而 a的引用没变。因此 print(a) 还是 1。
再来看下一段代码def bar(args): args.append(1) b = [] print(b)# 输出:[] print(id(b)) # 输出:4324106952 bar(b) print(b) # 输出:[1] print(id(b)) # 输出:4324106952
执行 append 方法前 b 和 arg 都指向同一个对象,执行 append 方法时,并没有重新赋值操作,也就没有新的引用过程,append 方法只是对列表对象插入一个元素,对象还是那个对象,只是对象里面的内容变了。因为 b 和 arg 都是绑定在同一个对象上,执行 b.append 或者 arg.append 方法本质上都是对同一个对象进行操作,因此 b 的内容在调用函数后发生了变化(但id没有变,还是原来那个对象)。
python中+=在可变对象运算中的特殊用法
num = 100 def update(a): a+=10 print(a) #打印110 update(num) print(num) #打印100 num1 = 100 def update1(a): a=a+10 print(a) #打印110 update(num1) print(num1) #打印100 ''' python中所有的变量都是引用类型 num和update(a),实参num和形参a都指向同一片内存地址 a += 10 这里对a做出修改的操作 因为a是数值类型,属于不可变类型,不能修改 所以,python会创建一个临时变量a,用来存储110 所以print(num) 打印的仍然是100 '''
list = [2] def test1(num): num += num print(num) #打印[2,2] test1(list) print(list) #打印[2,2] #从结果而言,修改了可变类型的值 list2 = [3] def test2(num): num = num + num #num+num 的结果是[3,3] 这里表示将[3,3]这个列表赋值给num这个临时变量 print(num) #打印[3,3] test2(list2) print(list2)#打印[3] ''' 在python中 +=运算符表示对当前变量进行操作 并不完全等同于 + '''
简单的加法中,列表的运算还是创建了一个新的列表对象;但在简写的加法运算+=实现中,则并没有创建新的列表对象。这一点要十分注意。验证一下是否理解
def add_list(p): p = p + [1] p1 = [1,2,3] add_list(p1) print p1 >>> [1, 2, 3] def add_list(p): p += [1] p2 = [1,2,3] proc2(p2) print p2 >>>[1, 2, 3, 1]
词典的引用
a = [] b = {'num':0, 'sqrt':0} resurse = [1,2,3] for i in resurse: b['num'] = i b['sqrt'] = i * i a.append(b) print a #输出 [{'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}]
但我们实际想要的结果是这样的:
输出 [{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]
这是由于a中的元素就是b的引用。可以修改为:
a = [] resurse = [1,2,3] for i in resurse: a.append({"num": i, "sqrt": i * i}) print(a) #输出[{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]
呕心沥血之作,。。。。,看了好长时间
更多推荐
所有评论(0)