先上个示例:html
>>> val = [1] >>> val[0] = val >>> val [[...]]
上述代码使val中包含自身,而产生了无限递归。上述示例代表Python中的变量名为引用类型,赋值只是使得左值指向与右值相同的内存对象。python
is
运算符能够判断两个引用是否指向了同一个对象,而==
运算符判断两个引用指向的值是否相等而不关心指向什么对象。git
对引用不了解的朋友,能够把Python引用与C/C++中的void *
类比,不过由于垃圾回收机制Python引用无需担忧内存泄漏的问题。github
上面的示例代表赋值没法为对象创建副本,python中的copy模块提供了copy
和deepccopy
创建副本。app
示例:函数
>>> import copy >>> a = [[1, 2, 3], [4, 5, 6]] >>> b = a >>> c = copy.copy(a) >>> d = copy.deepcopy(a) >>> a.append(7) >>> a[1][2] = 0 >>> a [[1, 2, 3], [4, 5, 0], 7] >>> b [[1, 2, 3], [4, 5, 0], 7] >>> c [[1, 2, 3], [4, 5, 0]] >>> d [[1, 2, 3], [4, 5, 6]]
浅复制copy.copy只复制父引用指向的对象,其子引用仍指向原来的内存对象,而深复制copy.deepcopy
则会复制全部引用指向的对象。deepcopy 本质上是递归 copy。post
示例中的副本c,d父对象是a的副本因此a.append
方法对它们没有影响。性能
可是copy.copy
建立的副本c中的元素仍指向与a相同的内存对象,而deepcopy
建立的d则指向了本身的元素。ui
tuple和frozenset之类的容器只是保证其中引用指向不变,可是引用指向的内存对象仍然是可变的。容器的切片对象的机制为浅复制。.net
x = x + y
,必须建立新的临时变量而后进行浅复制,性能较差。
x += y
,无需新建临时对象,只在内存块末尾增长元素,性能较好。
Python中的参数传递采用浅复制的值传递。
示例:
>>> def swap(a,b): ... b,a=a,b ... >>> a = 1 >>> b = 2 >>> swap(1,2) >>> a 1 >>> b 2
上述示例证实,Python参数传递是采用值传递的方式。
示例2:
>>> def fun(a): ... a[0] = 2 ... >>> a = [1] >>> fun(a) >>> a [2]
这个示例则证实采用浅复制的方法进行传递。
Python中的垃圾回收是以引用计数为主,标记-清除和分代收集为辅。
一组对象互相引用的状况称为循环引用(交叉引用),若出现这种状况引用计数将没法正确的回收垃圾。,能够包含其余对象引用的容器对象(如list, dict, set,甚至class)均可能产生循环引用。
“标记-清除”法是为了解决循环引用问题。
垃圾标记时,先将集合中对象的引用计数复制一份副本(以避免在操做过程当中破坏真实的引用计数值),而后操做这个副本,遍历对象集合,将被引用对象的引用计数副本值减1。
根据引用计数副本值是否为0将集合内的对象分红两类,reachable和unreachable,其中unreachable是能够被回收的对象。
分代回收的总体思想是:将系统中的全部内存块根据其存活时间划分为不一样的集合,每一个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减少,存活时间一般利用通过几回垃圾回收来度量。
弱引用是避免循环引用的一种方法,弱引用不记录引用计数。当一个对象只有弱引用时可能被垃圾回收器回收。
weakref.ref(obj,[callable])
用于创建一个指向obj的弱引用,当对象被回收前callable可选参数指定的函数将被执行以进行清理工做。