一、数据结构和算法 1. 解压序列赋值给多个变量 1 2 3 >>> year, month, day = [2002 , 6 , 10 ]>>> print (year, month, day)2002 6 10
可以用占位符,丢弃其他的值
1 2 3 year, _ , _ = [2002 ,6 ,10 ] print (year)2002
2. 解压可迭代对象赋值给多个变量 1 2 3 4 L = [1 ,2 ,3 ,4 ,5 ] first, *middle, last = grades print (middle)>>> [2 ,3 ,4 ]
3. 保留最后N个元素 使用deque,指定maxlen参数的值为AN
1 2 3 4 5 6 7 8 9 >>> from collections import deque>>> dq = deque(maxlen=3 )>>> dq.append(1 )>>> dq.append(2 )>>> dq.append(3 )>>> dq.append(4 )>>> dq.append(5 )>>> print (dq)deque([3 , 4 , 5 ], maxlen=3 )
4. 查找最大或最小的N个元素 利用heaq模块的nlargest()和nsmallest()方法
1 2 3 4 5 6 7 >>> nums = [3 , 5 , 2 , 4 , 1 ]>>> smallest = heapq.nsmallest(3 , nums)>>> print (smallest)[1 , 2 , 3 ] >>> largest = heapq.nlargest(3 , nums)>>> print (largest)[5 , 4 , 3 ]
5. 字典中的键如何映射多个值 可以将多个值放到列表或集合里。 使用defaultdict自动初始化每个key,只需关注添加元素操作,如下:
1 2 3 4 5 6 7 8 9 >>> from collections import defaultdict>>> d = defaultdict(list )>>> d['a' ].append(1 )>>> print (d)defaultdict(<class 'list' >, {'a' : [1 ]}) >>> d = defaultdict(set )>>> d['a' ].add(1 )>>> print (d)defaultdict(<class 'set' >, {'a' : {1 }})
8. 在数据字典中执行计算操作(比如求最小值,最大值,排序等) 考虑下面的股票名和价格
1 2 3 4 5 6 7 prices = { 'ACME' : 45.23 , 'AAPL' : 612.78 , 'IBM' : 205.55 , 'HPQ' : 37.20 , 'FB' : 10.75 }
比如查找最小和最大股票价格和股票名称
1 2 3 4 min_price = min (zip (prices.values(), prices.keys())) max_price = max (zip (prices.values(), prices.keys()))
使用zip和sorted函数排列字典数据
1 prices_sorted = sorted (zip (prices.values(), prices.keys()))
9. 寻找两个字典的相同点 1 2 3 4 5 a = {'x' : 1 , 'y' : 2 , 'z' : 3 } b = {'w' : 10 , 'x' : 11 , 'y' : 2 } a.keys() & b.keys() a.items() & b.items()
以现有字典构造一个排除几个指定键的字典 1 2 c = {key: a[key] for key in a.keys() - {'z' , 'w' }}
10.删除序列相同元素并保持顺序 1 2 3 4 5 6 def dedupe (items ): seen = set () for item in items(): if item not in seen: yield item seen.add(item)
11. 给切片命名,是代码清晰可读 利用slice函数,消除代码中的硬编码,使代码清晰可读
1 2 3 4 >>> ip="<127.0.0.1>" >>> GET_IP = slice (1 ,-1 )>>> ip[GET_IP]'127.0.0.1'
12. 找出序列中出现次数最多的元素 使用collections.Counter类
1 2 3 4 5 6 words = ['look' , 'into' , 'into' , 'the' , 'sky' ] from collections import Counterword_counts = Counter(words) top_two = word_counts.most_common(2 ) python test.py [('into' , 2 ), ('look' , 1 )]
13. 通过某个关键字排序一个字典列表 使用operator模块的itemgetter
函数
1 2 3 4 5 6 rows = [ {'fname' : 'Brian' , 'lname' : 'Jones' , 'uid' : 1003 }, {'fname' : 'David' , 'lname' : 'Beazley' , 'uid' : 1002 }, {'fname' : 'John' , 'lname' : 'Cleese' , 'uid' : 1001 }, {'fname' : 'Big' , 'lname' : 'Jones' , 'uid' : 1004 } ]
根据uid关键字排序
1 2 3 4 5 6 7 from operator import itemgetterrows_by_fname = sorted (rows, key=itemgetter('fname' )) rows_by_uid = sorted (rows, key=itemgetter('uid' )) [{'fname' : 'John' , 'lname' : 'Cleese' , 'uid' : 1001 }, {'fname' : 'David' , 'lname' : 'Beazley' , 'uid' : 1002 }, {'fname' : 'Brian' , 'lname' : 'Jones' , 'uid' : 1003 }, {'fname' : 'Big' , 'lname' : 'Jones' , 'uid' : 1004 }]
14. 排序不支持原生比较的对象 1 2 3 4 5 6 7 8 9 10 11 12 class User : def __init__ (self, user_id ): self .user_id = user_id def __repr__ (self ): return 'User({})' .format (self .user_id) users = [User(23 ),User(3 ),User(99 )] print (users)print (sorted (users, key = lambda u: u.user_id))[User(23 ), User(3 ), User(99 )] [User(3 ), User(23 ), User(99 )]
15. 通过某个字段将记录分组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from operator import itemgetterfrom itertools import groupbyrows = [ {'address' : '5412 N CLARK' , 'date' : '07/01/2012' }, {'address' : '5148 N CLARK' , 'date' : '07/04/2012' }, {'address' : '5800 E 58TH' , 'date' : '07/02/2012' }, {'address' : '2122 N CLARK' , 'date' : '07/03/2012' }, {'address' : '5645 N RAVENSWOOD' , 'date' : '07/02/2012' }, {'address' : '1060 W ADDISON' , 'date' : '07/02/2012' }, {'address' : '4801 N BROADWAY' , 'date' : '07/01/2012' }, {'address' : '1039 W GRANVILLE' , 'date' : '07/04/2012' }, ] rows.sort(key=itemgetter('date' )) for date, items in groupby(rows, key=itemgetter('date' )): print (date) for item in items: print (' ' , item)
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 07/01/2012 {'address' : '5412 N CLARK' , 'date' : '07/01/2012' } {'address' : '4801 N BROADWAY' , 'date' : '07/01/2012' } 07/02/2012 {'address' : '5800 E 58TH' , 'date' : '07/02/2012' } {'address' : '5645 N RAVENSWOOD' , 'date' : '07/02/2012' } {'address' : '1060 W ADDISON' , 'date' : '07/02/2012' } 07/03/2012 {'address' : '2122 N CLARK' , 'date' : '07/03/2012' } 07/04/2012 {'address' : '5148 N CLARK' , 'date' : '07/04/2012' } {'address' : '1039 W GRANVILLE' , 'date' : '07/04/2012' }
16. 过滤序列元素 最简单的方法是使用列表推导,例如:
1 2 3 4 5 >>> mylist = [1 , 4 , -5 , 10 , -7 , 2 , 3 , -1 ]>>> [n for n in mylist if n > 0 ][1 , 4 , 10 , 2 , 3 ] >>> [n for n in mylist if n < 0 ][-5 , -7 , -1 ]
如果对内存敏感,可以用生成器表达式,例如:
1 2 3 4 5 6 7 8 9 10 >>> pos = (n for n in mylist if n > 0 )>>> for x in pos:... print (x)... 1 4 10 2 3 >>>
如果过滤规则复杂,不能简单用列表推导,可以考虑用内建的filter()函数,例如:
1 2 3 4 5 6 7 8 9 values = ['1' , '2' , '-' , 'N/A' ] def is_int (val ): try : x = int (val) return True except : return False ivals = list (filter (is_int, values)) print (ivals)
如果你需要用另一个相关联的序列来过滤某个序列时,可以考虑itertools.compress()函数,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 addresses = [ '5412 N CLARK' ,'5148 N CLARK' ,'5800 E 58TH' ,'2122 N CLARK' ,'5645 N RAVENSWOOD' ,'1060 W ADDISON' ,'4801 N BROADWAY' ,'1039 W GRANVILLE' ,] counts = [ 0 , 3 , 10 , 4 , 1 , 7 , 6 , 1 ] from itertools import compressmore5 = [ n > 5 for n in counts] result = list (compress(addresses, more5)) print (result)>>> ['5800 E 58TH' , '1060 W ADDISON' , '4801 N BROADWAY' ]
从字典中提取子集 最简单的方式是使用字典推导, 例如:
1 2 3 4 5 6 7 8 9 10 11 prices = { 'ACME' : 45.23 ,'AAPL' : 612.78 ,'IBM' : 205.55 ,'HPQ' : 37.20 ,'FB' : 10.75 } p1 = {key: value for key, value in prices.items() if value > 200 } >>> {'AAPL' : 612.78 , 'IBM' : 205.55 }
二、字符串和文本 使用多个界定符分割字符串 re.split()
方法, 可以更加灵活的切割字符串
1 2 3 4 >>> line = 'asdf fjdk; afed, fjek,asdf, foo' >>> import re>>> re.split(r'[;,\s]\s*' , line)['asdf' , 'fjdk' , 'afed' , 'fjek' , 'asdf' , 'foo' ]
字符串开头或结尾匹配 利用startswith()
和endswith()
方法
1 2 3 4 5 6 >>> filename = 'spam.txt' >>> filename.endswith('.txt' )True url = 'http://www.python.org' >>> url.startswith('http:' )True
用Shell通配符匹配字符串 Python中可以使用fnmatch()
和fnmatchcase()
1 2 3 4 5 6 7 >>> from fnmatch import fnmatch, fnmatchcase>>> fnmatch('foo.txt' , '*.txt' )True >>> fnmatch('foo.txt' , '?oo.txt' )True >>> fnmatch('Dat45.csv' , 'Dat[0-9]*' )True
字符串搜索匹配 在字符串中搜索字符串,并返回下标, 使用find()函数
1 2 3 4 5 >>> text = 'yeah, but no, but yeah' >>> text.find('no' )10 >>> text.find('not' )-1
对于复杂匹配使用正则表达式和re模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> text1 = '11/27/2012' >>> text2 = 'Nov 27, 2012' >>> >>> import re>>> >>> if re.match (r'\d+/\d+/\d+' , text1):... print ('yes' )... else :... print ('no' )... yes >>> if re.match (r'\d+/\d+/\d+' , text2):... print ('yes' )... else :... print ('no' )... no >>>
注:如果使用同一个模式去多次匹配,应该将模式串预编译为模式对象,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> datepat = re.compile (r'\d+/\d+/\d+' )>>> if datepat.match (text1):... print ('yes' )... else :... print ('no' )... yes >>> if datepat.match (text2):... print ('yes' )... else :... print ('no' )... no >>>
字符串搜索和替换 对于简单字符串替换,用str.replace()函数
1 2 3 >>> text = 'yeah, but no, but yeah, but no, but yeah' >>> text.replace('yeah' , 'yep' )'yep, but no, but yep, but no, but yep'
对于复杂的模式,用re模块的sub函数
1 2 3 4 5 >>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.' >>> import re>>> re.sub(r'(\d+)/(\d+)/(\d+)' , r'\3-\1-\2' , text)'Today is 2012-11-27. PyCon starts 2013-3-13.' >>>
字符串忽略大小写的搜索替换 使用re模块时添加re.IGNORECASE参数
1 2 3 4 5 6 >>> text = 'UPPER PYTHON, lower python, Mixed Python' >>> re.findall('python' , text, flags=re.IGNORECASE)['PYTHON' , 'python' , 'Python' ] >>> re.sub('python' , 'snake' , text, flags=re.IGNORECASE)'UPPER snake, lower snake, Mixed snake' >>>
删除字符串中不需要的字符 strip方法可以删除开头和结尾的空白字符
lstrip()和rstrip()分别从左和从右执行删除操作
除了默认的空白字符,也可以指定其他字符,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> s = ' hello world \n' >>> s.strip()'hello world' >>> s.lstrip()'hello world \n' >>> s.rstrip()' hello world' >>> >>> >>> t = '-----hello=====' >>> t.lstrip('-' )'hello=====' >>> t.strip('-=' )'hello' >>>
如果要删除中间的空格,需要用其他技术, 比如replace方法或正则表达式替换
1 2 3 >>> s = ' hello world \n' >>> s.replace(' ' , '' )'helloworld'
字符串对齐 基本的字符串对齐操作,可以使用字符串的ljust(),rjust(),center()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> text = 'Hello World' >>> text.ljust(20 )'Hello World ' >>> text.rjust(20 )' Hello World' >>> text.center(20 )' Hello World ' >>> >>> text.rjust(20 ,'=' )'=========Hello World' >>> text.center(20 ,'*' )'****Hello World*****' >>>
合并字符串 如果待合并的字符串在一个序列中, 最快的方法是使用join()
1 2 3 4 5 6 7 >>> parts = ['Is' , 'Chicago' , 'Not' , 'Chicago?' ]>>> ' ' .join(parts)'Is Chicago Not Chicago?' >>> '|' .join(parts)'Is|Chicago|Not|Chicago?' >>> '' .join(parts)'IsChicagoNotChicago?'
注意不必要的字符串连接操作,比如打印时
1 2 3 print (a + ':' + b + ':' + c) print (':' .join([a, b, c])) print (a, b, c, sep=':' )
字符串中插入变量 使用format函数
1 2 '{name} has {n} message.' .format (name='peter' , n=5 )'peter has 5 message.'
三、数字日期和时间 数字四舍五入 简单的舍入运算,使用内置round(value, ndigits)
格式化输出,使用内置format函数
1 2 x = 1234.56789 >>> format (x, '0.2f' )
二八十六进制整数 为了将整数转为二进制,八进制,十六进制文本串。分别使用bin, oct, hex函数
1 2 3 4 5 6 7 8 9 x = 1234 >>> bin (x)'0b10011010010' >>> oct (x)'0o2322' >>> hex (x)'0x4d2' >>> format (x, 'b' )'10011010010'
随机选择 使用random.choice(), 从一个序列中随机抽取一个元素
1 2 3 4 >>> import random>>> values = [1 ,2 ,3 ,4 ,5 ,6 ]>>> random.sample(values, 3 )[3 , 1 , 2 ]
随机打乱元素顺序, 使用random.shuffle()
1 2 3 >>> random.shuffle(values)>>> values[2 , 4 , 6 , 5 , 3 , 1 ]
生成随机整数,用random.randint()
1 2 >>> random.randint(0 ,10 )10
生成0-1的浮点数
1 2 >>> random.random()0.9406677561675867
第四章 迭代器与生成器 不用for语句迭代 1 2 3 4 5 6 7 8 L = [1 ,2 ,3 ,4 ,5 ] it = iter (L) try : while True : item = next (it) print (item) except StopIteration: pass
反向迭代 1 2 3 4 5 6 7 8 >>> a = [1 , 2 , 3 , 4 ]>>> for x in reversed (a):... print (x)... 4 3 2 1
序列上索引值迭代 迭代同时,跟踪被处理元素索引 使用内置的enumerate
函数
1 2 3 4 5 6 7 >>> my_list = ['a' , 'b' , 'c' ]>>> for idx, val in enumerate (my_list):... print (idx, val)... 0 a1 b2 c
指定开始参数
>>> my_list = ['a', 'b', 'c']
>>> for idx, val in enumerate(my_list, 1):
... print(idx, val)
...
1 a
2 b
3 c
同时迭代多个序列 使用zip函数
1 2 3 4 5 xpts = [1 ,2 ,3 ,4 ,5 ] ypts = [6 ,7 ,8 ,9 ,10 ] for x,y in zip (xpts,ypts): print ('x={}, y={}' .format (x, y))
zip函数会创建一个迭代器作为结果返回,如果需要将结对值存到列表,使用list函数
1 2 3 4 >>> zip (a, b)<zip object at 0x1007001b8 > >>> list (zip (a, b))[(1 , 10 ), (2 , 11 ), (3 , 12 )]
不同集合上元素迭代 场景:想在多个对象执行相同操作, 但对象在不同容器中,希望代码在不失可读性情况下避免写重复循环
使用itertools.chain()方法
1 2 3 4 5 6 7 8 >>> from itertools import chain>>> a = [1 , 2 , 3 , 4 ]>>> b = ['x' , 'y' , 'z' ]>>> for x in chain(a, b):... print (x)... 1234xyz >>>
顺序迭代合并后排序迭代对象 使用heapq.merge()
1 2 3 4 5 6 7 8 9 10 >>> import heapq>>> a = [1 , 4 , 7 , 10 ]>>> b = [2 , 5 , 6 , 11 ]>>> for c in heapq.merge(a, b):... print (c)... 1 2 4 ...
heapq.merge
可迭代特性意味着它不会立马读取所有序列。 这就意味着你可以在非常长的序列中使用它,而不会有太大的开销。
heapq.merge()
需要所有输入序列必须是排过序的。
使用其他分隔符或行终止符打印 1 2 print ('ACME' ,50 ,sep=',' ,end='!!\n' )ACME,50 !!
输出中禁止换行,用end参数
1 2 3 for i in range (5 ): print (i, end=' ' ) 0 1 2 3 4 >>>
函数 定义有默认参数的函数 默认参数的值仅仅在函数定义的时候赋值一次
默认参数的值应该是不可变的对象,比如None、True、False、数字或字符串
测试None值时使用 is
操作符是很重要的
八、类对象 改变一个实例字符串表示,可通过重新定义str和repr方法实现: 1 2 3 4 5 6 7 class Pair : def __init__ (self, x, y ): self .x = x self .y = y def __repr__ (self ): return 'Pair({0.x!r}, {0.y!r})' .format (self )
类中定义等多个构造器 想实现一个类,除了使用init方法,还有其他方法初始化它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import timeclass Date :""" 方法一:使用类方法""" def __init__ (self, year, month, day ):self .year = yearself .month = monthself .day = day@classmethod def today (cls ):t = time.localtime() return cls(t.tm_year, t.tm_mon, t.tm_mday)a = Date(2012 , 12 , 21 ) b = Date.today()
通过字符串调用对象方法
使用attr方法
1 2 3 4 5 6 7 8 9 10 11 import mathclass Point : def __init__ (self, x, y ): self .x = x self .y = y def __repr__ (self ): return 'Point({!r:},{!r:})' .format (self .x, self .y) def distance (self, x, y ): return math.hypot(self .x - x, self .y - y) p = Point(2 , 3 ) d = getattr (p, 'distance' )(0 , 0 )
另一种方法是使用operator.methodcaller()
1 2 import operatoroperator.methodcaller('distance' , 0 , 0 )(p)
第十一章 网络与web编程 HTTP请求 urllib.request
创建TCP服务器 socketserver (单线程)
第十二章 并发编程 threading库创建线程 1 2 3 4 5 6 7 8 9 10 11 import timedef countdown (n ): while n > 0 : print ('T-minus' , n) n -= 1 time.sleep(5 ) from threading import Threadt = Thread(target=countdown, args=(10 ,)) t.start()
线程间通信
使用Queue对象,Queue对象已包含必要锁,可以用它在多个线程间安全共享数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from queue import Queuefrom threading import Threaddef producer (out_q ): while True : ... out_q.put(data) def consumer (in_q ): while True : data = in_q.get() ... q = Queue() t1 = Thread(target=consumer, args=(q,)) t2 = Thread(target=producer, args=(q,)) t1.start() t2.start()
对于生产者消费者速度有差异情况,可以为队列元素添加上限
get和put方法支持非阻塞方式和设定超时
给关键代码加锁 —— 使用threading库中的Lock对象 1 2 3 lock = threading.Lock() with lock: XXX
第十三章 脚本编程与系统管理 1 2 3 4 5 6 7 8 解析命令行选项 argparse 执行外部命令,获取输出 subprocess.check\_output 复制或启动文件 shutil模块 读取ini配置文件 configparser