流畅的python第八章--对象引用 可变性 垃圾回收
本章内容:
对象引用 :
引用式变量
对象标识符,值,别名
可变性
元组特性
潜复制和深复制
引用和函数参数
垃圾回收
删除对象
弱引用
提示:本章内容理解不清很可能出现不易发现的错误
8.1 变量不是盒子
#a是对象[1,2,3]的标识,或者说引用式变量
a=[1,2,3]
#b也是对象[1,2,3]的标签,或者说别名
b=a
a.append(4)
print(b)
对引用式变量来说,说把变量分配给对象更合理
8.2 标识、相等性和别名
charles={"name":"Charles L","born":1832}#lewis是charles的笔名
lewis=charles lewis is charlesid(charles),id(lewis)#此时有一个冒充者:
alex={"name":"Charles L","born":1832}#==比较的是值
alex==charles #is比较的是对像的id值
alex is not charles
==运算符比较两个对象的值(对象中保存的数据),而is比较对象的内存地址。
8.2.1 在==和is之间选择
通常我们使用==更多,因为我们通常关心的是对象的值
最常使用is检查变量绑定的值是不是None。下面是推荐的写法:
x is None
此是is运算符的调用速度更快,因为is运算符不能重载,所以Python不用寻找并调用特殊方法
8.2.2 元组的相对不可变性
元组保存的是对象的引用,元组的不可变性其实是指tuple数据结构的物理内容(即保存的引用)不可变,与引用的对象无关
即元组不可变的仅仅是元素的标识,而指向的对象是可变的
t1=(1,2,[30,40])
t2=(1,2,[30,40])
t1==t2
id(t1[-1])
t1[-1].append(99)
id(t1)
t1==t2
所以元组只具有相对不可变性,这是元组不可散列的原因
8.3 默认做浅复制
浅复制
l1=[3,[55,44],[7,8,9]]#[:]构造函数默认做浅复制
l2=l1[:]l2 l1l2==l1l2 is l1l1[1] is l2[1]l3=la.copy()
#该方法也是潜复制
l4=list(l1)l5=list(l1)
#也是潜复制
深复制(即副本不共享内部对象的引用)
import copy l5=copy.deepcopy(l1)
用下例看潜复制和深复制的差异
class Bus:def __init__(self,passengers=None):#创建一个passenger列表if passengers is None:self.passengers=[] else:self.passengers=list(passengers)def pick(self,name):self.passengers.append(name)def drop(self,name):self.passengers.remove(name)import copy bus1=Bus(["Alice","bill","Charles","David"])
bus2=copy.copy(bus1)
#用copy模块直接对变量进行复制,这里是潜复制
bus3=copy.deepcopy(bus1)
#用copy模块中的deepcopy方法进行深复制
bus1.drop("Alice")print(bus2.passengers)
print(bus3.passengers)print(id(bus1.passengers)==id(bus2.passengers))
#bus2是bus1的浅复制,和bus共享同一个乘客列表
print(id(bus1.passengers)==id(bus3.passengers))
8.4 函数的参数作为引用时
Python唯一支持的参数传递模式是共享传参
共享传参
:函数内部的形参是实参的别名
这种传参方式的结果:函数可能会修改作为参数传入的可变对象
当传入参数是不可变对象,比如整数、元组时,改变形参并不能把 实参改变。
def f(a,b):a+=breturn a
x=1
y=2
f(x,y)
print(x,y)#元组
t=(10,20)
u=(30,40)
f(t,u)
print(t,u)
特别注意当参数是可变对象时,例如列表
x=[1,2]
y=[3,4]
f(x,y)
print(x,y)
8.4.1 不要使用可变类型作为参数的默认值
python函数中,可选参数可以有默认值。
这样可以实现向后兼容。
(向后兼容:例如在python2.0上写的程序,在python3.0上可以完满运行时,我们称这种现象叫向后兼容,或者叫向历史兼容)
使用可变对象作为参数的默认值,有可能会造成不容易发现的错误。
看下例:
class funnyBus:"""人会失踪的神奇校车"""def __init__(self,passengers=[]):#这个将一个列表(可变对象)设置为默认参数self.passengers=passengersdef pick(self,name):self.passengers.append(name)def drop(self,name):self.passengers.remove(name)bus1=funnyBus(["uni","xiaoma","leo"])
#此时传入了一个列表参数。这时候,没有什么问题
bus1.passengers
bus1.pick("alice")
bus2=funnyBus()
bus3=funnyBus()
这个时候问题就来了,bus2和bus2两个实例会共享一个默认的列表[]
我们查看一下这时类的属性。
bus2=funnyBus()
bus2.pick("hello")
bus3=funnyBus()
bus2.pick("hello ni mei")
print(dir(funnyBus.__init__))
print(funnyBus.__init__.__defaults__)
output:
(['hello', 'hello ni mei'],)
运行下代码:
print(funnyBus.__init__.__defaults__[0] is bus2.passengers)
#实例bus2的属性passengers帮定到了类的第一个属性上。
正确的处理默认参数的方法:
class Bus:def __init__(self,passengers=None):#创建一个passenger列表if passengers is None:#如果没有默认参数,实例就自己创建个参数。self.passengers=[] else:self.passengers=list(passengers)
8.4.2 防御可变参数
如果定义的函数接收可变参数,应该谨慎考虑调用方是否期望修改传入的参数。
下面例子说明可变参数的风险:
class Bus:def __init__(self,passengers=None):#创建一个passenger列表if passengers is None:self.passengers=[] else:self.passengers=passengers#(1)def pick(self,name):self.passengers.append(name)def drop(self,name):self.passengers.remove(name)
此时,将可变参数传入了对象,当对象改变该参数时,外面的可变参数也会改变。因为是参数的引用传入了。
所以,如果我们不希望改变传入的参数,
上述代码的(1)部分应该这么写
self.passengers=list(passengers)
#创建参数的副本
这么写还有一个好处,就是还可以传入元组,等其他参数。
在不确定是否需要改变参数对象的时候,我们会先默认采用这种方法。
8.5 del和垃圾回收
对象绝不会自行销毁;然而,无法得到对象时,可能会被当作垃圾回收。
del语句删除引用,而不是对象。当del删除的引用,是该对象的唯一引用时,该对象会被垃圾回收。
python垃圾回收机制:
引用计数法
当引用计数归零时,对象立即就被销毁。
下面例子演示了对象的回收。
import weakref
s1={1,2,3}
s2=s1
def bye():
print("Gone....")
ender=weakref.finalize(s1,bye)
ender.alive
def sl
ender.alive
s2="spam"
ender.alive
8.6 弱引用
实际使用中,有时需要引用对象,但是不让对象存在的时间超过所需时间。
例如我们把数据对象保存在缓存中使用,当对象的引用不存在,我们希望将缓存中的这部分数据释放掉。这时候我们就可以使用弱引用。
对弱引用的原理解释:
弱引用底层实现原理
弱应用的一个使用场景。
假设我们有一个多线程程序,并发处理应用数据:
class Date:def __init__(self,key):pass
key是该数据的唯一标识,该数据可能同时被多个线程同时访问。
当Data数据量很大时,创建的成本很高,我们希望Data在程序中只维护一个副本,
就算被多个线程同时访问,也不重复创建。
可以设计一个缓存中间件Cacher
import threading
#数据缓存class Cacher:def __init__(self):self.pool={}self.lock=threading.Lock()def get(self,key):with self.lock:#加锁data=self.pool.get(key)if data:return data self.pool[key]=data=Data(key)return data
该代码有个严重的问题,
Data一旦穿件,就会保存在数据字典,哪怕已经没有线程需要该数据,因为该数据在字典中有键进行了引用,所以对象不会被回收。
这个时候就用到弱引用,用字典键值对中的值来保存数据对象的弱引用,当数据对像的引用为0之后,就会自动释放该对象。
import threading
import weakref
# 数据缓存
class Cacher:def __init__(self):self.pool=weakref.WeakValueDictionary()self.lock = threading.Lock()def get(self, key):with self.lock:data = self.pool.get(key)if data:return dataself.pool[key] = data = Data(key)return data
弱引用类在 weaker模块中,
常见的有:
weakValueDictinary
weakKEYDictinary
weakSet
最后看一个小程序,猜一个结果:
t1=(1,2,3)
t2=tuple(t1)
t2 is t1
?
t3=t1[:]
t3 is t1
?
流畅的python第八章--对象引用 可变性 垃圾回收相关推荐
- python内存管理和释放_《python解释器源码剖析》第17章--python的内存管理与垃圾回收...
17.0 序 内存管理,对于python这样的动态语言是至关重要的一部分,它在很大程度上决定了python的执行效率,因为在python的运行中会创建和销毁大量的对象,这些都设计内存的管理.同理pyt ...
- Python的内存管理与垃圾回收机制
在使用真格量化时,一些用户希望了解如何来提高系统性能.通过了解Python的内存管理和垃圾回收机制,我们可以有针对性地去提高策略代码性能. Python内存管理机制 Python的内存管理机制:引入计 ...
- python解析原理_Python语法垃圾回收机制原理解析
一 引入 解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,那 ...
- python入门系列:对象引用、垃圾回收、可变性
Python中的变量是什么 引言 Python和java中的变量本质不一样,java的变量可以理解为一个盒子,用来容纳我们的对象,使用前要先声明它,好分配给我们合适的内存空间.Python的变量可以理 ...
- Python内存管理方式和垃圾回收算法解析
在列表,元组,实例,类,字典和函数中存在循环引用问题.有 del 方法的实例会以健全的方式被处理.给新类型添加GC支持是很容易的.支持GC的Python与常规的Python是二进制兼容的. 分代式回收 ...
- python内存回收垃圾有哪些_[Python之路] 内存管理垃圾回收
一.python源码 1.准备源码 解压得到文件夹: 我们主要关注Include中的".h"文件以及Objects目录中的".c"文件. 我们从Include和 ...
- Python的内存管理以及垃圾回收
参考:http://www.cnblogs.com/CBDoctor/p/3781078.html 先从较浅的层面来说,Python的内存管理机制可以从三个方面来讲 (1)垃圾回收 (2)引用计数 ( ...
- python有向图_Python 中的垃圾回收机制
一.概述 python采用的是引用计数机制为主,标记-清除和分代收集(隔代回收)两种机制为辅的策略. 现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的 ...
- [转载] Python和java中的垃圾回收机制
参考链接: Python中的垃圾回收 Python的垃圾回收机制 Python的垃圾回收机制有两种(也可以说一种:叫引用计数): 一是引用计数, 二是隔代回收. 引用计数 引用计数原理: 当数据的引用 ...
最新文章
- Linux下C程序的反汇编【转】
- iphone开发小技巧,转载
- 要命的定义函数。。。参数组合。请认真理解!
- scala中_下划线的使用
- android进程调试(ro.debuggable=1或android:debuggable=true)----JDWP线程
- 利用 Conda 尝鲜 Python 3.10 不一样的特性 快来试试
- python3入门经典100例-Python3入门机器学习_经典算法与应用-慕课网实战
- 25.Linux/Unix 系统编程手册(上) -- 进程的终止
- idea springboot启动报SLF4J:Failed to load class “org.slf4j.impl.StaticLoggerBinder”
- C#串口介绍以及简单串口通信程序设计实现
- 扫描二维码 打开 小程序或是H5网页
- 为什么使用工作流引擎,什么是工作流引擎,工作流引擎选型以及如何使用
- NOIP模拟赛 czy的后宫3
- 一张图看明白云计算数据中心总体分层架构
- 微信Wifi物联架构---机智云/云智易如何接入微信硬件平台
- 这才是陆奇看重的创业者:最小19岁,开拖拉机的斯坦福毕业生,弃医从文的武大学生……...
- 安卓点击图片跳转界面_详解拳头注册 + 安卓LOL试玩教程
- Cuil搜尋引擎 挑戰Google
- 战地2服务器地图修改,《战地2》地图修改秘籍
- 读稻盛和夫《活法》-现代人的修行之路