亲宝软件园·资讯

展开

Effective python(五):内置模块

石天放 人气:0
#### 1,考虑使用contextlib和with语句改写可复用的try/finally代码 >1. `with lock:print('lock is held')`相当于`try:print('lock is held')`,`finally:lock.release`,使用with可以避免繁琐的语句 >2. 开发者可以使用内置的contextlib模块的contextmanager修饰器来处理自己编写的对象和函数以支持with语句,这样做比标准写法更便捷,如果使用标准方式写,需要定义新类并提供__enter__和__exit__方法 > ```python > from contextlib import contextmanager > > #上下文管理器 > @contextmanager > def test(): > print("初始化") > #可以在这里开启资源,如log等级上升 > try: > print("with开始") > #如果有错误会通过yield弹出 > yield > finally: > print("离开时释放资源") > > with test(): > print("go") > ``` >3. 可以在yield处弹出一个对象,通过as指定为一个局部变量,在with内可以对其进行交互 #### 2,使用copyreg实现可靠的pickle操作 >1. python内置的pickle模块能将python对象转化为字节流,也能把字节反序列化为python对象,但pickle处理后的数据实际上就是一个程序,可能会混入恶意信息,对程序造成损坏,json产生的数据是一种安全的信息,只描述对象如何构成,不会造成额外风险,所以pickle处理的字节流不应该在未受信任的程序之间传播 >2. 例如定义一个`GameState`类,实例化为对象`state=GameState()`包含玩家的当前游戏状态,生命,金币等等,玩家退出游戏的时候,将state直接写到一份文件里,游戏加载时读取 > ```python > with open(path,'wb') as f: > pickle.dump(state,f) > > with open(path,'rb') as f: > state_after=pickle.load(f) > print(state_after.__dict__) > ``` >3. 如果类新增加了一些属性,但保存的对象仍然是旧的,那就需要使用copyreg了,先将类添加一个`__init__`构造器,通过传参的方式初始化属性,然后使用如下代码注册函数,序列化和反序列化仍然按照原来的方式使用即可 > ```python > def unpickle_game_state(kwargs): > return GameState(**kwargs)#返回State实例化 > > def pickle_game_state(game_state):#参数为State对象 > kwargs=game_state.__dict__#获取其属性 > > #序列化封装需要返回反序列化的函数和参数 > return unpickle_game_state,(kwargs,) > > copyreg.pickle(GameState,pickle_game_state)#注册pickle函数 > ``` >4. 使用版本号管理类,修改copyreg注册的pickle函数,在里面添加一个版本号的参数`kwargs['version]=2`,然后在反序列化函数中根据版本号对其进行操作,即可兼容版本 >5. 固定引入路径,若重构时,将类名修改或删除,那么反序列化的时候会出错,使用copyreg.pickle注册后,会自动指向unpickle函数,所以不用担心修改类名的问题,但如果没有使用copyreg注册,那么修改类名后反序列化就会报错 #### 4,使用datetime模块处理本地时间而不是time模块 >1. time模块,内置的time模块中有一个名叫localtime的函数,可以把UNIX时间戳(timestamp即UTC时刻距离UNIX计时原点的秒数)转换为宿主计算机时区的当地时间,这个模块不够稳定,只能转换主机时区的时间,其它地区会出错,应该尽量不用,而是使用datetime模块 > ```python > from time import localtime,strftime,strptime,mktime > time_format='%Y-%m-%d %H:%M:%S' > time_str=strftime(time_format,localtime(1407694710)) > #将时间戳转换为当地时间 > print(time_str) > > #将本地时间转化为UTC时间 > #strptime解析时间字符串,mktime将本地时间转换为UNIX时间戳 > print(mktime(strptime(strptime(time_str,time_format)))) > ``` >2. datetime模块 > ```python > from datetime import datetime,timezone > from time import mktime > > #UTC时间转本地时间 > now=datetime(2014,8,10,18,18,30) > now_utc=now.replace(tzinfo=timezone.utc) > #注意此处若想可靠的转换时区,还需要搭配pytz模块 > now_local=now_utc.astimezone()#此处只包含UTC时区 > print(now_local) > > #本地时间转UTC格式时间戳 > time_str='2014-08-10 11:18:30' > time_format='%Y-%m-%d %H:%M:%S' > now=datetime.strptime(time_str,time_format) > time_tuple=now.timetuple() > utc_now=mktime(time_tuple) > print(time_tuple) > ``` >3. 若要在不同时区之间执行可靠的转换操作,还需要搭配pytz模块使用 #### 5,内置的数据结构与算法 (开发者不应该自己去重新实现,因为很难把他们写好) >1. 双向队列,collections模块中的deque类,从该队列头部与尾部插入或移除一个元素,只有O(1)的时间复杂度 > ```python > d=deque() > d.append(1) > x=d.popleft() > ``` >2. 有序字典,collections模块中的OrderedDict类,它能够按照键的插入顺序,保留键值对在字典中的次序。标准字典是无序的,也就是说,在相同键值对的两个字典上迭代可能出现不同的迭代顺序 > ```python > a=OrderedDict() > a['x']=1 > ``` >3. 默认值字典,collections模块中的defaultdict类,本例中用int函数创建字典,默认值为0 > ```python > stats=defaultdict() > stats['my_counter']+=1 > ``` >4. 堆队列(优先队列),heapq模块中的heappush、heappop和nsmallest等函数,能够在标准的list中创建堆结构,时间复杂度O(logn),普通列表O(n) > ```python > a=[] > heappush(a,5) > heappush(a,3) > heappush(a,7) > heappush(a,4) > #总是能弹出优先级较高的元素, > #默认是越小元素优先级越高 > print(heappop(a)) > #即使调用sort后依然能保持堆结构 > a.sort() > ``` >5. 二分查找,bisect模块中的bisect_left函数,使用Index查找复杂度为O(n),二分为O(logn),注意:使用前列表应排好序,bisect搜索一百万个元素的列表,与index搜索包含14个元素的列表,所耗时间差不多 > ```python > i_index = bisect_left(alist,number) > ``` >6. 与迭代器有关的工具,itertools模块,可分为三大类 >>♦ 能够把迭代器连接起来的函数: >>- chain:将多个迭代器按顺序连成一个迭代器 >>- cycle:无限重复某个迭代器中的各个元素 >>- tee:把一个迭代器拆分成多个平行的迭代器 >>- zip_longest:与内置的zip函数相似,可以应对不同长度的迭代器 >>♦ 能够从迭代器中过滤元素的函数: >>- islice:在不复制的前提下,根据索引值来切割迭代器获取迭代器的一部分 >>- takewhile:在判定函数为True的时候,从迭代器逐个返回元素 >>- dropwhile:从判定函数初次为False的地方开始,逐个返回迭代器中的元素 >>- filterfalse:从迭代器中逐个返回能令判定函数为False的所有元素,效果与filter函数相反 >>♦ 能够把迭代器中的元素组合起来的函数: >>- product:根据迭代器中的元素计算笛卡尔积(就是x*y),并返回。可以使用product改写深度嵌套的列表推导操作 >>- permutations:用迭代器中的元素构建长度为N的有序排列,例:`permutations('ABCD',2) # AB AC AD BA BC BD CA CB CD DA DB DC` >>- combination:用迭代器中的元素构建长度为N的无序组合,例:`combinations('ABCD', 2) # AB AC AD BC BD CD` >备注:如果发现自己需要编写一段非常麻烦的迭代程序,应该花些时间看看itertools的文档看看有没有现成的工具可以用 #### 6,在重视精度的场合,使用decimal模块中的Decimal类,该类默认提供28个小数位,以进行定点数学运算,有需要可以把精度提的更高 >1. `dec=Decimal('1.45')`在decimal之间进行计算会得到精确的结果,计算后的类型仍然是Decimal类型,`print(dec)` >2. Decimal类提供quantize内置函数,它可以按照精度和舍入方式精确的调整数值`result=dec.quantize(Decimal('0.01'),rounding=ROUND_UP)`,`print(result)` >3. 若要使用精度不受限的方式表达有理数,那么可以考虑使用Fraction类,包含在内置的fractions模块里 #### 7,学会使用pypi,Python中央仓库:http://pypi.python.org >1. 如果碰到不熟悉的编程难题,应该去PyPI里看看别人的代码 >2. pip3安装python3版本的软件包,pip安装python2版本的软件包

加载全部内容

相关教程
猜你喜欢
用户评论