所以即使a中修改了容器中元素的值,b中也没有进行修改
这里总结一下:
1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。
2. 浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素( 新瓶装旧酒 )。
3. 深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。
函数的参数: 我们首先来看一个函数的调用 def f(a,b): a+=b return aif __name__ == "__main__": x=1 y=2 ret=f(x,y) print x,ret E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py 1 3 在f函数中传入x,y,经过f运算后,返回的是3,但是x的值并没有发生改变。我们将代码改下,将参数变成列表。 def f(a,b): a+=b return aif __name__ == "__main__": x=[1,2] y=[3,4] ret=f(x,y) print x,ret E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py [1, 2, 3, 4] [1, 2, 3, 4] 返回的结果是[1,2,3,4],但同时x的值也发生了变化,变成了[1,2,3,4]。为什么在第一次的时候x的值并没有发生变化,但是在第二次时候发生了变化。这里涉及到了函数参数传递的两种方法:值传递和引用传递 值传递:被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值 引用传递:被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。 简单点说,值传递是会对被赋值的参数另外开辟一个内存空间,修改参数的时候修改的是局部变量的内存空间,但是引用传递是直接修改传入参数的内存空间 我们将代码修改下: def f(a,b): print 'before used:%d' % id(a) ⑵ a+=b print 'in the function:%d' % id(a) ⑶ return aif __name__ == "__main__": x=1 y=2 print 'before funciton:%d' % id(x) ⑴ ret=f(x,y) print x,ret E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py before funciton:22752576 before used:22752576 in the function:22752552 1 3 通过在代码里面新增id函数,可以看到在第一步和第二步中的内存地址是一样的,但是经过a+=b后第三步的地址发生了变化。可以看出在传入参数的时候,是将x的地址传入进去,所以a和x的地址是一样的。经过运算后a变成了3并且重新申请了内存,因此a指向了内存22752552,而x仍然是指向内存22752576 那我们来看下列表的呢: def f(a,b): print 'before used:%d' % id(a) a+=b print 'in the function:%d' % id(a) return aif __name__ == "__main__": x=[1,2] y=[3,4] print 'before funciton:%d' % id(x) ret=f(x,y) print x,ret E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py before funciton:25410576 before used:25410576 in the function:25410576 [1, 2, 3, 4] [1, 2, 3, 4] 从上面看到,在传入的开始和a+=b后,a的地址是始终和x的地址空间一样。因此a始终指向x的内存空间。这就是为什么在经过f(x,y)后x的值发生了变化,原因就在于由于a指向了x的内存空间。因此x的内存的值被发生了改变 下面的流程图可以看到,在函数内部,a,b都指向了x,y的内存 在经过计算后的ret值以及a都指向x的内存空间 总结一下:python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。 我们来看下由于传值导致的问题。书中列举了一个幽灵校车的例子: class HauntedBus: def __init__(self,passenagers=[]): self.passengers=passenagers def pick(self,name): self.passengers.append(name) def drop(self,name): self.passengers.remove(name)if __name__ == "__main__": bus1=HauntedBus(['Alice','Bill']) print 'bus1 passenger: %s' % bus1.passengers bus1.pick('Charlie') bus1.drop('Alice') print 'bus1 passenger: %s' % bus1.passengers bus2=HauntedBus() bus2.pick('Carrie') print 'bus2 passenger: %s' % bus2.passengers bus3=HauntedBus() print 'bus3 passenger: %s' % bus3.passengers bus3.pick('Dave') print 'bus2 passenger: %s' % bus2.passengers E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py bus1 passenger: ['Alice', 'Bill'] bus1 passenger: ['Bill', 'Charlie'] bus2 passenger: ['Carrie'] bus3 passenger: ['Carrie'] bus2 passenger: ['Carrie', 'Dave'] 从结果来看,bus1没有问题,首先在车上的是Alice和Bill,然后接上了Charlie,Alice下车,最后车上只剩下了Bill和Charlie 但是bus2和bus3就很诡异了。首先bus2和bus3在实例化的时候都没有传入初始值。首先bus2接到了Carrie,但是在bus3实例化后打印bus3上的乘客,居然有Carrie,然后bus3接到了Dave,此时再看bus2上的乘客,居然Dave也在bus2上。这里bus3和bus2共享了一个乘客列表。导致出现了一堆幽灵学生。为什么会产生这样的现象呢。我们打印下每次passenger的内存空间 class HauntedBus: def __init__(self,passenagers=[]): print 'passenagers %d' % id(passenagers) self.passengers=passenagers print 'self.passengers %d' % id(self.passengers) def pick(self,name): self.passengers.append(name) def drop(self,name): self.passengers.remove(name)if __name__ == "__main__": bus2=HauntedBus() bus2.pick('Carrie') print 'bus2 passenger: %s' % bus2.passengers bus3=HauntedBus() print 'bus3 passenger: %s' % bus3.passengers bus3.pick('Dave') print 'bus2 passenger: %s' % bus2.passengers E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py passenagers 24903512 self.passengers 24903512 bus2 passenger: ['Carrie'] passenagers 24903512 self.passengers 24903512 bus3 passenger: ['Carrie'] bus2 passenger: ['Carrie', 'Dave'] 从结果可以看到bus2和bus3中的passengers都是引用的同一个内存地址。因为passengers被初始化成了[]。而且list是可变对象,因此bus3和bus2在引用时候都是引用的[]。从而导致出现了幽灵学生。下面的结果可以看到bus2和bus3的passenagers都是同一个对象 那么如何避免这种现象呢,只需要在初始化的时候将passengers赋值为None,然后对None进行判断 class HauntedBus: def __init__(self,passenagers=None): if passenagers is None: self.passengers=[] else: self.passengers=passenagers print 'passenagers %d' % id(passenagers) print 'self.passengers %d' % id(self.passengers) def pick(self,name): self.passengers.append(name) def drop(self,name): self.passengers.remove(name)if __name__ == "__main__": bus2=HauntedBus() bus2.pick('Carrie') print 'bus2 passenger: %s' % bus2.passengers bus3=HauntedBus() print 'bus3 passenger: %s' % bus3.passengers bus3.pick('Dave') print 'bus2 passenger: %s' % bus2.passengers print 'bus3 passenger: %s' % bus3.passengers E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter8.py passenagers 1410247452 self.passengers 24769240 bus2 passenger: ['Carrie'] passenagers 1410247452 self.passengers 24772400 bus3 passenger: [] bus2 passenger: ['Carrie'] bus3 passenger: ['Dave'] 从这可以看到幽灵学生的现象消失了,从self.passengers的每次地址来看,bus2和bus3是不同的地址空间。下面的结果运行也可以看到bus2和bus3的passengers的地址空间是不一样的
转载于:https://www.cnblogs.com/zhanghongfeng/p/7138057.html