From 8d8c4a11b46be8206664f72068de179b502beeae Mon Sep 17 00:00:00 2001 From: Jimmy Xiang Date: Mon, 16 May 2016 10:12:00 -0600 Subject: [PATCH 1/9] add src --- ...45\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" | 0 Chapter1/Chapter1.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 "Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" create mode 100644 Chapter1/Chapter1.md diff --git "a/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" "b/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" new file mode 100644 index 0000000..e69de29 diff --git a/Chapter1/Chapter1.md b/Chapter1/Chapter1.md new file mode 100644 index 0000000..e69de29 From 5435d18534d8c0d75d2611e7389536c1533c1f69 Mon Sep 17 00:00:00 2001 From: Jimmy Xiang Date: Tue, 17 May 2016 10:12:00 -0600 Subject: [PATCH 2/9] update readme --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 00b0041..e7dbebc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,25 @@ -python-cookbook -=============== +##python-cookbook 第三版 -Code samples from the "Python Cookbook, 3rd Edition", published by O'Reilly & Associates, May, 2013. \ No newline at end of file + +##目录 +- [第1章 数据结构和算法](Chapter1/) +- 第2章 字符串和文本 +- 第3章 数字,日期和时间 +- 第4章 迭代器和生成器 +- 第5章 文件和I/O +- 第6章 数据编码与处理 +- 第7章 函数 +- 第8章 类与对象 +- 第9章 元编程 +- 第10章 模块与包 +- 第11章 网络和Web编程 +- 第12章 并发 +- 第13章 实用脚本和系统管理 +- 第14章 测试,调试以及异常 +- 第15章 C语言扩展 + + + + +##注意 +代码使用Python3 From 2cbfe9aba6f239e7c1c2780620cb288d4fc9abca Mon Sep 17 00:00:00 2001 From: Jimmy Xiang Date: Wed, 18 May 2016 10:12:00 -0600 Subject: [PATCH 3/9] add Chapter1 --- ...54\347\232\204\345\217\230\351\207\217.py" | 2 ++ Chapter1/Chapter1.md | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git "a/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" "b/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" index e69de29..63f77b6 100644 --- "a/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" +++ "b/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 + diff --git a/Chapter1/Chapter1.md b/Chapter1/Chapter1.md index e69de29..03712fb 100644 --- a/Chapter1/Chapter1.md +++ b/Chapter1/Chapter1.md @@ -0,0 +1,24 @@ +##第1章 数据结构和算法 + +##问题 +- 1.1 将序列分解为单独的变量 +- 1.2 从任意长度的可迭代对象中分解元素 +- 1.3 保存最后N个元素 +- 1.4 找到最大或最小的N个元素 +- 1.5 实现优先级队列 +- 1.6 在字典中将键映射到多个值上 +- 1.7 让字典保持有序 +- 1.8 与字典有关的计算问题 +- 1.9 在两个字典中寻找相同点 +- 1.10 从序列中移除重复项并保持元素间顺序不变 +- 1.11 对切片命名 +- 1.12 找出序列中出现次数最多的元素 +- 1.13 通过公共键对字典列表排序 +- 1.14 对不原生支持比较操作的对象排序 +- 1.15 根据字段将记录分组 +- 1.16 筛选序列中的元素 +- 1.17 从字典中提权子集 +- 1.18 将名称映射到序列的元素中 +- 1.19 同时对数据做转换和换算 +- 1.20 将对个映射合并为单个映射 + From 9152073a49419e711e4459545ca34b1265396ae1 Mon Sep 17 00:00:00 2001 From: Jimmy Xiang Date: Thu, 11 Aug 2016 15:01:00 -0600 Subject: [PATCH 4/9] add Chapter1 --- ...54\347\232\204\345\217\230\351\207\217.py" | 2 - Chapter1/Chapter1.py | 477 ++++++++++++++++++ Chapter1/ch1_3.txt | 9 + 3 files changed, 486 insertions(+), 2 deletions(-) delete mode 100644 "Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" create mode 100644 Chapter1/Chapter1.py create mode 100644 Chapter1/ch1_3.txt diff --git "a/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" "b/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" deleted file mode 100644 index 63f77b6..0000000 --- "a/Chapter1/1.1 \345\260\206\345\272\217\345\210\227\345\210\206\350\247\243\344\270\272\345\215\225\347\213\254\347\232\204\345\217\230\351\207\217.py" +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env python3 - diff --git a/Chapter1/Chapter1.py b/Chapter1/Chapter1.py new file mode 100644 index 0000000..5b39427 --- /dev/null +++ b/Chapter1/Chapter1.py @@ -0,0 +1,477 @@ +#!/usr/bin/env python3 +#!encoding=utf-8 + + +def ch1_1(): + ''' + 1.1 将序列分解为单独的变量 + 对象可迭代,就可以分解,包括字符串,文件,迭代器,生成器 + 分解操作可以使用"_"来丢弃一些特定的值 + ''' + print("\nch1_1:") + data=['hello','ch1',["*","_"], (-6,1,4)] + + _,a,[_,b], (_,middle,_) = data + + print(a+b+str(middle)) #ch1_1 + + + +def ch1_2(): + ''' + 1.2 从任意长度的可迭代对象中分解元素 + "*表达式" 用于未知长度或者任意长度的对象分解 + 分解linux shadow文件字符串 + ''' + print("\nch1_2:") + + linux_shadow = "username:$1$jMzjGK//$Do9jjAM9TqHVhkH3eSytT.:14576:0:99999:7:::" + username,passwd,*drop = linux_shadow.split(":") + _, encrpty_type, encrpty_salt,encrptyed_str = passwd.split("$") + + print("type:%s\nsalt:%s\npasswd:%s\n" % (encrpty_type,encrpty_salt,encrptyed_str)) + + + +def ch1_3(): + ''' + 1.3 保存最后N个元素 + 使用colletions.deque + 添加,弹出均为O(1) + ''' + print("\nch1_3:") + + from collections import deque + + def search(lines, pattern, history=5): + pre_lines = deque(maxlen=history) #保存最后5个搜索结果 + for line in lines: + if pattern in line: + yield line, pre_lines + pre_lines.append(line) + + + with open('ch1_3.txt') as f: + for line, prelines in search(f, 'Java', 5): + print(prelines, len(prelines)) + print(line) + + +def ch1_4(): + ''' + 1.4 找到最大或者最小的N个元素 + 使用heapq中的nlargest和nsmallest + 找出学生成绩的前五名 + ''' + print("\nch1_4:") + + from heapq import nlargest,heapify,heappop + from operator import itemgetter + + #元素数量相对较小时使用nlargest nsmallest + dict1 = {'name1': 34, 'name2':45, 'name3': 98, 'name4':34, 'name5': 66, "name6":90, "name7":90} + top5 = nlargest(5, dict1.items(),key=lambda x:x[1]) #注意python3 iteritem没有了 用items替代了 + print(top5) + + + #如果元素总数很大,N很小 + heap = list(dict1.values()) #转化为list 变成堆 pop + heapify(heap) + for i in range(5): #输出最小的5个值 + print(heappop(heap)) + + #如果元素数量和N差不多大,建议排序再做切片 + for name,score in sorted(dict1.items(), key=itemgetter(1),reverse=True)[:5]: + print(name,score) + + +def ch1_5(): + ''' + 1.5 实现优先级队列 + 使用heapq来pop优先级最高的元素 + ''' + print("\nch1_5:") + + import heapq + + class PrioriyQueue: + def __init__(self): + self._queue=[] + self._index = 0 + def push(self, item, priority): + heapq.heappush(self._queue, [-priority,self._index,item]) #从高到低排列 + def pop(self): + return heapq.heappop(self._queue)[-1] + + q= PrioriyQueue() + q.push('1',1) + q.push('10',10) + q.push('100',100) + q.push('50',50) + q.push('double 50',50) + q.push('25',25) + + for i in range(5): + print(q.pop()) + + + +def ch1_6(): + ''' + 1.6 在字典中将键映射到多个值上 (一键多值字典) + 使用collections中的defaultdir + ''' + + print("\nch1_6:") + + from collections import defaultdict + d = defaultdict(list) + #一个键对应一个list + d['a'].append(1) + d['a'].append(2) + d['a'].append(3) + + d['b'].append(4) + + print(d) + + +def ch1_7(): + ''' + 1.7 让字典保持有序 + 使用collections中的OrderedDict类 + ''' + + print("\nch1_7:") + + from collections import OrderedDict + #OrderedDict 中维护了一个双向链接,来保持顺序加入的位置,大量数据会增大内存消耗 + d= OrderedDict() + d['1'] = 1 + d['2'] = 2 + d['3'] = 3 + d['4'] = 4 + + for key in d: + print(key,d[key]) + + +def ch1_8(): + ''' + 1.8 与字典有关的计算问题 如最小值,最大值,排序等 + 使用zip把字典的键值反过来 + ''' + print("\nch1_8:") + + scores = {'name1': 34, 'name2':45, 'name3': 98, 'name4':34, 'name5': 66, "name6":90, "name7":90} + + min_score = min(zip(scores.values(),scores.keys())) + max_score = max(zip(scores.values(),scores.keys())) + scores_sorted = sorted(zip(scores.values(), scores.keys()),reverse=True) + + print(min_score,max_score) + print(scores_sorted) + + +def ch1_9(): + ''' + 1.9 在两个字典中寻找相同点 + 寻找相同的键,相同的值等 + 使用集合操作 如 & - + ''' + + print("\nch1_9:") + + a={'x':1,'y':2,'z':3} + b={'w':100,'x':50,'y':2} + + print(a.keys() & b.keys()) + print(a.keys() - b.keys()) + print(a.items()& b.items()) + + + +def ch1_10(): + ''' + 1.10 从序列中移除重复项且保持元素间顺序不变 + 利用set和生成器,返回不同的元素 + ''' + print("\nch1_10:") + + def dedupe(items, key= None): + b = set() + for i in items: + val = i if key is None else key(i) + if val not in b: + yield i #生成器 + b.add(val) + + a=[{'x':1,'y':5},{'x':2,'y':7},{'x':1,'y':5},{'x':1,'y':100}] + + print(a) + + print( list(dedupe(a,key=lambda d:(d['x'],d['y']))) ) + + +def ch1_11(): + ''' + 1.11 对切片命名 + 避免在代码中存在硬编码的索引值,可以利用slice对切片命名 + ''' + print("\nch1_11:") + + items = [1,2,3,4,5,6,7,8,9] + + odd_slice = slice(0,9,2) + odd = items[odd_slice] + + print(odd_slice.start,odd_slice.stop,odd_slice.step) + print(odd) + + +def ch1_12(): + ''' + 1.12 找出序列中出现次数最多的元素 + 使用collections 中的Counter + ''' + + print("\nch1_12:") + + from collections import Counter + + scores_one = [99,89,87,76,98,76,89,92,89,67,59,78,98,92,90,85,56] + scores_two = [100,89,56,98,78,97,96,99,94,93,91,90] + + one_same_top_three = Counter(scores_one).most_common(3) + print(one_same_top_three) + + #Counter可以使用各种数学运算操作 + all_same_top_three = ( Counter(scores_one) + Counter(scores_two) ).most_common(3) + print(all_same_top_three) + + +def ch1_13(): + ''' + 1.13 通过公共键对字典列表排序 + 使用operator中的itemgetter得到字典的值 + ''' + + print("\nch1_13:") + + from operator import itemgetter + + rows = [ + {'fname':'A','lname':'B','uid': 1001}, + {'fname':'B','lname':'C','uid': 1003}, + {'fname':'E','lname':'D','uid': 1002}, + {'fname':'A','lname':'F','uid': 1010}, + ] + + rows_by_uid_and_fname =sorted(rows, key=itemgetter('uid','fname')) + + print(rows_by_uid_and_fname) + + #使用lambda 如果考虑性能使用itemgetter + rows_by_uid = sorted(rows, key=lambda s:s['uid'], reverse=True) + print(rows_by_uid) + + print( max(rows, key=itemgetter('uid')) ) + + +def ch1_14(): + ''' + 1.14 对不原生支持比较操作的对象排序 + sorted 传入key参数 + ''' + + print("\nch1_14:") + + from operator import attrgetter #属性提取 + + class User: + def __init__(self,user_id): + self.user_id = user_id + def __repr__(self): + return 'User({})'.format(self.user_id) + + users = [User(1),User(2),User(3),User(100)] + users_by_user_id= sorted(users, key=lambda s:s.user_id, reverse=True) + + print(users_by_user_id) + + users_by_user_id= sorted(users, key=attrgetter('user_id'), reverse=True) + print(users_by_user_id) + + print(max(users, key=attrgetter('user_id'))) + + +def ch1_15(): + ''' + 1.15 根据字段将记录分组 + 使用itertools.groupby + ''' + + print("\nch1_15:") + + from operator import itemgetter + from itertools import groupby + + rows = [ + {'cost': 190, 'date': '07/02/2016'}, + {'cost': 100, 'date':'07/01/2016'}, + {'cost': 10, 'date':'07/18/2016'}, + {'cost': 89, 'date':'07/10/2016'}, + {'cost': 78, 'date':'07/10/2016'}, + {'cost': 1000, 'date':'07/01/2016'} + ] + + #先按时间排序 + rows.sort(key=itemgetter('date')) + + #分组 + for date, items in groupby(rows,key=itemgetter('date')): + print(date) + for i in items: + print(i) + + #如果不排序,使用一键多值字典 + from collections import defaultdict + + dict = defaultdict(list) + for row in rows: + dict[row['date']].append(row) + print('\n defaultdict:') + print(dict['07/01/2016']) + + +def ch1_16(): + ''' + 1.16 筛选序列中的元素 + 使用列表推导式和生成器表达式,或者使用itemtools.compress + ''' + + print("\nch1_16:") + mylist=[1,4,-6,9,-7,-5,99,100,-1] + list1=[n for n in mylist if n > 0] + print(list1) + + list2=(n for n in mylist if n < 0) + for i in list2: + print(i) + + def is_int(val): + try: + x = int(val) + return True + except ValueError: + return False + mylist=['1','2','3','4','-','N/A','5'] + + list3 = list(filter(is_int, mylist)) + print(list3) + + numbers = [ + '1', + '2', + '3', + '4' + ] + + counts=[4,100,9,0] + + from itertools import compress + + more5 =[n > 5 for n in counts] + restult=list(compress(numbers, more5)) + print(restult) + +def ch1_17(): + ''' + 1.17 从字典中提取子集 + 利用字典推导式 + + ''' + + print("\nch1_17:") + prices = { + 'ACME': 45.23, + 'AAPL': 612.78, + 'IBM': 205.55 + } + + p1 = {key:value for key, value in prices.items() if value > 200} + print(p1) + + +def ch1_18(): + ''' + 1.18 将名称映射到序列的元素中 + 使用collections.namedtuple(命名元组) + 命名元组不可变,使用_replace替换元素 + ''' + + print("\nch1_18:") + + from collections import namedtuple + Subscriber = namedtuple('Subscriber',['addr','joined']) + sub = Subscriber('xxx@xx.com','2016-01-01') + print(sub, sub.addr, sub.joined) + + #替换字典的作用 + s = Subscriber('1@1.com','2016-01-02') + #命名元组不可变,使用_replace替换元素 + s = s._replace(addr='2@2.com') + print(s) + + +def ch1_19(): + ''' + 1.19 同时对数据做转换和换算 + ''' + + print("\nch1_19:") + + nums = [1,2,3,4,5] + s = sum(x*x for x in nums) + print(s) + + import os + files = os.listdir('.') + if any(name.endswith(".py") for name in files): + print('Yes') + else: + print('No') + + +def ch1_20(): + ''' + 1.20 将多个映射合并为单个映射 + 使用collections ChainMap解决 + ''' + + print("\nch1_20:") + + from collections import ChainMap + + a={'x':1,'z':3} + b={'y':2,'z':4} + + c = ChainMap(a,b) #使用原始的字典 + print(c['x'],c['y'],c['z']) + + print(list(c.keys()),list(c.values())) + + #用新建字典代替 + merged = dict(b) + merged.update(a) + print(merged['x'],merged['y'],merged['z']) + +def main(): + + for i in range(1,21): + func='ch1_%d()'%(i) + exec(func) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Chapter1/ch1_3.txt b/Chapter1/ch1_3.txt new file mode 100644 index 0000000..41219f9 --- /dev/null +++ b/Chapter1/ch1_3.txt @@ -0,0 +1,9 @@ +Perl +Python +PHP +C++ +Java +R +JavaScript +HTML +CSS From 74c2d1103121c811b305a866c85921685302ce08 Mon Sep 17 00:00:00 2001 From: Jimmy Xiang Date: Thu, 13 Oct 2016 12:00:01 -0600 Subject: [PATCH 5/9] add python cookbook pdf --- Chapter2/Chapter2.md | 0 Chapter2/Chapter2.py | 0 ...211\210\344\270\255\346\226\207v2.0.0.pdf" | Bin 0 -> 2575707 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Chapter2/Chapter2.md create mode 100644 Chapter2/Chapter2.py create mode 100644 "\343\200\212Python Cookbook\343\200\213\347\254\254\344\270\211\347\211\210\344\270\255\346\226\207v2.0.0.pdf" diff --git a/Chapter2/Chapter2.md b/Chapter2/Chapter2.md new file mode 100644 index 0000000..e69de29 diff --git a/Chapter2/Chapter2.py b/Chapter2/Chapter2.py new file mode 100644 index 0000000..e69de29 diff --git "a/\343\200\212Python Cookbook\343\200\213\347\254\254\344\270\211\347\211\210\344\270\255\346\226\207v2.0.0.pdf" "b/\343\200\212Python Cookbook\343\200\213\347\254\254\344\270\211\347\211\210\344\270\255\346\226\207v2.0.0.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..97fa4a55ff7b419baa0b284afa49fe2d3b2c8d21 GIT binary patch literal 2575707 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