数据结构 如何将列表,字典等进行合并 列表
1 2 3 4 5 6 l1 =[1 ,2 ,3 ] l2 = [4 ,5 ,6 ] l1 + l2 Out[22 ]: [1 , 2 , 3 , 4 , 5 , 6 ] l1.extend(l2) l1[len (l1):len (l2)] = l2
字典 1 2 3 4 5 6 7 8 9 10 11 12 context = {} context.update(defaults) context.update(user) context = defaults.copy() context.update(user) context = dict (defaults) context.update(user) context = {**defaults, **user}
如何筛选列表、集合、字典中的数据 列表
列表解析更好,时间更短。
1 2 3 4 5 6 7 8 9 10 11 12 13 from random import randintdata = [randint(-10 , 10 ) for x in xrange(10 )] data2 = filter (lambda i: i > 0 , data) data3 = [x for x in data if x > 0 ] print data, data2, data3
字典
1 2 dictdata = {x: randint(0 , 100 ) for x in xrange(0 , 50 )} dictdata2 = {k: v for k, v in dictdata.iteritems() if v >= 60 }
集合
1 2 setdata = set (data) setdata2 = {x for x in setdata if x > 0 }
如何根据字典中值的大小,对字典中的项排序
使用内置函数 sorted
使用 zip 将字典转换为元组(将值放在第一个)
使用 sorted 排序
使用 sorted 函数的 key 参数
1 2 3 4 5 from random import randintd = {x:randint(60 ,100 ) for x in 'xyzabc' } sorted (zip (d.itervalues(),d.iterkeys()))sorted (d.iteritems(),key=lambda x:x[1 ])
如何为元组中的每个元素命名,提高程序可读性
定义类似于其他语言中的枚举类型,也就是定义一系列数值常量
使用标准库中的 collections.namedtuple
1 2 3 4 5 6 7 from collections import namedtuplestudent = namedtuple('student' ,['name' ,'age' ]) s1 = student('jim' ,16 ) s2 = student(name='tom' ,age=12 ) s1.name
如何统计序列中元素的出现频度 问题:
找到某随机序列中出现次数最高的三个元素
对某英文文章的单词进行词频统计,找到出现次数最高的 10 个单词
构造字典
使用 collections 下的 Counter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from random import randintdata = [randint(0 , 10 ) for x in xrange(20 )] c = dict .fromkeys(data,0 ) for x in data: c[x] += 1 sorted (c.iteritems(),key=lambda x:x[1 ]) from collections import Counterc2 = Counter(data) c2.most_common(3 )
如何快速找到多个字典中的公共键
1 2 3 4 5 6 7 from random import randint, samples1 = {x: randint(1 ,4 ) for x in sample('abcdefg' , randint(3 ,6 ))} s2 = {x: randint(1 ,4 ) for x in sample('abcdefg' , randint(3 ,6 ))} s2.viewkeys() & s1.viewkeys() reduce(lambda a, b: a & b, map (dict .viewkeys, [s1, s2]))
如何让字典保持有序 问题:按照创建元素的顺序创建字典
使用 collections 下的 OrderedDict
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from random import randintfrom time import timefrom collections import OrderedDictd = OrderedDict() players = list ('ABCDEFGH' ) start = time() for i in xrange(8 ): raw_input() p = players.pop(randint(0 , 7 - i)) end = time() print i + 1 , p, end - start d[p] = (i + 1 , end - start) print print ' ' * 20 for k in d: print k, d[k]
如何实现用户的历史记录功能(最多 n 条)
使用 collections 中的 deque(双端循环队列)实现
使用 pickle 序列化保存数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from random import randintfrom collections import dequeN = randint(0 , 100 ) history = deque([], 5 ) def guess_digit (k ): if k == N: print 'right' return True if k < N: print '%s is less than N' % k else : print '%s is greater than N' % k return False while True : line = raw_input('please input a number' ) if line.isdigit(): k = int (line) history.append(k) if guess_digit(k): break elif line == 'history' : print list (history) import picklepickle.dump(history,open ('history' ,'w' )) pickle.load('history' )
迭代器与生成器 参考:https://nvie.com/posts/iterators-vs-generators/
可迭代对象需要通过 iter () 方法实现为迭代器,然后才能通过 next() 迭代。
生成器是特殊(优雅)的迭代器(通过 yield 方法实现迭代器)。
一句话就是,只有迭代器对象才可以迭代,可迭代对象的意思就是可以实现为迭代器对象,然后迭代,生成器就是迭代器。
如何实现可迭代对象和迭代器对象 可迭代对象接口:__iter__(),__getitem__(),__next__()
迭代器对象接口:next()
实现一个迭代器对象,next() 方法
实现一个可迭代对象,__iter__() 方法返回一个迭代器对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from collections import Iterable, Iteratorimport requestsclass WeatherIterator (Iterator ): def __init__ (self, cities ): self.cities = cities self.index = 0 def get_weather (self, city ): r = requests.get(u'http://wthrcdn.etouch.cn/weather_mini?city=' + city) data = r.json()['data' ]['forecast' ][0 ] return '%s: %s, %s ' % (city, data['low' ], data['high' ]) def next (self ): if self.index == len (self.cities): raise StopIteration city = self.cities[self.index] self.index += 1 return self.get_weather(city) class WeatherIterable (Iterable ): def __init__ (self, cities ): self.cities = cities def __iter__ (self ): return WeatherIterator(self.cities) for x in WeatherIterable([u'北京' , u'上海' ]): print x
如何使用生成器函数实现可迭代对象
将 __iter__() 方法实现为 yield() 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class PrimeNumbers : def __init__ (self, start, end ): self.start = start self.end = end def is_primenum (self, k ): if k < 2 : return False for i in xrange(2 , k): if k % i == 0 : return False return True def __iter__ (self ): for k in xrange(self.start, self.end + 1 ): if self.is_primenum(k): yield k for x in PrimeNumbers(1 , 100 ): print x
如何进行反向迭代以及如何实现反向迭代 问题:实现一个浮点数发生器,根据范围和步长迭代
引子:如何将列表反向?
1 2 3 4 5 l = [1 , 2 , 3 , 4 , 5 ] l.reverse() l[::-1 ] reversed (l) iter (l)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class FloatFange : def __init__ (self, start, end, step=0.1 ): self.start = start self.end = end self.step = step def __iter__ (self ): t = self.start while t <= self.end: yield t t += self.step def __reversed__ (self ): t = self.end while t >= self.start: yield t t -= self.step for x in reversed (FloatFange(2 , 4 )): print x
如何对迭代器做切片操作 问题:有某个文本文件,我们想读取其中某范围的内容,如 100-300 行的内容
使用标准库中的 itertools.islice,它能返回一个迭代对象切片的生成器
1 2 3 4 5 6 7 from itertools import islicef = open ('test' ) islice(f, 100 , 300 ) islice(f, 500 ) islice(f, 100 , None )
如何在一个 for 语句中迭代多个可迭代对象 问题:
例如有两科成绩,想要算出总成绩
例如有三个班的成绩,想要找出大于 90 分的成绩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from random import randintfrom itertools import chainmath = [randint(0 , 100 ) for x in xrange(40 )] english = [randint(0 , 100 ) for x in xrange(40 )] for m, e in zip (math, english): sum = m + e for s in chain(math,english): if s > 90 : print s
字符串处理 如何拆分含有多种分隔符的字符串 问题:s = ‘ab;cd,|enf|def,kdjk;kdjfk\dd’ 有多种分隔符,如何处理
连续使用 str.split() 方法,每次处理一种分隔符
使用正则表达式的 re.split() 方法,一次性拆分字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import res = 'ab;cd,|enf|def,kdjk;kdjfk sd' def my_split (s, ds ): res = [s] print res for d in ds: t = [] map (lambda x: t.extend(x.split(d)), res) res = t return [x for x in res if x] print my_split(s, ';, |' )split_pattern = '[,; |]' print [x for x in re.split(split_pattern,s) if x]
如何判断字符串 a 是否以字符串 b 开头或者结尾 问题:某文件目录下有一系列文件,例如 .c,.py,.js
使用 str.startwith(),str.endwith(),如果有多个结尾的话,需要用元组传入进去
1 2 3 4 5 6 7 8 import os, stat[name for name in os.listdir('.' ) if name.endswith(('.sh' , '.py' ))] os.stat('e.py' ).st_mode stat.S_IXUSR os.chmod('e.py' , os.stat('e.py' ).st_mode | stat.S_IXUSR)
如何调整字符串中文本的格式 问题:例如需要更改 log 文件中的日期表示形式
使用正则表达式 re.sub() 方法做字符串替换,利用正则表达式的捕获组,捕获每个部分的内容,在替换字符串中调整各个捕获组的顺序
1 2 3 4 import rere.sub('(\d{4})-(\d{2})-(\d{2})' , r'\2/\3/\1' , file) re.sub('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})' , r'\g<month>/\g<day>/\g<year>' , file)
如何将多个小字符串拼接成一个大的字符串 问题:
迭代列表,使用 + 拼接字符串。(有巨大的开销浪费,拼接过的字符串只是一个临时的)
使用 join 方法
1 2 3 4 5 6 7 8 9 10 11 s1 = 'ddjfaljfl;' s2 = 'ddj1222' s1 + s2 l = ['dd' , 'kjdlaj' , 'kke' ] l2 = '' .join(l) l1 = ['dd' , 123 , 'dd' , 45 ] print ['dd' , 'kjdlaj' , 'kke' ]l3 = [str (x) for x in l1] l3 = (str (x) for x in l1)
如何对字符串进行左,右,居中对齐 问题:
使用字符串的 str.ljust(),str.rjust(),str.center() 对齐
使用 format 方法,传递类似 ‘<20’,’>20’,’^20’ 参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 s.ljust(20 ) s.rjust(20 ) s.center(20 ) format (s,'<20' )format (s,'>20' )format (s,'^20' )w = max (map (len , d.key())) for k in d: print k.ljust(w), ':' , d[k]
如何去掉字符串中不需要的字符
字符串 strip(),lstrip(),rstrip() 方法取掉字符串两端字符
删除单个固定位置的字符,可以使用切片+拼接的方式
字符串的 replace() 方法或正则表达式 re.sub() 删除任意位置字符
字符串 translate() 方法,可以同时删除多种不同字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 s = ' abc ' s.strip() s = '---dbc+++' s.strip('-+' ) s = 'dbc:ddd' s[:3 ] + s[4 :] s = '/4kdlkj/4' s.replace('/4' ,'' ) s = 'abc123456xyz' import stringstring.maketrans('abcxyz' ,'xyzabc' ) s.translate(string.maketrans('abcxyz' ,'xyzabc' )) s.translate(None ,'ax' ) unicode.translate()
文件 I/O 操作 如何读写文本文件 关于 python 的编解码,看这一篇就够了:https://foofish.net/why-python-encoding-is-tricky.html
1 2 3 4 5 6 7 8 9 10 11 12 13 s = u'你好' f = open ('filename' ,'w' ) f.write(s.encode('utf-8' )) f.read().decode('utf-8' ) f = open ('filename' ,'rt' ,encoding='utf-8' ) f.write(s) f.seek(0 )
如何处理二进制文件 二进制文件,例如:视频,图片,录音等。
大小端字节
参考:https://blog.csdn.net/hnlyyk/article/details/52541923
大端:低地址存放高字节位
小端:低地址存放低字节位
open 函数指定模式 ‘b’ 打开文件
二进制数据可以用 readinto,读入到提前分配好的 buffer 中
解析二进制数据可以使用标准库中的 struct 模块的 unpack 方法
1 2 3 4 5 6 7 8 9 import structimport arrayf = open ('demo.wav' ,'rb' ) struct.unpack('h' ,'\x01\02' ) struct.unpack('>h' ,'\x01\02' ) f.seek(0 ,2 ) f.tell() n = (f.tell() - 44 )/2 buff = array.array('h' ,(0 for _ in xrange(n)))
如何设置文件的缓冲 问题:
1 2 3 f = open ('demo.txt' ,'w' ,buffering=2048 ) f = open ('demo.txt' ,'w' ,buffering=1 ) f = open ('demo.txt' ,'w' ,buffering=0 )
如何将文件映射到内存
使用标准库中的 mmap 模块的 mmap() 函数
1 2 3 4 5 6 7 8 9 10 11 12 import mmapf = open ('demo.bin' , 'r+b' ) f.fileno() m = mmap.mmap(f.fileno(), 0 , access=mmap.ACCESS_WRITE) m[0 ] m[10 :20 ] m[0 ] = '\x88' mmap.mmap(f.fileno(), mmap.PAGESIZE * 8 , access=mmap.ACCESS_WRITE,offset=mmap.PAGESIZE*4 )
如何访问文件的状态
系统调用:os, stat 模块
os.path 下的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import os, statimport os.paths = os.stat('filename' ) s Out[8 ]: posix.stat_result(st_mode=33188 , st_ino=2373061 , st_dev=16777217 , st_nlink=1 , st_uid=501 , st_gid=20 , st_size=2064 , st_atime=1530613571 , st_mtime=1530613571 , st_ctime=1530613571 ) os.lstat('filename' ) os.fstat('filename' ) stat.S_ISDIR(s.st_mode) os.path.isdir() os.path.islink()
如何使用临时文件
1 2 3 4 5 6 7 8 9 10 from tempfile import TemporaryFile, NamedTemporaryFilef = TemporaryFile() f.write('abcdef' *1000000 ) f.seek(0 ) f.read(100 ) ntf = NamedTemporaryFile() ntf.name Out[17 ]: '/var/folders/6t/b8j3jt1x3nl5bp70ll6fmqfh0000gn/T/tmpa6DRYC'
数据编码与处理 如何读写 csv 数据
使用标准库 csv 模块,可以使用其中 reader 和 writer 完成 csv 文件读写
1 2 3 4 5 6 import csvrf = open ('pingan.csv' ,'rb' ) reader = csv.reader(rf) wf = open ('pingan.csv' ,'wb' ) writer = csv.writer(wf) writer.writewrow()
如何读写 json 数据
使用标准库中的 json 模块,其中 loads,dumps 函数可以完成 json 数据的读写
1 2 3 from record import Record record = Record(channels=1 ) audioData = record.record(2 )
1 2 3 4 5 6 7 8 9 import jsonl = [1 , 2 , 'abc' , {'name' : 'bpb' , 'age' : 'ddd' }] s = json.dumps(l,separators=[',' , ':' ]) json.dumps(d, sort_keys=True ) json.loads(s) json.dump() json.load()
如何解析简单的 xml 文档
1 2 from xml.etree import ElementTree
如何构建 xml 文档 1 2 3 4 5 6 7 8 9 10 11 from xml.etree.ElementTree import Element, ElementTreee = Element('Data' ) e.tag Out[21 ]: 'Data' e.set ('name' , 'abc' ) from xml.etree.ElementTree import Element, ElementTree, tostringtostring(e) Out[24 ]: '<Data name="abc" />' e.text = '123' tostring(e) Out[26 ]: '<Data name="abc">123</Data>'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import csvfrom xml.etree.ElementTree import Element, ElementTreefrom e2 import pretty def csvToXml (fname ): with open (fname, 'rb' ) as f: reader = csv.reader(f) headers = reader.next () root = Element('Data' ) for row in reader: eRow = Element('Row' ) root.append(eRow) for tag, text in zip (headers, row): e = Element(tag) e.text = text eRow.append(e) pretty(root) return ElementTree(root) et = csvToXml('pingan.csv' ) et.write('pingan.xml' )
如何读写 excel 文件
类与对象 如何派生内置不可变类型并修改实例化行为 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class IntTuple (tuple ): def __new__ (cls, iterable ): g = (x for x in iterable if isinstance (x, int ) and x > 0 ) return super (IntTuple, cls).__new__(cls, g) def __init__ (self, iterable ): print self super (IntTuple, self).__init__(iterable) t = IntTuple([1 , -1 , 'abc' , 6 , ['x' , 'y' ], 3 ]) print t
如何为创建大量实例节省内存
定义类的 __slots__ 属性,用来声明实例属性名字的列表
普通的实例创建完成之后,会有一个 __dict__ 方法,这个方法可以动态绑定实例,但是也造成了很大的内存开销,所以可以通过 __slot__ 属性,阻止动态绑定实例
如何让对象支持上下文管理器
实现上下文管理协议,需要定义实例的 __enter__, __exit__ 方法,它们分别在 with 开始和结束时被调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 def __enter (self ): return self def __exit__ (self, exc_type, exc_val, exc_tb ): return True from telnetlib import Telnetfrom sys import stdin, stdoutfrom collections import dequeclass TelnetClient (object ): def __init__ (self, addr, port=23 ): self.addr = addr self.port = port self.tn = None def start (self ): raise Exception('Test' ) t = self.tn.read_until('login: ' ) stdout.write(t) user = stdin.readline() self.tn.write(user) t = self.tn.read_until('Password: ' ) if t.startswith(user[:-1 ]): t = t[len (user) + 1 :] stdout.write(t) self.tn.write(stdin.readline()) t = self.tn.read_until('$ ' ) stdout.write(t) while True : uinput = stdin.readline() if not uinput: break self.history.append(uinput) self.tn.write(uinput) t = self.tn.read_until('$ ' ) stdout.write(t[len (uinput) + 1 :]) def cleanup (self ): pass def __enter__ (self ): self.tn = Telnet(self.addr, self.port) self.history = deque() return self def __exit__ (self, exc_type, exc_val, exc_tb ): print 'In __exit__' self.tn.close() self.tn = None with open (self.addr + '_history.txt' , 'w' ) as f: f.writelines(self.history) return True with TelnetClient('127.0.0.1' ) as client: client.start() print 'END' ''' client = TelnetClient('127.0.0.1') print '\nstart...' client.start() print '\ncleanup' client.cleanup() '''
如何创建可管理的对象属性 问题:不想让用户通过属性来管理,形式上是使用属性访问,实际上调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from math import piclass Circle (object ): def __init__ (self, radius ): self.radius = radius def getRadius (self ): return self.radius def setRadius (self, value ): if not isinstance (value, (int , long, float )): raise ValueError('wrong type.' ) self.radius = float (value) def getArea (self ): return self.radius ** 2 * pi R = property (getRadius, setRadius) c = Circle(3.2 ) c.R c.R = 23 print c.R
如何让类支持比较操作
比较运算符重载,需要实现以下方法:__lt__, __le__, __gt__, __ge__, __eq__, __ne__
使用标准库下的 functools 下的类装饰器 total_ordering 可以简化此过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Rectangle (object ): def __init__ (self, w, h ): self.w = w self.h = h def area (self ): return self.w * self.h def __lt__ (self, obj ): print 'in__lt__' return self.area() < obj.area() from functools import total_orderingfrom abc import abstractmethod@total_ordering class Shape (object ): @abstractmethod def area (self ): pass def __lt__ (self, obj ): print 'in__lt__' if not isinstance (obj, Shape): raise TypeError('obj is not shape' ) return self.area() < obj.area() def __eq__ (self, obj ): print 'in__eq__' if not isinstance (obj, Shape): raise TypeError('obj is not shape' ) return self.area() == obj.area() class Rectangle (Shape ): def __init__ (self, w, h ): self.w = w self.h = h def area (self ): return self.w * self.h class Circle (Shape ): def __init__ (self, r ): self.r = r def area (self ): return self.r ** 2 * 3.14 r1 = Rectangle(5 , 3 ) r2 = Circle(3 ) print r1 < r2 print r2 < r1
如何使用描述符对实例属性做类型检查 要求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Attr (object ): def __init__ (self, name, type_ ): self.name = name self.type_ = type_ def __get__ (self, instance, cls ): return instance.__dict__[self.name] def __set__ (self, instance, value ): if not isinstance (value, self.type_): raise TypeError('expected an %s' % self.type_) instance.__dict__[self.name] = value def __delete__ (self, instance ): raise AttributeError("can't delete this attr" ) class Person (object ): name = Attr('name' , str ) age = Attr('age' , int ) height = Attr('height' , float ) weight = Attr('weight' , float ) s = Person() s.name = 'Bob' s.age = 17 s.height = 1.82 s.weight = 52.5
如何在环状数据结构中管理内存
使用标准库 weakref,它可以创建一种能访问对象但不增加引用计数的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import weakrefclass Data (object ): def __init__ (self, value, owner ): self.owner = weakref.ref(owner) self.value = value def __str__ (self ): return "%s's data, value is %s" % (self.owner(), self.value) def __del__ (self ): print 'in Data.__del__' class Node (object ): def __init__ (self, value ): self.data = Data(value, self) def __del__ (self ): print 'in Node.__del__' node = Node(100 ) del noderaw_input('wait...' )
如何通过实例方法名字的字符串调用方法
使用内置函数 getattr,通过名字在实例上获取方法对象,然后调用
使用标准库 operator 下的 methodcaller 函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from lib1 import Cirlefrom lib2 import Trianglefrom lib3 import Rectangledef getArea (shape ): for name in ('area' , 'getArea' , 'get_area' ): f = getattr (shape, name, None ) if f: return f() shape1 = Circle(2 ) shape2 = Triangle(3 , 4 , 5 ) shape3 = Rectangle(6 , 4 ) shapes = [shape1, shape2, shape3] print map (getArea, shapes)from operator import methodcallers = 'abc123abc456' print s.find('abc' , 4 )print methodcaller('find' , 'abc' , 4 )(s)
多线程与多进程 如何使用多线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from threading import Threadclass MyThread (Thread ): def __init__ (self, sid ): Thread.__init__(self) self.sid = sid def run (self ): handle(self.sid) threads = [] for i in xrange(1 , 11 ): t = MyThread(i) threads.append(t) t.start() for t in threads: t.join()
如何线程间通信
如何在线程间进行事件通知
1 from threading import Event, Thread
如何使用线程本地数据 如何使用线程池 如何使用多进程 装饰器 如何使用函数装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 """ 题目 1 计算斐波那契数列 1,1,2,3,5,8,13 """ def cache (func ): cache = {} def wrap (*args ): if args not in cache: cache[args] = func(*args) return cache[args] return wrap @cache def fibonacci (n ): if n <= 1 : return 1 return fibonacci(n - 1 ) + fibonacci(n - 2 ) def fibonacci_cache (n, cache=None ): if cache is None : cache = {} if n in cache: return cache[n] if n <= 1 : return 1 cache[n] = fibonacci_cache(n - 1 , cache) + fibonacci_cache(n - 2 , cache) return cache[n] @cache def climb (n, steps ): count = 0 if n == 0 : count = 1 elif n > 0 : for step in steps: count += climb(n - step, steps) return count print climb(20 , (1 , 2 ,3 ))
如何为被装饰的函数保存元数据
参考:https://foofish.net/python-decorator.html
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring
、__name__
、参数列表,先看例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def logged (func ): def with_logging (*args, **kwargs ): print func.__name__ print func.__doc__ return func(*args, **kwargs) return with_logging @logged def f (x ): """does some math""" return x + x * x logged(f)
1 2 3 4 5 6 7 8 9 10 11 12 13 from functools import wrapsdef logged (func ): @wraps(func ) def with_logging (*args, **kwargs ): print func.__name__ print func.__doc__ return func(*args, **kwargs) return with_logging @logged def f (x ): """does some math""" return x + x * x
如何定义带参数的装饰器
带参数的装饰器 装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)
。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def use_logging (level ): def decorator (func ): def wrapper (*args, **kwargs ): if level == "warn" : logging.warn("%s is running" % func.__name__) elif level == "info" : logging.info("%s is running" % func.__name__) return func(*args) return wrapper return decorator @use_logging(level="warn" ) def foo (name='foo' ): print ("i am %s" % name) foo()
上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")
调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。
@use_logging(level="warn")
等价于@decorator
如何实现属性可修改的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from functools import wrapsimport time, loggingfrom random import randintdef warn (timeout ): timeout = [timeout] def decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): start = time.time() res = func(*args, **kwargs) used = time.time() - start if used > timeout[0 ]: msg = '%s, %s, %s' % (func.__name__, used, timeout[0 ]) logging.warn(msg) return res def setTimeout (k ): timeout[0 ] = k wrapper.setTimeout = setTimeout return wrapper return decorator @warn(1.5 ) def test (): print 'In test' while randint(0 , 1 ): time.sleep(0.5 ) for _ in range (30 ): test() test.setTimeout(1 ) for _ in range (30 ): test()