├── .gitignore ├── 01_Welcome to Python! └── note01_Welcome to Python!.md ├── 02_Getting Started └── note02_Getting Started.md ├── 03_Python Basics ├── 3-10_makeTextFile.py ├── 3-10_readTextFile.py ├── 3-13_editTextFile.py ├── aa.txt ├── makeTextFile.py ├── note03_Python Basics.md ├── readNwriteTextFile.py └── readTextFile.py ├── 04_Python Objects ├── 4.1_typechk_isinstance.py ├── 4.2_typechk_type.py ├── 4.9 Categorizing the Standard Types.png └── note04_Python Objects.md ├── 05_Numbers ├── 5-10-tran_F_to_C.py ├── 5-12_number_range.py ├── 5-14_compound_interest.py ├── 5-15_gcd_lcm.py ├── 5-16 exercise.png ├── 5-16_month_paid.py ├── 5-17-random.py ├── 5-2-plus.py ├── 5-3-get_letter_grade.py ├── 5-4-is_leep_year.py ├── 5-5-get_cent_count.py ├── 5-6-num1_op_num2.py ├── 5.7 Integer Type Built-in Functions.png └── note05_Numbers.md ├── 06_Sequences-Strings, Lists, and Tuples ├── 6-10_reverse_up_low.py ├── 6-14_rock_paper_scissors.py ├── 6-1_idcheck.py ├── 6-3_sort_numstring.py ├── 6-4_get_letter_grade.py ├── 6-6_my_strip.py ├── 6-7_fix-bug.py ├── 6-8_english-num.py ├── 6.1 Sequence Type Operators.png ├── 6.15.1-stack.py ├── 6.15.2-queue.py ├── 6.8-encode-decode.py ├── note06_Sequences-Strings, Lists, and Tuples.md └── unicode.txt ├── 07_Mapping and Set Types ├── 7-1 How dictionaries are compared.png ├── 7-15-sets-calculator.py ├── note07_Mapping and Set Types.md └── userpw.py ├── 08_Conditionals and Loops ├── 8-10-nametrack.py ├── 8-4-isprime.py ├── 8-5-getfactors.py ├── 8-6-prime-split.py ├── 8-7-isperfect.py ├── 8-8-N!.py ├── 8-9-Fibonacci.py ├── check-user-passwd-3-times.py └── note08_Conditionals and Loops.md ├── 09_File and Input Output ├── 9-1-filter.py ├── 9-11_favourite_URL.py ├── 9-12_sysadmin_pickle_shelve.py ├── 9-15_copyfile.py ├── 9-20_gzip.py ├── 9-21_zipfile.py ├── 9-22_lszip.py ├── 9-23_tarfile.py ├── 9-24_movefile_in_zip.py ├── 9-25_multi_tar_zip.py ├── 9-8-cmd-dir-module.py ├── 9-9_python_stdlib_doc.py ├── 9.7-eg-ospathex.py └── note09_Files and Input Output.md ├── 10_Errors and Exceptions ├── 10-8-safe_input.py ├── 10-9-safe_sqrt.py ├── 10–6-myopen.py ├── carddata.txt ├── cardlog.txt ├── cardrun.py ├── myexc.py └── note10_Errors and Exceptions.md ├── 11_Functions and Functional Programming ├── 11-12_timeit.py ├── 11-13_mult_reduce_lambda.py ├── 11-15_backward_forward.py ├── 11-16_easyMath2.py ├── 11.1_easyMath.py ├── 11.2_deco.py ├── 11.2_deco2.py ├── 11.3_numConv.py ├── 11.4_grabWeb.py ├── 11.5_testit.py ├── 11.6_pfaGUI.py ├── 11.7_closureVars.py ├── 11.8_funcLog.py ├── 11.9_scope.py ├── dest.txt └── note11_Functions and Functional Programming.md ├── 12_Modules ├── 12-1_Namespaces_versus_variable_scope.png ├── 12.6_namespace.py ├── cli4vof.py ├── note12_Modules.md └── omh4cli.py ├── README.md └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | bin/ 4 | *.jar 5 | *.war 6 | *.zip 7 | *.tar 8 | *.tar.gz 9 | 10 | # eclipse ignore 11 | .settings/ 12 | .project 13 | .classpath 14 | 15 | # idea ignore 16 | .idea/ 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # temp ignore 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | /ostore-mq/.project 32 | /ostore-mq/.settings 33 | /ostore-mq/target 34 | /ostore-mq/.classpath 35 | -------------------------------------------------------------------------------- /01_Welcome to Python!/note01_Welcome to Python!.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 第一章主要讲了Python的起源、特点、Python的安装和运行、Python的实现版本 3 | 4 | # 知识点 5 | ## 1.1 Python的起源 6 | python创始人 贵铎·范·罗萨姆(Guido van Rossum)为了让程序员更方便容易地写代码,于 1989 年底创造了 Python。 7 | 【小故事:蒙提·派森:Monty Python,也称“蒙地蟒蛇”。是英国的一个六人喜剧团体,其七十年代的电视剧和八十年代的电影作品红极一时。贵铎·范·罗萨姆就是该团体的忠实影剧迷,故而将本语言命名为 Python。这里的 IDLE 指的是其成员艾瑞克·艾多 (Eric Idle )】 8 | 9 | ## 1.2 Python的特点 10 | - 高级:类json格式的dict和list使得定义数据结构非常方便 11 | - 面向对象 12 | - 可升级:模块化架构,导入模块即可加入新功能,和java类似 13 | - 可扩展 14 | - 可移植:python可以运行在任何带有ANSI C编译器的平台上 15 | - 简洁易读:这是很大的亮点,能用几句代码搞定的事就绝不多写几行 16 | - 解释性和编译性:python是解释型语言,即python程序可以不编译就能够运行。由于不是以本地机器码运行,纯粹的解释型语言通常比编译型语言运行的慢。实际上,Python和Java一样,也是可以字节编译的,其结果就是可以生成一种近似机器语言的中间形式。这不仅改善了 Python 的性能,还同时使它保持了解释型语言的优点。类比PHP,PHP是解释型语言,不能字节编译;类比Java,Java编译型语言,必须编译后才能运行。而python是集合了PHP和Java两者的优点,既是解释型语言,也是编译型语言(提高运行速度)。 17 | 18 | ## 1.3 Python的安装和运行 19 | 20 | ### 1.3.1 Python的安装 21 | 在官网下载安装包,笔者使用的是Windows环境,安装完为了在任何地方都能执行python命令,将 `$PYTHON_HOME/bin` 添加到系统环境变量$PATH里。 22 | 23 | ### 1.3.2 Python的运行 24 | Windows环境下,双击.py文件可以运行python程序,但是会闪退,作为程序员一般不会这么干,为了提高逼格建议打开CMD,用命令 python xxx.py 运行程序。另外,双击.py文件大多数情况下是为了查看源码,可以选中一个.py文件,右键选择打开文件的默认程序,笔者选的是Sublime,Notepad++等其他编辑器也是不错的选择。 25 | 26 | ## 1.4 Python的实现版本 27 | - Python 的标准实现是使用 C 语言完成的 (也就是 CPython),所以要使用 C 和 C++ 编写 Python 扩展; 28 | - Python 的 Java 实现被称作 Jython,要使用 Java 编写其扩展; 29 | - Python 的 C# 实现被称作 IronPython,是针对 .NET 或 Mono 平台的实现,可以使用 C# 或者 VB.Net 扩 展 IronPython。 30 | 31 | # 练习 32 | Python 执行程序的安装位置和标准库模块的安装位置: 33 | 笔者安装的Python版本是2.7.8,安装目录是D:\Python27,执行程序的位置就是 D:\Python27\python.exe 34 | 35 | 标准库模块的安装位置:D:\Python27\Lib,通过 `python -v` 进入python命令行模式时,可以看到python启动时导入的模块和模块路径,这些模块大部分位于D:\Python27\Lib 下。 36 | -------------------------------------------------------------------------------- /02_Getting Started/note02_Getting Started.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 第二章是对Python主要特性的一个简单介绍,建立起宏观的知识点体系。 3 | 4 | # 知识点 5 | ## 2.1 输入/输出 6 | - print 语句调用`str()`函数显示对象,而交互式解释器则调用`repr()`函数来显示对象。 7 | 8 | - 通过用函数名作为`help()`的参数就能得到相应的帮助信息,例如:`help(raw_input)` 9 | 10 | - 符号 `>>` 用来重定向输出,例如:`print >> sys.stderr, 'Fatal error: invalid input!'` 。 11 | 12 | ## 2.2 注释 13 | - 单行注释:`# 这是注释` 14 | 15 | - 多行注释:`'''这是注释'''` 16 | 17 | ## 2.3 操作符 18 | - 双斜杠`//`:浮点除法,且对结果向下取整 19 | 20 | - `3 < 4 < 5` 是 `3 < 4 and 4 < 5` 的缩写 21 | 22 | ## 2.4 变量与赋值 23 | Python不支持自增1和自减1运算符,因为+和-也是单目运算符,Python 会将 --n 解释为-(-n) 从而得到 n , 同样 ++n 的结果也是 n;另外n++或n - -也是不支持的,会导致编译失败。 24 | 25 | ## 2.5 Python类型 26 | - 十进制浮点数:`decimal.Decimal('1.1')` 27 | - 星号`*`用于字符串重复 28 | 29 | ## 2.6 缩进 30 | 用四个空格代替Tab 31 | 32 | ## 2.7 循环与条件 33 | - 同时循环索引和元素:`for i, ch in enumerate (foo)` 34 | 35 | ## 2.8 数据结构 36 | - 列表解析:`squared = [x ** 2 for x in range(4)]` 37 | 38 | ## 2.8 文件 39 | - 操作文件的两个方法:`open()` 和 `file()`(工厂函数) 40 | - `open()`的 access_mode(后四个要与前三个组合使用): 41 | - **r**:读,默认 42 | - **w**:写 43 | - **a**:追加 44 | - +:读写模式 45 | - b:二进制模式 46 | - U:通用换行符 47 | - t:文本模式,默认 48 | 49 | ## 2.9 错误 50 | - 关键字: `try-except` 51 | 52 | ## 2.10 函数 53 | - 设置函数的默认参数:`foo(debug=True)` 54 | 55 | - 通过 `dir.__doc__` 可以访问 `dir()` 内建函数的文档字符串。 56 | 57 | - `sys.exit()`是一种热键之外的另一种退出Python解释器的方式 。 58 | 59 | ## 2.11 类 60 | - `self.__class__.__name__` : 对一个实例来说,这个变量表示实例化它的类的名字。 61 | 62 | ## 2.12 模块 63 | - PEP是一个Python增强提案(Python Enhancement Proposal),也是在新版 Python 中增加新特性的方式。 -------------------------------------------------------------------------------- /03_Python Basics/3-10_makeTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 'makeTextFile.py -- create text file' 3 | 4 | import os 5 | ls = os.linesep 6 | 7 | # get filename 8 | fname = raw_input('Enter filename: ') 9 | print 10 | 11 | # get file content (text) lines 12 | all = [] 13 | print "\nEnter lines ('.' by itself to quit).\n" 14 | 15 | # loop until user terminates input 16 | while True: 17 | entry = raw_input('$ ') 18 | if entry == '.': 19 | break 20 | else: 21 | all.append(entry) 22 | 23 | # write lines to file with proper line-ending 24 | try: 25 | fobj = open(fname, 'w') 26 | except IOError, e: 27 | print "*** file open error:", e 28 | else: 29 | fobj.writelines(['%s%s' % (x, ls) for x in all]) 30 | fobj.close() 31 | -------------------------------------------------------------------------------- /03_Python Basics/3-10_readTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | 'readTextFile.py -- read and display text file' 4 | 5 | # get filename 6 | fname = raw_input('Enter filename: ') 7 | print 8 | 9 | # attempt to open file for reading 10 | if not os.path.exists(fname): 11 | print "ERROR: '%s' not exists" % fname 12 | else: 13 | fobj = open(fname, 'r') 14 | # display contents to the screen 15 | for eachLine in fobj: 16 | print eachLine.strip() 17 | fobj.close() 18 | 19 | 20 | -------------------------------------------------------------------------------- /03_Python Basics/3-13_editTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | 4 | print "************ Text File Editor ************" 5 | print 6 | 7 | line_sum = 0; 8 | all_content = [] 9 | 10 | # get filename 11 | while True: 12 | try: 13 | fname = raw_input('>>> enter filename: ') 14 | fobj = open(fname, 'r') 15 | break 16 | except IOError, e: 17 | print '>>> [ERROR] file open failed:', e 18 | 19 | # get original content 20 | for line in fobj.readlines(): 21 | all_content.append(line) 22 | print line, 23 | line_sum += 1 24 | 25 | def get_new_content(): 26 | while True: 27 | choose_line_num = raw_input('>>> select one line to edit [1-%d] : ' % line_sum) 28 | try: 29 | choose_line_num = int(choose_line_num) 30 | if not (0 < choose_line_num <= line_sum): 31 | print '>>> [ERROR] the line number must in [1-%d] !' % line_sum 32 | continue 33 | break 34 | except ValueError, e: 35 | print '>>> [ERROR] number transform failed: ', e 36 | 37 | print '>>> your choose line is : %d' % choose_line_num 38 | new_content = raw_input('>>> input new content: ') 39 | all_content[choose_line_num-1] = new_content + os.linesep 40 | 41 | # generate new content 42 | if line_sum > 0: 43 | get_new_content() 44 | else: 45 | print '>>> the file is empty, please input new content directly:' 46 | new_content = raw_input('>>> ') 47 | all_content.append(new_content) 48 | 49 | # save new content 50 | fobj = open(fname, 'w') 51 | for line in all_content: 52 | fobj.write(line) 53 | fobj.close() 54 | print '>>> save success, bye!' -------------------------------------------------------------------------------- /03_Python Basics/aa.txt: -------------------------------------------------------------------------------- 1 | aaaa 2 | bbbb 3 | ccc 4 | ddd eee ffff 77777 888 9999 1010101010 1111111 1212121212 -------------------------------------------------------------------------------- /03_Python Basics/makeTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 'makeTextFile.py -- create text file' 3 | 4 | import os 5 | ls = os.linesep 6 | 7 | # get filename 8 | while True: 9 | fname = raw_input('Enter filename: ') 10 | print 11 | 12 | if os.path.exists(fname): 13 | print "ERROR: '%s' already exists" % fname 14 | else: 15 | break 16 | 17 | # get file content (text) lines 18 | all = [] 19 | print "\nEnter lines ('.' by itself to quit).\n" 20 | 21 | # loop until user terminates input 22 | while True: 23 | entry = raw_input('$ ') 24 | if entry == '.': 25 | break 26 | else: 27 | all.append(entry) 28 | 29 | # write lines to file with proper line-ending 30 | fobj = open(fname, 'w') 31 | fobj.writelines(['%s%s' % (x, ls) for x in all]) 32 | fobj.close() 33 | -------------------------------------------------------------------------------- /03_Python Basics/note03_Python Basics.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍基本的语法、编码风格、内存管理机制,以一个读写文件的Python程序拉开Python编程的序幕。 3 | 4 | # 知识点 5 | ## 3.1 语句和语法 6 | - Python 使用缩进来分隔代码组,代码的层次关系通过同样深度的空格或制表符缩进体现。同一代码组的代码行必须严格左对齐(左边有同样多的空格或同样多的制表符)。 7 | - Python 的赋值语句不会返回值。 8 | 9 | ## 3.2 变量赋值 10 | - 同时给多个变量赋值(类似元组`):go_surf, get_a_tan_while, boat_size, toll_money = (1,'windsurfing', 40.0, -2.00)` 11 | - 交换两个值:`x, y = y, x` 12 | 13 | ## 3.3 标识符和关键字 14 | - `__xxx__`:系统定义的名字(`__doc__` | `__class__` | `__name__` | ...) 15 | - `__xxx`:类中的私有变量名 16 | 17 | ## 3.4 基本风格指南 18 | - 在 Python 解释器输入 `import this` 然后回车可以查看“Pythonic”【以 Python 的方式去编写代码、组织逻辑,及对象行为】 19 | - **尽量使用局部变量代替全局变量**,好处:易维护、提高性能、节省内存。(*在查找全局变量之前,总是先查找本地变量!*) 20 | 21 | ## 3.5 内存管理 22 | - Python是一种解释型语言,对象的类型和内存占用都是运行时确定的,在赋值时解释器会根据语法和右侧的操作数来决定新对象的类型。 23 | - Python 内部记录着所有使用中的对象各有多少引用,一个内部跟踪变量称为一个**引用计数器**。每个对象各有多少个引用,简称**引用计数**。当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时(这个对象的引用计数变为0时),它被垃圾回收。 24 | - Python 的垃圾收集器实际上是一个**引用计数器**和一个**循环垃圾收集器**。 当一个对象的引用计数变为 0,解释器会释放掉这个对象和仅有这个对象可访问(可到达)的其它对象。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来,试图清理所有未引用的循环。 25 | - **`del obj`**:删除对象的最后一个引用, 也就是该对象的引用计数会减为0,使该对象就成为垃圾回收机制的回收对象。(注意:任何追踪或调试程序会给一个对象增加一个额外的引用, 这会推迟该对象被回收的时间。) 26 | 27 | ## 3.6 第一个 Python 程序 28 | 简洁的写法:`fobj.writelines(['%s %s' % (x, ls) for x in all])` 29 | 30 | ## 3.7 常用模块 31 | - PyUnit(测试模块):`unittest`。 32 | - Debugger(调试模块): `pdb`,支持断点,代码逐行执行,检查堆栈。它还支持事后调试 33 | - Logger(日志模块): `logging`,定义了一些函数和类帮助你的程序实现灵活的日志系统。 34 | - Profilers(性能测试模块): `profile`, `hotshot`, `cProfile` 35 | 36 | 37 | # 练习 38 | 39 | **3–1. 标识符。为什么 Python 中不需要变量名和变量类型声明?** 40 | 41 | 变量名的作用是指向一个对象,单纯只创建对象而没有变量指向它是允许的; 42 | 43 | Python是动态语言,在运行时根据语法和右侧的操作数来决定对象的类型,所以不需要申明变量类型。 44 | 45 | **【补充】** 46 | 47 | **动态语言和静态语言:** 48 | 49 | - *动态、静态是指变量的绑定方式,静态语言在编译时绑定,动态语言可以在运行时随意绑定。* 50 | - Java是静态语言,一个变量定义好了数据类型就不能再改变。如:定义了 `String str = 'hello'` 后,`str = 123` 就不行; 51 | - Python是动态语言,在运行时根据语法和右侧的操作数来决定对象的类型。如:`str = 'hello'`,重新赋值 `str = 123` 是可以的。 52 | 53 | **强类型和弱类型:** 54 | 55 | - 强类型、弱类型是指变量的类型在运算上下文中是否可以自动转换。 56 | - 对于 `1 + "1000"` 这样一条语句: 57 | - Python会报错,因为它是强类型语言,不能将"1000"自动转换成1000,再和1相加; 58 | - 而Perl便能进行自动类型转换,所以它是弱类型。 59 | 60 | 61 | **3–2. 标识符。为什么 Python 中不需要声明函数类型?** 62 | 63 | 跟python是动态语言有关,在运行时根据返回类型决定函数类型,如果函数没有显式地`return`一个函数类型的话,那么函数类型是`NoneType` 64 | 65 | 66 | **3–3. 标识符。为什么应当避免在变量名的开始和和结尾使用双下划线?** 67 | 68 | 因为类似`__xxx__`的变量名是系统定义的名字,容易发生冲突。 69 | 70 | **3–4. 语句。在 Python 中一行可以书写多个语句吗?** 71 | 72 | 可以,用分号 `;` 73 | 74 | **3–5. 语句。在 Python 中可以将一个语句分成多行书写吗?** 75 | 76 | 可以,用反斜杠 `\` 77 | 78 | **3–6. 变量赋值** 79 | 80 | **(a)赋值语句 x, y, z = 1, 2, 3 会在 x、y、z 中分别赋什么值?** 81 | 82 | `x=1, y=2, z=3` 83 | 84 | **(b)执行 z, x, y = y, z, x 后,x、y、z 中分别含有什么值?** 85 | 86 | `x=3, y=1, z=2` 87 | 88 | **3–7. 标识符。下面哪些是 Python 合法的标识符?如果不是,请说明理由!在合法的标识符中,哪些是关键字?** 89 | 90 | 1. `int32` (合法标识符) 91 | 2. `40XL` (非法标识符 - 以数字开头) 92 | 3. `$aving$` (非法标识符 - 以$开头) 93 | 4. `printf` (合法标识符) 94 | 5. `print` (关键字) 95 | 6. `_print` (合法标识符) 96 | 7. `this` (关键字) 97 | 8. `self` (关键字) 98 | 9. `__name__` (合法标识符) 99 | 10. `0x40L` (非法标识符 - 以数字开头) 100 | 11. `bool` (合法标识符) 101 | 12. `true` (合法标识符) 102 | 13. `big-daddy` (合法标识符) 103 | 14. `2hot2touch` (非法标识符 - 以数字开头) 104 | 15. `type` (关键字) 105 | 16. `thisIsn'tAVar` (非法标识符 - 含有') 106 | 17. `thisIsAVar` (合法标识符) 107 | 18. `R_U_Ready` (合法标识符) 108 | 19. `Int` (合法标识符) 109 | 20. `True` (关键字) 110 | 21. `if` (关键字) 111 | 22. `do` (关键字) 112 | 23. `counter-1` (合法标识符) 113 | 24. `access` (合法标识符) 114 | 25. `_` (合法标识符) 115 | 116 | **3–8. Python 代码。将脚本拷贝到您的文件系统中,然后修改它。可以添加注释,修改提示符(‘>’太单调了)等等,修改这些代码,使它看上去更舒服。** 117 | 118 | *略。* 119 | 120 | **3–9. 移植。 如果你在不同类型的计算机系统中分别安装有 Python, 检查一下,os.linesep 的值是否有不同。 记下操作系统的类型以及 linesep 的值。** 121 | 122 | Linux_x64操作系统:`'\n'` 123 | 124 | Windows_x64操作系统:`'\r\n'` 125 | 126 | **3–10. 异常。使用类似 readTextFile.py 中异常处理的方法取代makeTextFile.py 中 对 os.path.exists() 的 调 用 。 反 过 来 , 用 os.path.exists() 取 代readTextFile.py 中的异常处理方法。** 127 | 128 | makeTextFile.py: 129 | ```python 130 | #!/usr/bin/env python 131 | 'makeTextFile.py -- create text file' 132 | 133 | import os 134 | ls = os.linesep 135 | 136 | # get filename 137 | fname = raw_input('Enter filename: ') 138 | print 139 | 140 | # get file content (text) lines 141 | all = [] 142 | print "\nEnter lines ('.' by itself to quit).\n" 143 | 144 | # loop until user terminates input 145 | while True: 146 | entry = raw_input('$ ') 147 | if entry == '.': 148 | break 149 | else: 150 | all.append(entry) 151 | 152 | # write lines to file with proper line-ending 153 | try: 154 | fobj = open(fname, 'w') 155 | except IOError, e: 156 | print "*** file open error:", e 157 | else: 158 | fobj.writelines(['%s%s' % (x, ls) for x in all]) 159 | fobj.close() 160 | ``` 161 | 162 | readTextFile.py: 163 | ```python 164 | #!/usr/bin/env python 165 | import os 166 | 'readTextFile.py -- read and display text file' 167 | 168 | # get filename 169 | fname = raw_input('Enter filename: ') 170 | print 171 | 172 | # attempt to open file for reading 173 | if not os.path.exists(fname): 174 | print "ERROR: '%s' not exists" % fname 175 | else: 176 | fobj = open(fname, 'r') 177 | # display contents to the screen 178 | for eachLine in fobj: 179 | print eachLine, 180 | fobj.close() 181 | ``` 182 | 183 | **3–11.字符串格式化 不再抑制 readTextFile.py 中 print 语句生成的 NEWLINE 字符, 修改你的代码, 在显示一行之前删除每行末尾的空白。 这样, 你就可以移除 print 语句末尾的逗号了。** 184 | 185 | `eachLine.strip()` 186 | 187 | **3–12. 合并源文件。将两段程序合并成一个,给它起一个你喜欢的名字,比方readNwriteTextFiles.py。让用户自己选择是创建还是显示一个文本文件。** 188 | ```python 189 | #!/usr/bin/env python 190 | 191 | import os 192 | ls = os.linesep 193 | 194 | def readFile(): 195 | # get filename 196 | fname = raw_input('Enter filename: ') 197 | print 198 | 199 | # attempt to open file for reading 200 | try: 201 | fobj = open(fname, 'r') 202 | except IOError, e: 203 | print "*** file open error:", e 204 | else: 205 | # display contents to the screen 206 | for eachLine in fobj: 207 | print eachLine, 208 | fobj.close() 209 | 210 | def writeFile(): 211 | # get filename 212 | while True: 213 | fname = raw_input('Enter filename: ') 214 | print 215 | 216 | if os.path.exists(fname): 217 | print "ERROR: '%s' already exists" % fname 218 | else: 219 | break 220 | 221 | # get file content (text) lines 222 | all = [] 223 | print "\nEnter lines ('.' by itself to quit).\n" 224 | 225 | # loop until user terminates input 226 | while True: 227 | entry = raw_input('$ ') 228 | if entry == '.': 229 | break 230 | else: 231 | all.append(entry) 232 | 233 | # write lines to file with proper line-ending 234 | fobj = open(fname, 'w') 235 | fobj.writelines(['%s%s' % (x, ls) for x in all]) 236 | fobj.close() 237 | 238 | while True: 239 | choose = raw_input('choose action: r | w :') 240 | if (choose == 'r'): 241 | readFile() 242 | break 243 | elif (choose == 'w'): 244 | writeFile() 245 | break 246 | ``` 247 | 248 | **3–13. 添加新功能。 将你上一个问题改造好的 readNwriteTextFiles.py 增加一个新功能:允许用户编辑一个已经存在的文本文件。 你可以使用任何方式,无论是一次编辑一行,还是一次编辑所有文本。需要提醒一下的是, 一次编辑全部文本有一定难度, 你可能需要借助 GUI工具包或一个基于屏幕文本编辑的模块比如 curses 模块。要允许用户保存他的修改(保存到文件)或取消他的修改(不改变原始文件),并且要确保原始文件的安全性(不论程序是否正****常关闭)。** 249 | ```python 250 | #!/usr/bin/env python 251 | import os 252 | 253 | print "************ Text File Editor ************" 254 | print 255 | 256 | line_sum = 0; 257 | all_content = [] 258 | 259 | # get filename 260 | while True: 261 | try: 262 | fname = raw_input('>>> enter filename: ') 263 | fobj = open(fname, 'r') 264 | break 265 | except IOError, e: 266 | print '>>> [ERROR] file open failed:', e 267 | 268 | # get original content 269 | for line in fobj.readlines(): 270 | all_content.append(line) 271 | print line, 272 | line_sum += 1 273 | 274 | def get_new_content(): 275 | while True: 276 | choose_line_num = raw_input('>>> select one line to edit [1-%d] : ' % line_sum) 277 | try: 278 | choose_line_num = int(choose_line_num) 279 | if not (0 < choose_line_num <= line_sum): 280 | print '>>> [ERROR] the line number must in [1-%d] !' % line_sum 281 | continue 282 | break 283 | except ValueError, e: 284 | print '>>> [ERROR] number transform failed: ', e 285 | 286 | print '>>> your choose line is : %d' % choose_line_num 287 | new_content = raw_input('>>> input new content: ') 288 | all_content[choose_line_num-1] = new_content + os.linesep 289 | 290 | # generate new content 291 | if line_sum > 0: 292 | get_new_content() 293 | else: 294 | print '>>> the file is empty, please input new content directly:' 295 | new_content = raw_input('>>> ') 296 | all_content.append(new_content) 297 | 298 | # save new content 299 | fobj = open(fname, 'w') 300 | for line in all_content: 301 | fobj.write(line) 302 | fobj.close() 303 | print '>>> save success, bye!' 304 | ``` 305 | 306 | -------------------------------------------------------------------------------- /03_Python Basics/readNwriteTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | ls = os.linesep 5 | 6 | def readFile(): 7 | # get filename 8 | fname = raw_input('Enter filename: ') 9 | print 10 | 11 | # attempt to open file for reading 12 | try: 13 | fobj = open(fname, 'r') 14 | except IOError, e: 15 | print "*** file open error:", e 16 | else: 17 | # display contents to the screen 18 | for eachLine in fobj: 19 | print eachLine, 20 | fobj.close() 21 | 22 | def writeFile(): 23 | # get filename 24 | while True: 25 | fname = raw_input('Enter filename: ') 26 | print 27 | 28 | if os.path.exists(fname): 29 | print "ERROR: '%s' already exists" % fname 30 | else: 31 | break 32 | 33 | # get file content (text) lines 34 | all = [] 35 | print "\nEnter lines ('.' by itself to quit).\n" 36 | 37 | # loop until user terminates input 38 | while True: 39 | entry = raw_input('$ ') 40 | if entry == '.': 41 | break 42 | else: 43 | all.append(entry) 44 | 45 | # write lines to file with proper line-ending 46 | fobj = open(fname, 'w') 47 | fobj.writelines(['%s%s' % (x, ls) for x in all]) 48 | fobj.close() 49 | 50 | while True: 51 | choose = raw_input('choose action: r | w :') 52 | if (choose == 'r'): 53 | readFile() 54 | break 55 | elif (choose == 'w'): 56 | writeFile() 57 | break -------------------------------------------------------------------------------- /03_Python Basics/readTextFile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 'readTextFile.py -- read and display text file' 4 | 5 | # get filename 6 | fname = raw_input('Enter filename: ') 7 | print 8 | 9 | # attempt to open file for reading 10 | try: 11 | fobj = open(fname, 'r') 12 | except IOError, e: 13 | print "*** file open error:", e 14 | else: 15 | # display contents to the screen 16 | for eachLine in fobj: 17 | print eachLine, 18 | fobj.close() 19 | -------------------------------------------------------------------------------- /04_Python Objects/4.1_typechk_isinstance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def displayNumType(num): 4 | print num, 'is', 5 | if isinstance(num, (int, long, float, complex)): 6 | print 'a number of type:', type(num).__name__ 7 | else: 8 | print 'not a number at all!!' 9 | 10 | displayNumType(-69) 11 | displayNumType(9999999999999999999999L) 12 | displayNumType(98.6) 13 | displayNumType(-5.2+1.9j) 14 | displayNumType('xxx') 15 | -------------------------------------------------------------------------------- /04_Python Objects/4.2_typechk_type.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import types 3 | 4 | def displayNumType(num): 5 | print num, "is", 6 | if type(num) is types.IntType: # or : == types.IntType: 7 | print 'an integer' 8 | elif type(num) is long: 9 | print 'a long' 10 | elif type(num) is float: 11 | print 'a float' 12 | elif type(num) is types.ComplexType: 13 | print 'a complex number' 14 | else: 15 | print 'not a number at all!!' 16 | 17 | displayNumType(-69) 18 | displayNumType(9999999999999999999999L) 19 | displayNumType(98.6) 20 | displayNumType(-5.2+1.9j) 21 | displayNumType('xxx') -------------------------------------------------------------------------------- /04_Python Objects/4.9 Categorizing the Standard Types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/04_Python Objects/4.9 Categorizing the Standard Types.png -------------------------------------------------------------------------------- /04_Python Objects/note04_Python Objects.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍Python对象、最常用的标准类型、标准运算符、内建函数、标准类型的分类方式(三种模型)、Python目前不支持的类型 3 | 4 | # 知识点 5 | ## 4.1 Python对象的三个特性 6 | - 身份:对象的唯一标识,是对象的内存地址,可以通过`id()`获取,只读 7 | - 类型:通过`type()`查看对象的类型,注意返回的是对象而不是简单的字符串,只读 8 | - 值:对象的数据,可读可写 9 | 10 | ## 4.2 基本数据类型 11 | 数字、整型、布尔型、长整型、浮点型、复数型、字符串、列表、元组、字典 12 | 13 | ## 4.3 对象类型 14 | **对象的类型本身也是对象**,对象类型的类型都是 `type`(类似Java里的`Object`),它是所有Python类型的根和所有Python标准类的默认元类(metaclass)。 15 | Python2.2 统一了类型和类。 16 | 17 | ## 4.4 None 18 | - None是Python的`null对象` 19 | - None不支持任何运算,也没有任何内建方法 20 | - None类型和Java的void类型相似,None类型的值和Java的null值相似 21 | 22 | ## 4.5 布尔值测试 23 | 所有标准对象均可用于布尔测试,同类型的对象之间可以比较大小。 24 | 25 | **布尔值为False的情况:** 26 | 27 | - 空对象 28 | - 值为零的任何数字 29 | - Null对象None 30 | 31 | **测试两个变量是否指向同一个对象**: `is` 和 `is not`,`a is b `等价于 `id(a) == id(b)` 32 | 33 | **不可变对象**:**整数对象**和**字符串对象**,Python会缓存**[-5, 256]**的整数对象和比较小的字符串对象。 34 | 35 | not 运算符拥有最高优先级,只比所有比较运算符低一级。 and 和 or 运算符则相应的再低一级。 36 | 37 | ## 4.6 标准类型内建函数 38 | - `cmp(obj1, obj2)`: 比较 obj1 和 obj2, 根据比较结果返回整数 i: 39 | - i < 0 if obj1 < obj2 40 | - i > 0 if obj1 > obj2 41 | - i == 0 if obj1 == obj2 42 | - `repr(obj)` 或 `` `obj` `` :返回一个对象的字符串表示(不鼓励使用 ``) 43 | - `str(obj)`: 返回对象适合可读性好的字符串表示 44 | - `type(obj)`:得到一个对象的类型,并返回相应的 type 对象 45 | 46 | ## 4.7 repr(obj) 和 str(obj) 47 | - `repr()`函数得到的字符串可以用来重新获得该对象(通过求值运算 `eval()`), 通常情况下 `obj == eval(repr(obj))` 这个等式是成立的。 48 | - `str()` 用于生成一个对象的可读性好的字符串表示,它的返回结果通常无法用于`eval()`求值, 但很适合用于 print 语句输出。 49 | - `repr() `输出对 Python 比较友好, 而 `str()`的输出对人比较友好。 50 | 51 | ## 4.8 isinstance(obj, type):判断对象类型 52 | `if isinstance(7, int)` 比 `if type(7) is types.IntType` 简洁 53 | 54 | **优化**:通过使用 from-import,可以减少一次查询:`from types import IntType`(为了得到整数的对象类型,解释器会先查找 types 这个模块的名字,然后在该模块的字典中查找 IntType。) 55 | 56 | ## 4.9 类型工厂函数 57 | - int(), long(), float(), complex() 58 | - str(), unicode(), basestring() 59 | - list(), tuple(), dict() 60 | - type() 61 | 62 | ## 4.10 三种模型 63 | 三种不同的模型对基本类型进行分类,每种模型都表示这些类型之间的相互关系。 64 | 65 | 1. 存储模型 66 | - 标量/原子类型:数值(所有的数值类型),字符串(全部是文字) 67 | - 容器类型:列表、元组、字典 68 | 69 | 2. 更新模型 70 | - 可变类型:列表、字典 71 | - 不可变类型:数字、字符串、元组 72 | 73 | 3. 访问模型(区分数据类型的主要模型) 74 | - 直接访问:数字 75 | - 顺序访问:字符串、列表、元组 76 | - 映射访问:字典 77 | 78 | 标准类型分类: 79 | 80 | ![Categorizing the Standard Types](./4.9 Categorizing the Standard Types.png "Categorizing the Standard Types") 81 | 82 | ## 4.11 不支持的类型 83 | + **char、byte**:使用长度为1的字符串表示字符或8比特整数 84 | + **int vs short vs long**:Python的整数实现等同于C语言的长整数,若数值超出整型的表达范围,Python会自动的返回一个长整数而不会报错。 85 | + **float vs double**:Python的浮点类型实际上是C语言的双精度浮点类型,不支持单精度浮点数(高精度计算可用`Decimal`) 86 | 87 | ------------ 88 | 89 | # 练习 90 | **4-1. Python 对象。与所有 Python 对象有关的三个属性是什么?请简单的描述一下。** 91 | 92 | - 身份:对象的唯一标识,是对象的内存地址 93 | - 类型:通过`type()`查看对象的类型 94 | - 值:对象的数据 95 | 96 | **4-2. 类型。不可更改(immutable)指的是什么?Python 的哪些类型是可更改的 (mutable),哪些不是?** 97 | 98 | immutable:对象创建成功之后,其值不可以被更新。 99 | 100 | - 可变类型:列表、字典 101 | - 不可变类型:数字、字符串、元组 102 | 103 | **4-3. 类型。哪些 Python 类型是按照顺序访问的,它们和映射类型的不同是什么?** 104 | 顺序访问:字符串、列表、元组。 105 | 顺序访问的索引使用有序的数字偏移量取值,而映射类型的元素是无序存放的,通过一个唯一的 key 来访问。 106 | 107 | **4-4. type()。内建函数 type()做什么?type()返回的对象是什么?** 108 | type()用于获取对象的类型,返回的对象是对应的type对象 109 | 110 | **4-5. str() 和 repr()。内建函数 str()与 repr()之间的不同是什么?哪一个等价于反引号运算符?** 111 | 参照知识点的【repr(obj) 和 str(obj)】 112 | `repr(obj)` 等价于 `` `obj` `` 113 | 114 | **4-6. 对象相等。 您认为 type(a) == type(b)和 type(a) is type(b)之间的不同是什么? 为什么会选择后者?函数 isinstance()与这有什么关系?** 115 | 116 | 1. `==`判断对象的值是否相等,`is`判断两个对象是否相同(同一内存地址)。 117 | 2. 选择`type(a) is type(b)`是因为,我们需要判断的是对象是否为同一个类型对象。 118 | 3. `isinstance()`等同于`type(a) is type(b)`,简化了语法。 119 | 120 | **4-7. 内建函数 dir()。在第二章的几个练习中,我们用内建函数 dir()做了几个实验, 它接受一个对象,然后给出相应的属性。请对 types 模块做相同的实验。记下您熟悉的类型, 包括您对这些类型的认识,然后记下你还不熟悉的类型。在学习 Python 的过程中,你要逐步将 “不熟悉”的类型变得“熟悉”起来。** 121 | ```python 122 | import types 123 | dir(types) 124 | ``` 125 | > ['BooleanType', 'BufferType', 'BuiltinFunctionType', 'BuiltinMethodType', 'ClassType', 'CodeType', 'ComplexType', 'DictProxyType', 'DictType', 'DictionaryType', 'EllipsisType', 'FileType', 'FloatType', 'FrameType', 'FunctionType', 'GeneratorType', 'GetSetDescriptorType', 'InstanceType', 'IntType', 'LambdaType', 'ListType', 'LongType', 'MemberDescriptorType', 'MethodType', 'ModuleType', 'NoneType', 'NotImplementedType', 'ObjectType', 'SliceType', 'StringType', 'StringTypes', 'TracebackType', 'TupleType', 'TypeType', 'UnboundMethodType', 'UnicodeType', 'XRangeType'] 126 | 127 | **4-8. 列表和元组。列表和元组的相同点是什么?不同点是什么?** 128 | 列表和元组都是容器存储类型,按顺序进行数据的存储和访问,不同的是列表是可变的,而元组不可变。 129 | 130 | **4-9. 练习,给定以下赋值:** 131 | ```python 132 | a = 10 133 | b = 10 134 | c = 100 135 | d = 100 136 | e = 10.0 137 | f = 10.0 138 | ``` 139 | **请问下面各表达式的输出是什么?为什么?** 140 | 141 | - (a) a is b 142 | - (b) c is d 143 | - (c) e is f 144 | 145 | **Answer:** 146 | 147 | - (a) True 148 | - (b) True 149 | - (c) False 150 | 151 | 原因:Python会缓存[-5, 256]的整数对象,而不会缓存可变类型float的对象。 152 | 153 | **注意**:如果使用以下方式赋值,`e is f`的结果是`True`! 154 | ```python 155 | a = 10; b = 10; c = 100; d = 100; e = 10.0; f = 10.0 156 | ``` -------------------------------------------------------------------------------- /05_Numbers/5-10-tran_F_to_C.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import division 3 | 4 | def tran_F_to_C(F): 5 | C = (F - 32) * (5 / 9) 6 | return round(C, 2) 7 | 8 | print tran_F_to_C(42) -------------------------------------------------------------------------------- /05_Numbers/5-12_number_range.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | print sys.maxint 4 | print -sys.maxint-1 5 | print sys.float_info 6 | print sys.long_info -------------------------------------------------------------------------------- /05_Numbers/5-14_compound_interest.py: -------------------------------------------------------------------------------- 1 | def compound_interest(rate): 2 | return (1+rate) ** 364 3 | 4 | print compound_interest(0.0005) -------------------------------------------------------------------------------- /05_Numbers/5-15_gcd_lcm.py: -------------------------------------------------------------------------------- 1 | def gcd(num1, num2): 2 | while num2 > 0: 3 | tmp = num1 % num2 4 | num1 = num2 5 | num2 = tmp 6 | return num1 7 | 8 | def lcm(num1, num2): 9 | return num1 * num2 / gcd(num1, num2) 10 | 11 | print gcd(24, 18) 12 | print lcm(24,18) 13 | -------------------------------------------------------------------------------- /05_Numbers/5-16 exercise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/05_Numbers/5-16 exercise.png -------------------------------------------------------------------------------- /05_Numbers/5-16_month_paid.py: -------------------------------------------------------------------------------- 1 | def pay(): 2 | balance = float(raw_input('Enter opening balance: ')) 3 | balance = round(balance, 2) 4 | payment = float(raw_input('Enter monthly payment: ')) 5 | payment = round(payment, 2) 6 | 7 | print ' \tAmount\tRemaining' 8 | print 'Pymt#\tPaid\tBalance' 9 | print '------\t------\t------' 10 | 11 | print '0\t$0.00\t$%s' % balance 12 | i = 1 13 | while balance > payment: 14 | balance -= payment 15 | balance = round(balance, 2) 16 | print '%d\t$%s\t$%s' % (i, payment, balance) 17 | print '%d\t$%s\t$0.00' % (i, balance) 18 | 19 | pay() -------------------------------------------------------------------------------- /05_Numbers/5-17-random.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def rand_sort_num(): 4 | res = [] 5 | N = random.randint(1, 100) 6 | while N > 0: 7 | n = random.randint(0, 2**31-1) 8 | res.append(n) 9 | N -= 1 10 | 11 | res.sort() 12 | return res 13 | 14 | print rand_sort_num() 15 | 16 | -------------------------------------------------------------------------------- /05_Numbers/5-2-plus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | def multiply(year): 3 | return num1 * num2 4 | 5 | print multiply(7, 11) -------------------------------------------------------------------------------- /05_Numbers/5-3-get_letter_grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | def get_letter_grade(score): 3 | if score >= 90 : 4 | return 'A' 5 | elif score >=80 : 6 | return 'B' 7 | elif score >=70 : 8 | return 'C' 9 | elif score >= 60 : 10 | return 'D' 11 | else : 12 | return 'F' 13 | 14 | print get_letter_grade(77) -------------------------------------------------------------------------------- /05_Numbers/5-4-is_leep_year.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | def is_leep_year(year): 3 | if year % 4 == 0: 4 | if year % 100 != 0 : 5 | return True 6 | elif year % 400 == 0: 7 | return True 8 | else: 9 | return False 10 | else: 11 | return False 12 | 13 | print is_leep_year(1992) 14 | print is_leep_year(2000) 15 | print is_leep_year(1900) 16 | print is_leep_year(1999) -------------------------------------------------------------------------------- /05_Numbers/5-5-get_cent_count.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | def get_cent_count(num): 3 | cent_count_arr = [] 4 | cents = [25, 10, 5, 1] 5 | 6 | left = num 7 | i = 0 8 | while left != 0: 9 | cent_count_arr.append(left // cents[i]) 10 | left = left % cents[i] 11 | i += 1 12 | 13 | return cent_count_arr 14 | 15 | print get_cent_count(76) 16 | print get_cent_count(123) 17 | print get_cent_count(88) -------------------------------------------------------------------------------- /05_Numbers/5-6-num1_op_num2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import decimal 3 | 4 | def num1_op_num2(op_str): 5 | arr = op_str.split() 6 | op = arr[1] 7 | num1 = decimal.Decimal(arr[0]) 8 | num2 = decimal.Decimal(arr[2]) 9 | 10 | if op == '+': 11 | return num1 + num2 12 | elif op == '-': 13 | return num1 - num2 14 | elif op == '*': 15 | return num1 * num2 16 | elif op == '/': 17 | return num1 / num2 18 | elif op == '%': 19 | return num1 % num2 20 | elif op == '**': 21 | return num1 ** num2 22 | else: 23 | raise ValueError("op don't exist") 24 | 25 | print num1_op_num2("3 * 4") 26 | print num1_op_num2("11 % 7") 27 | print num1_op_num2("5 ** 2") -------------------------------------------------------------------------------- /05_Numbers/5.7 Integer Type Built-in Functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/05_Numbers/5.7 Integer Type Built-in Functions.png -------------------------------------------------------------------------------- /05_Numbers/note05_Numbers.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍Python支持的多种数字类型,包括:整型、长整型、布尔型、双精度浮点型、十进制浮点型和复数。介绍和数字相关的运算符和函数。 3 | 4 | # 知识点 5 | ## 5.1 布尔型 6 | 从Python2.3开始支持bool,取值范围:True、False 7 | 8 | ## 5.2 标准整型 9 | 在32位机器上,标准整数类型的取值范围:-2的31次方 ~ 2的31次方-1 10 | 11 | - Python标准整数类型等价于C语言的(有符号)长整型。 12 | - 八进制整数以数字 “0” 开头,十六进制整数以 “0x” 或 “0X” 开头 13 | 14 | ## 5.3 长整型 15 | - Python的长整数类型能表达的数值,仅与机器支持的(虚拟)内存大小有关。 16 | - 在一个整数值后面加个大写L,表示是长整数。 17 | - 必要时整型会自动转换为长整型。 18 | 19 | ## 5.4 双精度浮点数 20 | - 每个浮点数占8个字节 21 | - 类似C语言中的double类型 22 | - 浮点数值通常都有一个小数点和一个可选的后缀e,eg: `9.384e-23` 23 | 24 | ## 5.5 复数 25 | - Python1.4开始支持复数 26 | - 虚数的表示: real+imagj,eg: `6.23+1.5j` 27 | 28 | **复数属性:** 29 | 30 | - num.real:复数的实部 31 | - num.imag:复数的虚部 32 | - num.conjugate():返回该复数的共轭复数 33 | 34 | ## 5.6 数值类型转换 35 | 不同数据类型之间在运算之前,Python内部会将两个操作数转换为同一数据类型(强制转换)。 36 | 37 | **不同数据类型转换基本原则**:整数转换为浮点数,非复数转换为复数。 38 | 39 | 数值类型强制转换的流程: 40 | 41 | ![Integer Type Built-in Functions](./5.7 Integer Type Built-in Functions.png "Integer Type Built-in Functions") 42 | 43 | 使用内建函数`coerce()`,对某种数据类型进行特殊类型转换。 44 | 45 | ## 5.7 运算符 46 | - / :除法运算,如果两个数都是整数则地板除,否则执行真正的除法(未来版本Python中除法运算的标准行为:不管操作数是整数还是浮点数,除法运算总是返回真实的商。现阶段通过执行 `from __future__ import division` 指令可实现真正的除法) 47 | - //:地板除(向下取整) 48 | - % :取余,对于浮点数结果为:`x - (math.floor(x/y) * y)` 49 | - **:幂运算,操作符比其左侧操作数的一元操作符优先级高,比右侧操作数的一元操作符的优先级低(右边的是老大) 50 | - `-3 ** 2` # 优先级高于左侧的 - 51 | - `4.0 ** -1.0` # 优先级低于右侧的 - 52 | - 位运算符:取反优先级最高,左移和右移次之,与、或、异或最低; 53 | 54 | ## 5.8 数字类型函数 55 | ### 5.8.1 转换工厂函数 56 | - `int(4.25555)` # 4 57 | - `long(42)` # 42L 58 | - `float(4)` # 4.0 59 | - `complex(2.3e-10, 45.3e4)` # (2.3e-10+453000j) 60 | 61 | ### 5.8.2 功能函数 62 | - **abs(num)**:返回给定参数的绝对值。eg:`abs(-1)` # 1 63 | - **coerce(num1,num2)**:返回一个包含类型转换完毕的两个数值元素的元组。eg:`coerce(1.23-41j, 134L)` # ((1.23-41j), (134+0j)) 64 | - **divmod(num1,num2)**:结合除法运算和取余运算, 返回一个包含商和余数的元组。eg:`divmod(10,3)` # (3, 1) 65 | - **pow(num1,num2,mod=1)**:指数运算,eg:`pow(2,3)` 。如果提供 mod参数,则再对mod进行取余运算 66 | - **round(flt, ndig=0)** 接受一个浮点数flt并对其四舍五入,保存ndig位小数。 若不提供ndig参数,则默认小数点后0位 67 | 68 | ### 5.8.3 仅用于整数的函数 69 | ![](index_files/Unnamed_20QQ_20Screenshot20160218184306.png) 70 | 71 | ## 5.9 其他数字类型 72 | ### 5.9.1 布尔“数” 73 | - 有两个永不改变的值 True 或 False。 74 | - 布尔型是整型的子类,但是不能再被继承而生成它的子类。 75 | - 没有`__nonzero__()`方法的对象的默认值是 True。 76 | - 对于值为零的任何数字或空集(空列表、空元组和空字典等)在 Python 中的布尔值都 是 False。 77 | - 在数学运算中,Boolean 值的 True 和 False 分别对应于 1 和 0。 78 | - 以前返回整数的大部分标准库函数和内建布尔型函数现在返回布尔型。 79 | - True 和 False 现在都不是关键字,但是在 Python 将来的版本中会是。 80 | 81 | ### 5.9.2 十进制浮点数 Decimal 82 | ```python 83 | from decimal import Decimal 84 | dec = Decimal('.1') 85 | result = dec + Decimal('10.1') 86 | ``` 87 | 88 | ## 5.10 数字类型相关模块 89 | - 第三方包:Numeric(NumPy) 和 SciPy 90 | - array:高效数值数组(字符,整数,浮点数等等) 91 | - math/cmath:标准C库数学运算函数。常规数学运算在match模块,复数运算在cmath模块 92 | - operator:数字运算符的函数实现 93 | - random:多种伪随机数生成器 94 | - `random.randrange(100, 110, 3)` # 103,随机返回range([start,]stop[,step])结果的一项 95 | - `random.uniform(1.1, 1.34)` # 1.300319482480275,返回的是二者之间的一个浮点数(不包括范围上限) 96 | - `random.randint(12,23)` # 21,返回的是二者之间的一个整数(包括范围上限) 97 | - `random.random()` # 0.926576527700594,和uniform()类似,只不过下限恒等于 0.0,上限恒等于 1.0 98 | - `random.choice([1,2,4])` # 4,随机返回给定序列的一个元素 99 | 100 | # 练习 101 | **5-1 整形。讲讲Python普通整型和长整型的区别。** 102 | 普通整型:最大可表示2的63方-1(64位机器) 103 | 长整形:可表示的最大数只与机器的内存有关,普通整型“溢出”时Python可将其自动转为长整形 104 | 105 | **5-2 运算符。 106 | (a) 写一个函数,计算并返回两个数的乘积 107 | (b) 写一段代码调用这个函数,并显示它的结果** 108 | 109 | ```pytnon 110 | def multiply(num1, num2): 111 | return num1 * num2 112 | 113 | print multiply(7, 11) 114 | ``` 115 | 116 | **5-3 标准类型运算符. 写一段脚本,输入一个测验成绩,根据下面的标准,输出他的评分成绩(A-F)。 A: 90–100 B: 80–89 C: 70–79 D: 60–69 F: < 60** 117 | ```python 118 | def get_letter_grade(score): 119 | if score >= 90 : 120 | return 'A' 121 | elif score >=80 : 122 | return 'B' 123 | elif score >=70 : 124 | return 'C' 125 | elif score >= 60 : 126 | return 'D' 127 | else : 128 | return 'F' 129 | ``` 130 | 131 | **5-4 取余。判断给定年份是否是闰年。使用下面的公式: 一个闰年就是指它可以被 4 整除但不能被 100 整除, 或者它可以被 400 整 除。比如 1992,1996 和 2000 年是闰年,但 1967 和 1900 则不是闰年。下一个是闰年的整世 纪是 2400 年。** 132 | ```python 133 | def is_leep_year(year): 134 | if year % 4 == 0: 135 | if year % 100 != 0 : 136 | return True 137 | elif year % 400 == 0: 138 | return True 139 | else: 140 | return False 141 | else: 142 | return False 143 | 144 | print is_leep_year(1992) 145 | print is_leep_year(2000) 146 | print is_leep_year(1900) 147 | print is_leep_year(1999) 148 | ``` 149 | 150 | **5-5 取余。取一个任意小于 1 美元的金额,然后计算可以换成最少多少枚硬币。硬币有 1 美分,5 美分,10 美分,25 美分四种。1 美元等于 100 美分。举例来说,0.76 美元换算结果 应该是 3 枚 25 美分, 1 枚 1 美分。类似 76 枚 1 美分,2 枚 25 美分+2 枚 10 美分+1 枚 5 美分+1 枚 1 美分这样的结果都是不符合要求的。** 151 | 152 | 思路:先取大面值的,再依次取小面值的,得到的就是最少枚硬币的组合 153 | ```python 154 | def get_cent_count(num): 155 | cent_count_arr = [] 156 | cents = [25, 10, 5, 1] 157 | 158 | left = num 159 | i = 0 160 | while left != 0: 161 | cent_count_arr.append(left // cents[i]) 162 | left = left % cents[i] 163 | i += 1 164 | 165 | return cent_count_arr 166 | 167 | print get_cent_count(76) 168 | print get_cent_count(123) 169 | print get_cent_count(88) 170 | ``` 171 | 172 | **5-6 算术。写一个计算器程序 你的代码可以接受这样的表达式,两个操作数加一个运算符: N1 运算符 N2. 其中 N1 和 N2 为整数或浮点数,运算符可以是+, -, \*, /, %, \*\* 分别表示 加法,减法, 乘法, 整数除,取余和幂运算。计算这个表达式的结果,然后显示出来。提示: 可以使用字符串方法 split(),但不可以使用内建函数 eval().** 173 | 174 | 思路:如果使用eval()的话,直接可以计算表达式的结果:`eval('3 * 4')`。此处需将数字字符串转换成数字,由于不知道数字的类型,使用Decimal进行转换比较方便。当然,一个个数字类型去判断也是可以的。 175 | ```python 176 | import decimal 177 | 178 | def num1_op_num2(op_str): 179 | arr = op_str.split() 180 | op = arr[1] 181 | num1 = decimal.Decimal(arr[0]) 182 | num2 = decimal.Decimal(arr[2]) 183 | 184 | if op == '+': 185 | return num1 + num2 186 | elif op == '-': 187 | return num1 - num2 188 | elif op == '*': 189 | return num1 * num2 190 | elif op == '/': 191 | return num1 / num2 192 | elif op == '%': 193 | return num1 % num2 194 | elif op == '**': 195 | return num1 ** num2 196 | else: 197 | raise ValueError("op don't exist") 198 | 199 | print num1_op_num2("3 * 4") 200 | print num1_op_num2("11 % 7") 201 | print num1_op_num2("5 ** 2") 202 | ``` 203 | 204 | **5-7 营业税。随意取一个商品金额,然后根据当地营业税额度计算应该交纳的营业税。** 205 | 略。 206 | 207 | **5-8 几何。计算面积和体积: 208 | (a) 正方形 和 立方体 209 | (b) 圆 和 球。** 210 | 略。 211 | 212 | **5–9. 数值形式 回答下面关于数值格式的问题:** 213 | **(a) 为什么下面的例子里 17+32 等于 49, 而 017+32 等于 47, 017+032 等于 41?** 214 | Because:数字以0开头表示8进制 215 | 216 | **(b)为什么下面这个表达式我们得到的结果是 134L 而不是 1342 ?** 217 | Because:数字以l(L,不是1)结尾表示长整形 218 | 219 | **5-10 转换。写一对函数来进行华氏度到摄氏度的转换。转换公式为 C = (F - 32) * (5 / 9) 应该在这个练习中使用真正的除法, 否则你会得到不正确的结果。** 220 | ```python 221 | from __future__ import division 222 | 223 | def tran_F_to_C(F): 224 | C = (F - 32) * (5 / 9) 225 | return round(C, 2) # 取小数点后两位 226 | 227 | print tran_F_to_C(42) 228 | ``` 229 | 230 | **5-11 取余。 ** 231 | (a) 使用循环和算术运算,求出 0-20 之间的所有偶数 232 | (b) 同上,不过这次输出所有的奇数 233 | (c) 综合 (a) 和 (b), 请问辨别奇数和偶数的最简单的方法是什么? 234 | (d) 使用(c)的成果,写一个函数,检测一个整数能否被另一个整数整除。 先要求用户输 入两个数,然后你的函数判断两者是否有整除关系,根据判断结果分别返回 True 和 False; 235 | 236 | *略。* 237 | 238 | **5-12 系统限制。写一段脚本确认一下你的 Python 所能处理的整数,长整数,浮点数和复数的范围。** 239 | ```python 240 | import sys 241 | 242 | print sys.maxint 243 | print -sys.maxint-1 244 | print sys.float_info 245 | print sys.long_info 246 | ``` 247 | result: 248 | ``` 249 | 2147483647 250 | -2147483648 251 | sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1) 252 | sys.long_info(bits_per_digit=30, sizeof_digit=4) 253 | ``` 254 | 255 | **5-13 转换。写一个函数把由小时和分钟表示的时间转换为只用分钟表示的时间。** 256 | 略。 257 | 258 | **5-14 银行利息。写一个函数,以定期存款利率为参数, 假定该账户每日计算复利,请计算并返回年回报率。** 259 | ```python 260 | def compound_interest(rate): 261 | return (1+rate) ** 364 262 | 263 | print compound_interest(0.0005) 264 | ``` 265 | 266 | **5–15 最大公约数和最小公倍数。请计算两个整数的最大公约数和最小公倍数。** 267 | ```python 268 | def gcd(num1, num2): 269 | while num2 > 0: 270 | tmp = num1 % num2 271 | num1 = num2 272 | num2 = tmp 273 | return num1 274 | 275 | def lcm(num1, num2): 276 | return num1 * num2 / gcd(num1, num2) 277 | 278 | print gcd(24, 18) 279 | print lcm(24,18) 280 | ``` 281 | 282 | **5-16 家庭财务。给定一个初始金额和月开销数, 使用循环,确定剩下的金额和当月的支出数, 包括最后的支出数。 Payment() 函数会用到初始金额和月额度, 输出结果应该类似下 面的格式(例子中的数字仅用于演示):** 283 | 284 | ![5-16 exercise](./5-16 exercise.png "5-16 exercise") 285 | 286 | ```python 287 | def pay(): 288 | balance = float(raw_input('Enter opening balance: ')) 289 | balance = round(balance, 2) 290 | payment = float(raw_input('Enter monthly payment: ')) 291 | payment = round(payment, 2) 292 | 293 | print ' \tAmount\tRemaining' 294 | print 'Pymt#\tPaid\tBalance' 295 | print '------\t------\t------' 296 | 297 | print '0\t$0.00\t$%s' % balance 298 | i = 1 299 | while balance > payment: 300 | balance -= payment 301 | balance = round(balance, 2) 302 | print '%d\t$%s\t$%s' % (i, payment, balance) 303 | print '%d\t$%s\t$0.00' % (i, balance) 304 | 305 | pay() 306 | ``` 307 | 308 | **5-17 随机数。熟读随机数模块然后解下面的题: 生成一个有 N 个元素的由随机数 n 组成的列表, 其中 N 和 n 的取值范围分别为: (1 < N <= 100), (0 <= n <= 2^31 -1)。然后再随机从这个列表中取 N (1 <= N <= 100)个随机数 出来, 对它们排序,然后显示这个子集。** 309 | ```python 310 | import random 311 | 312 | def rand_sort_num(): 313 | res = [] 314 | N = random.randint(1, 100) 315 | while N > 0: 316 | n = random.randint(0, 2**31-1) 317 | res.append(n) 318 | N -= 1 319 | 320 | res.sort() 321 | return res 322 | 323 | print rand_sort_num() 324 | ``` -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-10_reverse_up_low.py: -------------------------------------------------------------------------------- 1 | #!usrbinenv python 2 | 3 | def reverse(ss): 4 | result = [] 5 | for s in ss: 6 | if 'a' <= s <= 'z': 7 | result.append(s.upper()) 8 | elif 'A' <= s <= 'Z': 9 | result.append(s.lower()) 10 | else: 11 | result.append(s) 12 | return ''.join(result) 13 | 14 | print reverse('Mr.Ed') 15 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-14_rock_paper_scissors.py: -------------------------------------------------------------------------------- 1 | #!usrbinenv python 2 | import random 3 | 4 | def rps(arg1, arg2): 5 | # ['rock','paper','scissors'] 6 | rule = {'rp':-1,'rs':1,'rr':0, 'pr':1,'ps':-1,'pp':0, 'sr':-1,'sp':1,'ss':0} 7 | return rule[arg1[0] + arg2[0]] 8 | 9 | user_value = raw_input('your choose: [rock|paper|scissors] ') 10 | app_value = ['rock','paper','scissors'][random.randint(0,2)] 11 | print 'app chooses ' + app_value 12 | 13 | res = rps(user_value, app_value) 14 | if res == 1: 15 | print 'user win' 16 | elif res == 0: 17 | print 'equals' 18 | else: 19 | print 'application win' 20 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-1_idcheck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import string 3 | import keyword 4 | 5 | alphas = string.letters + '_' 6 | nums = string.digits 7 | 8 | print 'Welcome to the Identifier Checker v1.0' 9 | print 'Testees must be at least 2 chars long.' 10 | myInput = raw_input('Identifier to test? ') 11 | 12 | if myInput[0] not in alphas: 13 | print 'invalid: first symbol must be alphabetic' 14 | else: 15 | if len(myInput) == 0: 16 | print "okay as an identifier" 17 | else: 18 | for otherChar in myInput[1:]: 19 | if otherChar not in alphas + nums: 20 | print 'invalid: remaining symbols must be alphanumeric' 21 | break 22 | if keyword.iskeyword(myInput): 23 | print "invalid: input string is a keyword" 24 | else: 25 | print "okay as an identifier" 26 | 27 | 28 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-3_sort_numstring.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import string 3 | 4 | def des_sort_numstr_by_decimal(numstr): 5 | numlist = numstr.split(',') 6 | for i in range(0, len(numlist)): 7 | numlist[i] = int(numlist[i]) 8 | 9 | numlist.sort() 10 | numlist.reverse() 11 | return numlist 12 | def des_sort_numstr_by_dictionary(numstr): 13 | numlist = numstr.split(',') 14 | numlist.sort() 15 | numlist.reverse() 16 | return numlist 17 | 18 | if __name__ == '__main__': 19 | numstr = raw_input('input some number: ') 20 | print "before sort: " + numstr 21 | print "sort by decimal: " + str(des_sort_numstr_by_decimal(numstr)) 22 | print "sort by dictionary: " + str(des_sort_numstr_by_dictionary(numstr)) -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-4_get_letter_grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | def cal_average_score(score_list): 3 | sum = 0 4 | for score in score_list: 5 | sum += score 6 | return float(sum) / len(score_list) 7 | 8 | def get_letter_grade(score_list): 9 | average_score = cal_average_score(score_list) 10 | if average_score >= 90 : 11 | return 'A' 12 | elif average_score >=80 : 13 | return 'B' 14 | elif average_score >=70 : 15 | return 'C' 16 | elif average_score >= 60 : 17 | return 'D' 18 | else : 19 | return 'F' 20 | 21 | print get_letter_grade([79,23,99,82,54,43]) -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-6_my_strip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import string 3 | 4 | def my_string_strip(_str): 5 | if len(_str) <= 0: 6 | return _str 7 | 8 | # left strip 9 | while _str[0] == ' ': 10 | _str = _str[1:len(_str)] 11 | # right strip 12 | while _str[len(_str)-1] == ' ': 13 | _str = _str[0:len(_str)-1] 14 | 15 | return _str 16 | 17 | print my_string_strip(' asdf asdf ') -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-7_fix-bug.py: -------------------------------------------------------------------------------- 1 | #!usrbinenv python 2 | 3 | num_str = raw_input('Enter a number ') 4 | num_num = int(num_str) 5 | fac_list = range(1, num_num+1) 6 | print "BEFORE:", `fac_list` 7 | 8 | i = 0 9 | while i < len(fac_list): 10 | if num_num % fac_list[i] == 0: 11 | del fac_list[i] 12 | i -= 1 13 | i = i + 1 14 | 15 | print "AFTER:", `fac_list` 16 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6-8_english-num.py: -------------------------------------------------------------------------------- 1 | #!usrbinenv python 2 | class numtool(): 3 | zero_to_nine = ['zero','one','two','three','four','five','six','seven','eight','nine'] 4 | ten_to_nineteen = ['ten','eleven','twelve','thirdteen','fourteen','fifteen','sixteen','eighteen','nineteen'] 5 | twenty_to_ninety = ['twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety'] 6 | hundred = 'hundred' 7 | _and = ' and ' 8 | space = ' ' 9 | 10 | def num2en_0_to_100(self, num): 11 | if not 0 <= num < 100: 12 | return 13 | 14 | elif num < 10: 15 | return self.zero_to_nine[num] 16 | elif num < 20: 17 | return self.ten_to_nineteen[num-10] 18 | elif num < 100: 19 | if num % 10 == 0: 20 | return self.twenty_to_ninety[num/10 - 2] 21 | else: 22 | left = self.twenty_to_ninety[num/10 - 2] 23 | right = self.zero_to_nine[num % 10] 24 | return left + self.space + right 25 | 26 | def num2en(self, num): 27 | if not 0 <= num <= 1000: 28 | return 29 | 30 | if num < 100: 31 | return self.num2en_0_to_100(num) 32 | else: 33 | if num == 1000: 34 | return 'one thousand' 35 | elif num % 100 == 0: 36 | return self.zero_to_nine[num / 100] + self.space + self.hundred 37 | else: 38 | left = self.zero_to_nine[num / 100] 39 | right = self.num2en_0_to_100(num % 100) 40 | return left + self.space + self.hundred + self._and + right 41 | 42 | tool = numtool() 43 | while True: 44 | num = raw_input('input a number: ') 45 | print tool.num2en(int(num)) -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6.1 Sequence Type Operators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/06_Sequences-Strings, Lists, and Tuples/6.1 Sequence Type Operators.png -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6.15.1-stack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | stack = [] 4 | 5 | def pushit(): 6 | stack.append(raw_input('Enter new string: ').strip()) 7 | 8 | def popit(): 9 | if len(stack) == 0: 10 | print 'Cannot pop from an empty stack!' 11 | else: 12 | ''' 13 | use single backquotes or backticks ( ` ) to symbolize the repr() command, 14 | showing the string complete with quotes, not just the contents of the string. 15 | ''' 16 | print 'Removed [', `stack.pop()`, ']' 17 | 18 | 19 | def viewstack(): 20 | print stack # calls str() internally 21 | 22 | CMDs = {'u': pushit, 'o': popit, 'v': viewstack} 23 | 24 | def showmenu(): 25 | pr = """ 26 | p(U)sh 27 | p(O)p 28 | (V)iew 29 | (Q)uit 30 | 31 | Enter choice: """ 32 | 33 | while True: 34 | while True: 35 | try: 36 | choice = raw_input(pr).strip()[0].lower() 37 | except (EOFError,KeyboardInterrupt,IndexError): 38 | choice = 'q' 39 | 40 | print '\nYou picked: [%s]' % choice 41 | if choice not in 'uovq': 42 | print 'Invalid option, try again' 43 | else: 44 | break 45 | 46 | if choice == 'q': 47 | break 48 | CMDs[choice]() 49 | 50 | if __name__ == '__main__': 51 | showmenu() 52 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6.15.2-queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | queue = [] 4 | 5 | def enQ(): 6 | queue.append(raw_input('Enter new string: ').strip()) 7 | 8 | def deQ(): 9 | if len(queue) == 0: 10 | print 'Cannot pop from an empty queue!' 11 | else: 12 | print 'Removed [', `queue.pop(0)`, ']' 13 | 14 | def viewQ(): 15 | print queue # calls str() internally 16 | 17 | CMDs = {'e': enQ, 'd': deQ, 'v': viewQ} 18 | 19 | def showmenu(): 20 | pr = """ 21 | (E)nqueue 22 | (D)equeue 23 | (V)iew 24 | (Q)uit 25 | 26 | Enter choice: """ 27 | 28 | while True: 29 | while True: 30 | try: 31 | choice = raw_input(pr).strip()[0].lower() 32 | except (EOFError,KeyboardInterrupt,IndexError): 33 | choice = 'q' 34 | print '\nYou picked: [%s]' % choice 35 | if choice not in 'devq': 36 | print 'Invalid option, try again' 37 | else: 38 | break 39 | 40 | if choice == 'q': 41 | break 42 | CMDs[choice]() 43 | 44 | if __name__ == '__main__': 45 | showmenu() 46 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/6.8-encode-decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | An example of reading and writing Unicode strings: Writes 4 | a Unicode string to a file in utf-8 and reads it back in. 5 | ''' 6 | CODEC = 'utf-8' 7 | FILE = 'unicode.txt' 8 | 9 | hello_out = u"Hello world\n" 10 | bytes_out = hello_out.encode(CODEC) 11 | f = open(FILE, "w") 12 | f.write(bytes_out) 13 | f.close() 14 | 15 | f = open(FILE, "r") 16 | bytes_in = f.read() 17 | f.close() 18 | hello_in = bytes_in.decode(CODEC) 19 | print hello_in, 20 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/note06_Sequences-Strings, Lists, and Tuples.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 详细介绍字符串、列表、元组的相关操作,常用的序列内建函数,Unicode和编码解码原理,深拷贝和浅拷贝的原理。 3 | 4 | # 知识点 5 | ## 6.1 序列 6 | ### 6.1.1 标准类型操作符 7 | 标准类型操作符一般都能适用于所有的序列类型。 8 | 9 | ### 6.1.2 序列类型操作符 10 | 11 | ![](./6.1 Sequence Type Operators.png "Sequence Type Operators") 12 | 13 | **高效合并:** 14 | 15 | - 字符串:`''.join(strList)` 16 | - 列表:`list1.extend(list2)` 17 | 18 | 实战:遍历一个字符串,每次都把位于最后的一个字符砍掉 19 | ```python 20 | for i in [None] + range(-1, -len(strList), -1): 21 | print strList[:i] 22 | ``` 23 | 24 | ### 6.1.3 序列的类型转换工厂函数 25 | 26 | - `list(iter)` 27 | - `tuple(iter)` 28 | - `str(obj)` 29 | - `unicode(obj)` 30 | 31 | ## 6.2 字符串 32 | 33 | ### 6.2.1 字符串的操作符 34 | 35 | 判断一个字符串是否包含另一个字符串: `find()`、`index()`、`rfind()`、`rindex()` 36 | 37 | **string模块预定义的字符串:** 38 | 39 | - `string.ascii_uppercase` 40 | - `string.ascii_lowercase` 41 | - `string.letters` 42 | - `string.digits` 43 | 44 | **出于性能方面的考虑, 尽量不适用string模块:**Python必须为每一个参加连接操作的字符串分配新的内存,包括新产生的字符串。最好使用`join()`进行字符串的连接! 45 | 46 | **注意:**把重复操作作为参数放到循环里面进行是非常低效的。 47 | e.g.:`while i < len(myString):` 48 | 可优化为:`length = len(myString)` 49 | 50 | **编译时字符串连接:**一个字符串字分成几个部分写(避免加反斜杠),作为函数参数时可换行写并加上注释。 51 | e.g.:`foo = "Hello" 'world!'` 52 | 53 | ### 6.2.2 字符串的格式化 54 | #### 6.2.2.1 格式化操作符( % ) 55 | 1、元组形式: 56 | 57 | - `"%x" % 108 ` \# '6c' 58 | - `"%#X" % 108 ` \# '0X6C' 59 | - `'%.2f' % 1234.567890` \# '1234.57' 60 | - `'%E' % 1234.567890` \# '1.234568E+03' 61 | - `'%g' % 1234.567890234` \# '1234.57' 62 | - `'dec: %d/oct: %#o/hex: %#X' % (123, 123, 123)` \# 'dec: 123/oct: 0173/hex: 0X7B' 63 | - `"MM/DD/YY = %02d/%02d/%d" % (2, 15, 67)` \# 'MM/DD/YY = 02/15/67' 64 | 65 | 2、字典形式: 66 | 67 | `'There are %(howmany)d %(lang)s Quotation Symbols' % {'lang': 'Python', 'howmany': 3}` 68 | \# 'There are 3 Python Quotation Symbols' 69 | 70 | #### 6.2.2.2 字符串模板 71 | ```python 72 | #!/usr/bin/env python 73 | import string from Tamplate 74 | 75 | s = Template('There are ${howmany} ${lang} Quotation Symbols') 76 | 77 | # There are 3 Python Quotation Symbols 78 | print s.substitute(lang='Python', howmany=3) 79 | 80 | # There are ${howmany} Python Quotation Symbols 81 | print s.safe_substitute(lang='Python') 82 | ``` 83 | 84 | #### 6.2.2.3 原始字符串操作符( r/R ) 85 | e.g.:`m = re.search(r'\\[rtfvn]', r'Hello World!\n')` 86 | 87 | #### 6.2.2.4 Unicode字符串操作符( u/U ) 88 | - `u'a\u1234\n'` \# U+0061 U+1234 U+0012 89 | - 原始Unicode字符串:`ur'Hello\nWorld!'` 90 | 91 | ### 6.2.3 字符串的内建函数 92 | 初始:`quest = 'what is your favorite color?'` 93 | 94 | - `quest.center(40)` # 居中后剩下的字符用空格填充 95 | - `quest.count('or')` # 2 96 | - `quest.find('or', 22)` # 25 97 | - `quest.index('or', 10)` # 16, 同find(), 但如果'or'不存在会报错 98 | 99 | ## 6.3 常用内建函数 100 | - `cmp('abc', 'lmn')` # -11 101 | - `min('ab42cd')` # '2' 102 | - `for i, t in enumerate('foo')` 103 | - `zip('foa', 'obr')` # [('f', 'o'), ('o', 'b'), ('a', 'r')] 104 | - `isinstance(u'\0xAB', str)` # False 105 | - `chr(65) `# 'A' 106 | - `ord('a')` # 97 107 | - `unichr(12345)` # u'\u3039' 108 | 109 | ## 6.4 Unicode 110 | 1. ASCII码用8位二进制(一个字节)把字符转换成数字的方式存储在计算机内。 111 | 2. Unicode 使用一个或多个字节来表示一个字符的方法突破了 ASCII 的限制, 可以表示超过 90,000 个字符。 112 | 3. 在任何需要跟 Unicode 兼容的代码里都不要string模块,Python保留该模块仅仅是为了向后兼容。 113 | 4. 内建函数 `unicode()`和 `unichar()`可以看成 Unicode 版本的 `str()`和 `chr()`。 114 | 5. `unicode()` 函数可以把任何 Python 的数据类型转换成一个 Unicode 字符串,如果是对象,并且该对象定义 了`__unicode__()`方法,它还可以把该对象转换成相应的 Unicode 字符串。 115 | 116 | **Codecs**: 117 | 118 | - codec 是 COder/DECoder 的首字母组合,它定义了文本跟二进制值的转换方式。 119 | - 常见的有编码格式:ASCII,ISO 8859-1/Latin-1,UTF-8,UTF-16。 120 | - **UTF-8编码**:用一个字节来编码 ASCII 字符,用 1~4 个字节来表示其他语言的字符,CJK/East这样的东亚文字一般用3个字节来表示,少数字符用 4个字节来表示。 121 | - UTF-16编码:容易读写,但不向后兼容ASCII,所有的字符都是用单独的一个16位字(2个字节)来存储的。 122 | - **编码解码**:`encode()` 和 `decode()` 内建函数接受一个字符串做参数返回该字符串对应的 解码后/编码后 的字符串。 123 | 124 | ## 6.5 列表 125 | 126 | ### 6.5.1 列表解析 127 | `[ i * 2 for i in [8, -2, 5] ]` #[16, -4, 10] 128 | `[ i for i in range(8) if i % 2 == 0 ]` #[0, 2, 4, 6] 129 | 130 | ### 6.5.2 列表的cmp流程 131 | 132 | 1. 对两个列表的元素进行比较. 133 | 2. 如果比较的元素是同类型的,则比较其值,返回结果. 134 | 3. 如果两个元素不是同一种类型,则检查它们是否是数字. 135 | a. 如果是数字,执行必要的数字强制类型转换,然后比较. 136 | b. 如果有一方的元素是数字,则另一方的元素比较"大"(数字是"最小的") 137 | c. 否则,通过类型名字的字母顺序进行比较 138 | 4. 如果有一个列表首先到达末尾,则另一个长一点的列表比较"大". 139 | 5. 如果用尽了两个列表的元素,而且所有元素都是相等的,那么返回0. 140 | 141 | ### 6.5.3 使用内建函数操作列表 142 | - `for t in reversed(s): `# `reversed(s)`得到的是一个listreverseiterator object,而`s.reverse()`无返回值 143 | - `sorted(s)` # 返回排序后的一个新列表,而`s.sort()`无返回值 144 | - `for i, album in enumerate(albums):` # 遍历时有序号和元素 145 | - `for i, j in zip(list1, list2):` # 同时遍历两个列表 146 | - `reduce(operator.add, numList)` # 对numList的所有元素进行add操作 147 | - `sum(numList, 7)` # 计算numList的总和,再加上7 148 | 149 | 小技巧-同时输出三个对象的ID:`[id(x) for x in aList, aTuple, anotherList]` 150 | 151 | ### 6.5.4 列表的方法 152 | **获取元素在列表中的位置** 153 | 使用`index()`获取元素位置时,若元素不存在会抛ValueError,解决方案是先用` in `判断一下,如: 154 | ```python 155 | for eachMediaType in (45, '8-track tape', 'cassette'): 156 | if eachMediaType in music_media: 157 | print music_media.index(eachMediaType) 158 | ``` 159 | 160 | **`extend()`的妙用(参数支持任何可迭代对象):** 161 | ```python 162 | >>> motd = [] 163 | >>> motd.append('MSG OF THE DAY') 164 | >>> f = open('/lines.txt', 'r') 165 | >>> motd.extend(f) 166 | >>> f.close() 167 | >>> motd 168 | ['MSG OF THE DAY', 'line1\n', 'line2\n'] 169 | ``` 170 | ### 6.5.5 用列表实现其他数据结构 171 | - 堆栈:stack是一种后进先出(LIFO)的数据类型 172 | - 队列:queue是一种先进先出(FIFO)的数据类型 173 | 174 | 175 | ## 6.6 元组 176 | 所有的多对象的,逗号分隔的,没有明确用符号定义的,等等这些集合默认的类型都是元组。 177 | 178 | **元组除了不可变操作外,其他操作大多和列表相似。** 179 | 180 | **实用例子:** 181 | 182 | - “移除”元组的元素:`aTuple = aTuple[0] , aTuple[-1]` # 通过组合一个新的元组来实现 183 | - 创建单一元素的元组:`('xyz',)` # 圆括号被重载了,它也被用作分组操作符。 184 | 185 | 186 | **元组不可变性的好处:** 187 | 188 | - 当把元组数据传给一个API时,可以确保数据不会被修改。同样地,如果操作从一个函数返回的元组,可以通过内建list()函数把它转换成一个列表。 189 | - 不可变对象的值是不可改变的,它们通过 hash 算法得到的值总是一个值,这是作为字典键值的一个必备条件。 190 | 191 | ## 6.7 浅拷贝和深拷贝 192 | ## 6.7.1 浅拷贝 193 | 194 | **浅拷贝:**新创建了一个类型跟原对象一样的对象,且内容是原来对象元素的引用。即拷贝的对象本身是新的,但是它的内容不是。序列类型对象的默认类型拷贝就是浅拷贝。 195 | 196 | **浅拷贝的方式有:** 197 | 198 | 1. 完全切片操作[:] 199 | 2. 利用工厂函数`list()`、`dict()`等 200 | 3. 使用 copy 模块的 `copy()` 201 | 202 | ## 6.7.2 深拷贝 203 | **深拷贝:**新创建了一个类型跟原对象一样的对象,内容和原来对象元素的相同但不是同样的对象元素,更改原对象的元素不会影响到深拷贝对象,反之亦然。深拷贝可以通过copy模块的`deepcopy()`实现,或手动遍历一个个copy。 204 | 205 | **深拷贝的约束条件(具有可变性的容器类型才能深拷贝):** 206 | 207 | - 非容器类型(比如数字、字符串和元组等)没有被拷贝一说,浅拷贝是用完全切片操作来完成的。 208 | - 如果元组变量只包含原子类型对象,对它的深拷贝将不会进行。 209 | 210 | # 练习 211 | **6–1. 字符串.string 模块中是否有一种字符串方法或者函数可以帮我鉴定一下一个字符串是否是另一个大字符串的一部分?** 212 | 213 | `string.find()` & `string.index()` 214 | 215 | **6–2. 字符串标识符.修改例 6-1 的 idcheck.py 脚本,使之可以检测长度为一的标识符,并且可以识别 Python 关键字,对后一个要求,你可以使用 keyword 模块(特别是 keyword.kelist)来帮你.** 216 | 217 | ```python 218 | #!/usr/bin/env python 219 | import string 220 | import keyword 221 | 222 | alphas = string.letters + '_' 223 | nums = string.digits 224 | 225 | print 'Welcome to the Identifier Checker v1.0' 226 | print 'Testees must be at least 2 chars long.' 227 | myInput = raw_input('Identifier to test? ') 228 | 229 | if myInput[0] not in alphas: 230 | print 'invalid: first symbol must be alphabetic' 231 | else: 232 | if len(myInput) == 0: 233 | print "okay as an identifier" 234 | else: 235 | for otherChar in myInput[1:]: 236 | if otherChar not in alphas + nums: 237 | print 'invalid: remaining symbols must be alphanumeric' 238 | break 239 | if keyword.iskeyword(myInput): 240 | print "invalid: input string is a keyword" 241 | else: 242 | print "okay as an identifier" 243 | ``` 244 | 245 | **6–3. 排序** 246 | (a) 输入一串数字,从大到小排列之. 247 | (b) 跟a一样,不过要用字典序从大到小排列之. 248 | 249 | ```python 250 | #!/usr/bin/env python 251 | import string 252 | 253 | def des_sort_numstr_by_decimal(numstr): 254 | numlist = numstr.split(',') 255 | for i in range(0, len(numlist)): 256 | numlist[i] = int(numlist[i]) 257 | 258 | numlist.sort() 259 | numlist.reverse() 260 | return numlist 261 | def des_sort_numstr_by_dictionary(numstr): 262 | numlist = numstr.split(',') 263 | numlist.sort() 264 | numlist.reverse() 265 | return numlist 266 | 267 | if __name__ == '__main__': 268 | numstr = raw_input('input some number: ') 269 | print "before sort: " + numstr 270 | print "sort by decimal: " + str(des_sort_numstr_by_decimal(numstr)) 271 | print "sort by dictionary: " + str(des_sort_numstr_by_dictionary(numstr)) 272 | ``` 273 | 274 | **6–4. 算术. 更新上一章里面你的得分测试练习方案,把测试得分放到一个列表中去.你的代码应该可以计算出一个平均分,见练习 2-9 和练习 5-3.** 275 | ```python 276 | #!/usr/bin/env python 277 | def cal_average_score(score_list): 278 | sum = 0 279 | for score in score_list: 280 | sum += score 281 | return float(sum) / len(score_list) 282 | 283 | def get_letter_grade(score_list): 284 | average_score = cal_average_score(score_list) 285 | if average_score >= 90 : 286 | return 'A' 287 | elif average_score >=80 : 288 | return 'B' 289 | elif average_score >=70 : 290 | return 'C' 291 | elif average_score >= 60 : 292 | return 'D' 293 | else : 294 | return 'F' 295 | 296 | print get_letter_grade([79,23,99,82,54,43]) 297 | ``` 298 | 299 | **6–5. 字符串** 300 | (a)更新你在练习 2-7 里面的方案,使之可以每次向前向后都显示一个字符串的一个字符. 301 | (b)通过扫描来判断两个字符串是否匹配(不能使用比较操作符或者 cmp()内建函数)。附加题:在你的方案里加入大小写区分. 302 | (c)判断一个字符串是否重现(后面跟前面的一致).附加题:在处理除了严格的回文之外,加入对 例如控制符号和空格的支持。 303 | (d)接受一个字符,在其后面加一个反向的拷贝,构成一个回文字符串. 304 | 略。 305 | 306 | **6–6. 字符串.创建一个 string.strip()的替代函数:接受一个字符串,去掉它前面和后面的空格(如果使用 string.*strip()函数那本练习就没有意义了)** 307 | ```python 308 | #!/usr/bin/env python 309 | import string 310 | 311 | def my_string_strip(_str): 312 | if len(_str) <= 0: 313 | return _str 314 | 315 | # left strip 316 | while _str[0] == ' ': 317 | _str = _str[1:len(_str)] 318 | # right strip 319 | while _str[len(_str)-1] == ' ': 320 | _str = _str[0:len(_str)-1] 321 | 322 | return _str 323 | 324 | print my_string_strip(' asdf asdf ') 325 | ``` 326 | 327 | **6–7. 调试.看一下在例 6.5 中给出的代码(buggy.py) ** 328 | (a)研究这段代码并描述这段代码想做什么.在所有的(#)处都要填写你的注释. 329 | (b)这个程序有一个很大的问题,比如输入 6,12,20,30,等它会死掉,实际上它不能处理任何的偶数,找出原因. 330 | (c)修正(b)中提出的问题. 331 | (a)输入一个数字n,生成一个从1到n的数字列表,去掉列表中可以被n整除的数字。 332 | (b)在del fac_list[i]后,列表的长度减少了1 333 | (c)fix bug: 334 | ```python 335 | num_str = raw_input('Enter a number ') 336 | num_num = int(num_str) 337 | fac_list = range(1, num_num+1) 338 | print "BEFORE:", `fac_list` 339 | 340 | i = 0 341 | while i < len(fac_list): 342 | if num_num % fac_list[i] == 0: 343 | del fac_list[i] 344 | i -= 1 345 | i = i + 1 346 | 347 | print "AFTER:", `fac_list` 348 | ``` 349 | 350 | **6–8. 列表.给出一个整数值,返回代表该值的英文,比如输入 89 返回"eight-nine"。附加题: 能够返回符合英文语法规则的形式,比如输入“89”返回“eighty-nine”。本练习中的值限定在 0 到 1,000。** 351 | ```python 352 | #!usrbinenv python 353 | #!usrbinenv python 354 | class numtool(): 355 | zero_to_nine = ['zero','one','two','three','four','five','six','seven','eight','nine'] 356 | ten_to_nineteen = ['ten','eleven','twelve','thirdteen','fourteen','fifteen','sixteen','eighteen','nineteen'] 357 | twenty_to_ninety = ['twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety'] 358 | hundred = 'hundred' 359 | _and = ' and ' 360 | space = ' ' 361 | 362 | def num2en_0_to_100(self, num): 363 | if not 0 <= num < 100: 364 | return 365 | 366 | elif num < 10: 367 | return self.zero_to_nine[num] 368 | elif num < 20: 369 | return self.ten_to_nineteen[num-10] 370 | elif num < 100: 371 | if num % 10 == 0: 372 | return self.twenty_to_ninety[num/10 - 2] 373 | else: 374 | left = self.twenty_to_ninety[num/10 - 2] 375 | right = self.zero_to_nine[num % 10] 376 | return left + self.space + right 377 | 378 | def num2en(self, num): 379 | if not 0 <= num <= 1000: 380 | return 381 | 382 | if num < 100: 383 | return self.num2en_0_to_100(num) 384 | else: 385 | if num == 1000: 386 | return 'one thousand' 387 | elif num % 100 == 0: 388 | return self.zero_to_nine[num / 100] + self.space + self.hundred 389 | else: 390 | left = self.zero_to_nine[num / 100] 391 | right = self.num2en_0_to_100(num % 100) 392 | return left + self.space + self.hundred + self._and + right 393 | 394 | tool = numtool() 395 | while True: 396 | num = raw_input('input a number: ') 397 | print tool.num2en(int(num)) 398 | ``` 399 | 400 | **6–9. 转换.为练习 5-13 写一个姊妹函数, 接受分钟数, 返回小时数和分钟数. 总时间不变,并且要求小时数尽可能大. ** 401 | 402 | *略。* 403 | 404 | **6–10.字符串.写一个函数,返回一个跟输入字符串相似的字符串,要求字符串的大小写反转. 比如,输入"Mr.Ed",应该返回"mR.eD"作为输出.** 405 | ```python 406 | def reverse_up_low(ss): 407 | result = [] 408 | for s in ss: 409 | if 'a' <= s <= 'z': 410 | result.append(s.upper()) 411 | elif 'A' <= s <= 'Z': 412 | result.append(s.lower()) 413 | else: 414 | result.append(s) 415 | return ''.join(result) 416 | 417 | print reverse_up_low('Mr.Ed') 418 | ``` 419 | 420 | **6–11.转换 421 | (a)创建一个从整数到 IP 地址的转换程序,如下格式: `WWW.XXX.YYY.ZZZ` 422 | (b)更新你的程序,使之可以逆转换.** 423 | 424 | *略。* 425 | 426 | **6–12.字符串 ** 427 | (a)创建一个名字为 findchr()的函数,函数声明如下: def findchr(string, char) findchr()要在字符串 string 中查找字符 char,找到就返回该值的索引,否则返回-1.不能用 string.*find()或者 string.*index()函数和方法 428 | (b)创建另一个叫 rfindchr()的函数,查找字符 char 最后一次出现的位置.它跟 findchr()工作类似,不过它是从字符串的最后开始向前查找的. 429 | (c)创建第三个函数,名字叫 subchr(),声明如下: def subchr(string, origchar, newchar) subchr()跟 findchr()类似,不同的是,如果找到匹配的字符就用新的字符替换原先字符.返回修改后的字符串. 430 | 431 | *略。* 432 | 433 | **6–13.字符串.string 模块包含三个函数,atoi(),atol(),和 atof(),它们分别负责把字符串转换成整数,长整型,和浮点型数字.从 Python1.5 起,Python 的内建函数 int(),long(),float()也可以做相同的事了, complex()函数可以把字符串转换成复数.(然而 1,5 之前,这些转换函数只能工作于数字之上).string 模块中并没有实现一个 atoc()函数,那么你来实现一个atoc(),接受单个字符串做参数输入,一个表示复数的字符串,例如,'-1.23e+4-5.67j',返回相应的复数对象.你不能用 eval()函数,但可以使用 complex()函数,而且你只能在如下的限制之下使用 complex():complex(real,imag)** 434 | 435 | *略。* 436 | 437 | **6–14.随机数.设计一个"石头,剪子,布"游戏,有时又叫"Rochambeau",你小时候可能玩过,下面是规则.你和你的对手,在同一时间做出特定的手势,必须是下面一种手势:石头,剪子,布.胜利者从下面的规则中产生,这个规则本身是个悖论.** 438 | (a)布包石头. (b)石头砸剪子 (c)剪子剪破布. 439 | 在你的计算机版本中,用户输入她/他的选项,计算机找一个随机选项,然后由你的程序来决定一个胜利者或者平手.注意:最好的算法是尽量少的使用 if 语句. 440 | 441 | ```python 442 | #!usrbinenv python 443 | import random 444 | 445 | def rps(arg1, arg2): 446 | # ['rock','paper','scissors'] 447 | rule = {'rp':-1,'rs':1,'rr':0, 'pr':1,'ps':-1,'pp':0, 'sr':-1,'sp':1,'ss':0} 448 | return rule[arg1[0] + arg2[0]] 449 | 450 | user_value = raw_input('your choose: [rock|paper|scissors] ') 451 | app_value = ['rock','paper','scissors'][random.randint(0,2)] 452 | print 'app chooses ' + app_value 453 | 454 | res = rps(user_value, app_value) 455 | if res == 1: 456 | print 'user win' 457 | elif res == 0: 458 | print 'equals' 459 | else: 460 | print 'application win' 461 | ``` 462 | 463 | **6–15.转换 (a)给出两个可识别格式的日期,比如 MM/DD/YY 或者 DD/MM/YY 格式,计算出两个日期间的天数. (b)给出一个人的生日,计算从此人出生到现在的天数,包括所有的闰月. (c)还是上面的例子,计算出到此人下次过生日还有多少天.** 464 | 465 | *略。* 466 | 467 | **6–16.矩阵.处理矩阵 M 和 N 的加和乘操作.** 468 | 469 | *略。* 470 | 471 | **6–17.方法.实现一个叫 myPop()的函数,功能类似于列表的 pop()方法,用一个列表作为输入, 移除列表的最新一个元素,并返回它.** 472 | 473 | *略。* 474 | 475 | **6–18. zip() 内建函数 在 6.13.2 节里面关于 zip()函数的例子中,zip(fn,ln)返回的是什么?** 476 | `[('ian', 'bairnson'), ('stuart', 'elliott'), ('david', 'paton')]` 477 | 478 | **6–19.多列输出.有任意项的序列或者其他容器,把它们等距离分列显示.由调用者提供数据和输出格式.例如,如果你传入 100 个项并定义 3 列输出,按照需要的模式显示这些数据.这种情况下,应该是两列显示 33 个项,最后一列显示 34 个.你可以让用户来选择水平排序或者垂直排序.** 479 | 480 | *略。* 481 | -------------------------------------------------------------------------------- /06_Sequences-Strings, Lists, and Tuples/unicode.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | -------------------------------------------------------------------------------- /07_Mapping and Set Types/7-1 How dictionaries are compared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/07_Mapping and Set Types/7-1 How dictionaries are compared.png -------------------------------------------------------------------------------- /07_Mapping and Set Types/7-15-sets-calculator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def issubset(set1, set2): 4 | return set1.issubset(set2) 5 | def issuperset(set1, set2): 6 | return set1.issuperset(set2) 7 | def union(set1, set2): 8 | return set1 | set2 9 | def intersection(set1, set2): 10 | return set1 & set2 11 | def difference(set1, set2): 12 | return set1 - set2 13 | def symmetric_difference(set1, set2): 14 | return set1 ^ set2 15 | 16 | set1 = set(['aa',123,'bb']) 17 | set2 = set(['aa',456]) 18 | 19 | # ^, <, <=, >, >=, ==, != 20 | ops = {'sub':issubset, 'super':issuperset, '|':union, '&':intersection, '-':difference, '^':symmetric_difference} 21 | 22 | for op in ops: 23 | print op, ops[op](set1, set2) 24 | -------------------------------------------------------------------------------- /07_Mapping and Set Types/note07_Mapping and Set Types.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍由多个键值对组成的字典和元素不可重复的集合。 3 | 4 | # 知识点 5 | ## 7.1 字典 6 | 字典中的key必须是可哈希的(选择可哈希的key是因为它们的值不能改变),数字和字符串可以作为字典中的key, 但是列表和其他字典不行。(使用`hash()`计算可哈希对象的hash值) 7 | 8 | **创建字典的三种方法:** 9 | 10 | - `dict1 = dict(x=1, y=2)` # {'y': 2, 'x': 1} 11 | - `dict1 = dict((['x',1],['y',2]))` # {'y': 2, 'x': 1} 12 | - `dict1 = {}.fromkeys(('x','y'), -1)` # {'y': -1, 'x': -1} 13 | 14 | **字典的常用操作:** 15 | 16 | - `dict2.keys()` 17 | - `for key in dict2:` # 默认遍历的是keys 18 | - `if key1 in dict1:` # dict1[key1]取值前先判断key是否存在,不存在会会抛异常 19 | - `print 'host %(name)s is running on port %(port)d' %dict2` 20 | - `dict2.pop('name')` 21 | 22 | 23 | ## 7.2 字典的比较算法 24 | 25 | 1. 比较字典长度:key越多,dict越大 26 | 2. 比较key:key比较的顺序和 keys()方法返回key的顺序相同。 27 | 3. 比较value:若key相同,则比较对应的value 28 | 4. 长度、keys、values都相等,完全匹配返回0 29 | 30 | ![](./7-1 How dictionaries are compared.png "How dictionaries are compared") 31 | 32 | ## 7.3 字典的浅复制 33 | 34 | - dict2 = dict(**dict1) 35 | - `dict2 = dict1.copy()` 36 | 37 | ## 7.4 字典的内建方法 38 | 39 | - `dict1.update(dict2)` # 将字典 dict2 的键-值对添加到字典 dict1 40 | - `sorted(dict1)` # 返回排序后的字典的keys列表 41 | - `dict1.get('x',123)` # 获取key为x的value,如果key不存在返回默认值123(不是抛异常),如果存在则返回value 42 | - `dict1.setdefault('b',444)` # 若字典不存在则添加一对新的key-value,否则覆盖key的旧value为新的value 43 | 44 | ## 7.5 集合类型 45 | 集合的**元素不可重复**,分为两种类型:可变集合(可更新元素)和不可变集合。 46 | 47 | ### 7.5.1 可变集合 48 | - `set('aaabcd')` # `set(['a', 'c', 'b', 'd'])` 49 | - `a.add('z')` 50 | - `a.update('xyz')` 51 | - `a.remove('z')` 52 | - `a -= set('abc')` 53 | 54 | ### 7.5.2 不可变集合 55 | `frozenset('hijk')` # `frozenset(['i', 'h', 'k', 'j'])` 56 | 57 | ## 7.6 集合类型操作符 58 | **注意:** 集合的操作结果类型与左操作数的类型相同。 59 | 60 | ## 7.6.1 适用于两种集合类型的操作符 61 | - 子集/超集:“小于”符号( <, <= ) 用来判断子集,“大于”符号( >, >= )用来判断超集。 62 | - 并集( | ):`a | b` # **a+b是错误的**,加号不是集合类型的运算符! 63 | - 交集( & ):`a & b` # 相当于`a.intersection(b)` 64 | - 差集( – ):`a - b` # 相当于`a.difference(b)` 65 | - 对称差分( ^ ):`a ^ b` # 异或,相当于`(a-b)|(b-a)`或`s.symmetric_difference(t)` 66 | 67 | ## 7.6.2 只适用于可变集合类型的操作符 68 | 69 | - 交集更新( &= ):`s &= set('pypi')` # 相当于`s.intersection_update(t)` 70 | - 差集更新( –= ):`s -= set('pypi')` # 相当于`s.difference_update(t)` 71 | - 对称差分更新( ^= ):`s ^= set('pypi')` # 相当于`s.symmetric_difference_update(t)` 72 | 73 | ## 7.7 集合的内建方法 74 | 75 | - `s.issubset(t)` 如果 s 是 t 的子集,则返回 True,否则返回 False 76 | - `s.issuperset(t)` 如果 s 是 t 的超集,则返回 True,否则返回 False 77 | - **`s.remove(obj)`** 从集合 s 中删除对象 obj;如果 obj 不是集合 s 中的元素(obj not in s),将引发 KeyError 错误 78 | - **`s.discard(obj)`** 如果 obj 是集合 s 中的元素,从集合 s 中删除对象 obj; 79 | - `s.clear()` 删除集合 s 中的所有元素 80 | 81 | # 练习 82 | **7–1. 字典方法。哪个字典方法可以用来把两个字典合并到一起?** 83 | `dict1.update(dict2)` 84 | 85 | **7–2. 字典的键。我们知道字典的值可以是任意的 Python 对象,那字典的键又如何呢?请试着将除数字和字符串以外的其他不同类型的对象作为字典的键,看一看,哪些类型可以,哪些不行? 对那些不能作字典的键的对象类型,你认为是什么原因呢?** 86 | 87 | `frozenset()`可以作为字典的键。 88 | 那些不能作字典的键的对象类型,是因为其值不可hash. 89 | 90 | **7–3. 字典和列表的方法。 91 | (a) 创建一个字典,并把这个字典中的键按照字母顺序显示出来。 92 | (b) 现在根据已按照字母顺序排序好的键,显示出这个字典中的键和值。 93 | (c)同(b),但这次是根据已按照字母顺序排序好的字典的值,显示出这个字典中的键和值。** 94 | 95 | 思路:遍历keys或values,用内建函数`sorted()`排好序,再根据key/value取对应的value/key。 96 | 97 | **7-4. 建立字典。给定两个长度相同的列表,比如说,列表[1, 2, 3,...]和['abc', 'def', 'ghi',...],用这两个列表里的所有数据组成一个字典,像这样: {1:'abc', 2: 'def', 3: 'ghi',...}** 98 | ``` 99 | >>> dict1 = [1,2,3] 100 | >>> dict2 = ['abc','def','ghi'] 101 | >>> zips = zip(dict1, dict2) 102 | >>> zips 103 | [(1, 'abc'), (2, 'def'), (3, 'ghi')] 104 | >>> dict(zips) 105 | {1: 'abc', 2: 'def', 3: 'ghi'} 106 | ``` 107 | 108 | **7-7. 颠倒字典中的键和值。用一个字典做输入,输出另一个字典,用前者的键做值,前者的值做键。** 109 | 参考7-4,调换`zip()`的参数位置。 110 | 111 | **7-8. 人力资源。创建一个简单的雇员姓名和编号的程序。让用户输入一组雇员姓名和编号。 你的程序可以提供按照姓名排序输出的功能,雇员姓名显示在前面,后面是对应的雇员编号。附加 题:添加一项功能,按照雇员编号的顺序输出数据。** 112 | 参考7-3。 113 | 114 | **7–11. 定义。什么组成字典中合法的键? 举例说明字典中合法的键和非法的键。** 115 | 可以计算出哈希值的数据类型组成字典的合法的键,如:数字、字符串、不可变集合。其他数据类型为法的键。 116 | 117 | **7-12. 定义。** 118 | **(a)在数学上,什么是集合?** 119 | 由一个或多个元素所构成的叫做集合。 120 | 121 | **(b)在 Python 中,关于集合类型的定义是什么?** 122 | 由不同元素组成的组合。 123 | 124 | **7–15. 编写计算器。 编写一个程序允许用户选择两个集合:A 和 B, 及运算操作符。例如,in, not in, &, |, ^, <, <=, >, >=, ==, !=, 等. (你自己定义集合的输入语法,它们并不一定要像 Java 示例中那样用方括 号括住。)解析输入的字符串,按照用户选择的运算进行操作。你写的程序代码应该比 Java 版本的 该程序更简洁。** 125 | ```python 126 | #!/usr/bin/env python 127 | 128 | def issubset(set1, set2): 129 | return set1.issubset(set2) 130 | def issuperset(set1, set2): 131 | return set1.issuperset(set2) 132 | def union(set1, set2): 133 | return set1 | set2 134 | def intersection(set1, set2): 135 | return set1 & set2 136 | def difference(set1, set2): 137 | return set1 - set2 138 | def symmetric_difference(set1, set2): 139 | return set1 ^ set2 140 | 141 | set1 = set(['aa',123,'bb']) 142 | set2 = set(['aa',456]) 143 | 144 | # ^, <, <=, >, >=, ==, != 145 | ops = {'sub':issubset, 'super':issuperset, '|':union, '&':intersection, '-':difference, '^':symmetric_difference} 146 | 147 | for op in ops: 148 | print op, ops[op](set1, set2) 149 | ``` 150 | -------------------------------------------------------------------------------- /07_Mapping and Set Types/userpw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | db = {} 4 | 5 | def newuser(): 6 | prompt = 'login desired: ' 7 | while True: 8 | name = raw_input(prompt) 9 | if db.has_key(name): 10 | prompt = 'name taken, try another: ' 11 | continue 12 | else: 13 | break 14 | pwd = raw_input('passwd: ') 15 | db[name] = pwd 16 | 17 | def olduser(): 18 | name = raw_input('login: ') 19 | pwd = raw_input('passwd: ') 20 | passwd = db.get(name) 21 | if passwd == pwd: 22 | print 'welcome back', name 23 | else: 24 | print 'login incorrect' 25 | 26 | def showmenu(): 27 | prompt = """ 28 | (N)ew User Login 29 | (E)xisting User Login 30 | (Q)uit 31 | 32 | Enter choice: """ 33 | 34 | done = False 35 | while not done: 36 | 37 | chosen = False 38 | while not chosen: 39 | try: 40 | choice = raw_input(prompt).strip()[0].lower() 41 | except (EOFError, KeyboardInterrupt): 42 | choice = 'q' 43 | print '\nYou picked: [%s]' % choice 44 | if choice not in 'neq': 45 | print 'invalid option, try again' 46 | else: 47 | chosen = True 48 | 49 | if choice == 'q': done = True 50 | if choice == 'n': newuser() 51 | if choice == 'e': olduser() 52 | 53 | if __name__ == '__main__': 54 | showmenu() 55 | -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-10-nametrack.py: -------------------------------------------------------------------------------- 1 | num = int(raw_input('Enter total number of names: ')) 2 | i = 0 3 | error_count = 0 4 | names = [] 5 | while i < num: 6 | name = raw_input('Please enter name %d: ' % i) 7 | if ', ' not in name: 8 | print '>> Wrong format... should be Last, First.' 9 | error_count += 1 10 | print '>> You have done this %d time(s) already. Fixing input. . .' % error_count 11 | nameTmp = name.split() 12 | name = nameTmp[0] + ', ' + nameTmp[1] 13 | names.append(name) 14 | i += 1 15 | else: 16 | print 'The sorted list (by last name) is:' 17 | names.sort() 18 | for name in names: 19 | print '\t%s' % name -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-4-isprime.py: -------------------------------------------------------------------------------- 1 | def isprime(num): 2 | if num < 2: 3 | return False 4 | elif num == 2: 5 | return True 6 | 7 | n = int(num ** 0.5) 8 | while n > 1: 9 | if num % n == 0: 10 | return False 11 | else: 12 | n -= 1 13 | else: 14 | return True 15 | 16 | while True: 17 | num = int(raw_input('input a number: ')) 18 | print isprime(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-5-getfactors.py: -------------------------------------------------------------------------------- 1 | def getfactors(num): 2 | if num <= 0: 3 | return [] 4 | 5 | result = set([]) 6 | n = int(num ** 0.5) 7 | while n > 0: 8 | tmp = divmod(num, n) 9 | if tmp[1] == 0: 10 | result.add(n) 11 | result.add(tmp[0]) 12 | n -= 1 13 | 14 | return sorted([x for x in result]) 15 | 16 | while True: 17 | num = int(raw_input('input a number: ')) 18 | print getfactors(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-6-prime-split.py: -------------------------------------------------------------------------------- 1 | def isprime(num): 2 | if num < 2: 3 | return False 4 | elif num == 2: 5 | return True 6 | 7 | n = int(num ** 0.5) 8 | while n > 1: 9 | if num % n == 0: 10 | return False 11 | else: 12 | n -= 1 13 | else: 14 | return True 15 | 16 | def getfactors(num): 17 | if num <= 0: 18 | return [] 19 | 20 | result = set([]) 21 | n = int(num ** 0.5) 22 | while n > 0: 23 | tmp = divmod(num, n) 24 | if tmp[1] == 0: 25 | result.add(n) 26 | result.add(tmp[0]) 27 | n -= 1 28 | 29 | return sorted([x for x in result]) 30 | 31 | def prime_split(num): 32 | if num <= 1: 33 | return [] 34 | if isprime(num): 35 | return [num] 36 | 37 | factors = getfactors(num) 38 | if len(factors) > 2: 39 | result = [] 40 | maxF = factors[-2] 41 | tmp = divmod(num, maxF) 42 | 43 | result += prime_split(maxF) 44 | result += prime_split(tmp[0]) 45 | 46 | return sorted(result) 47 | else: 48 | return [] 49 | 50 | while True: 51 | num = int(raw_input('input a number: ')) 52 | print prime_split(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-7-isperfect.py: -------------------------------------------------------------------------------- 1 | def isprime(num): 2 | if num < 2: 3 | return False 4 | elif num == 2: 5 | return True 6 | 7 | n = int(num ** 0.5) 8 | while n > 1: 9 | if num % n == 0: 10 | return False 11 | else: 12 | n -= 1 13 | else: 14 | return True 15 | 16 | def prime_split(num): 17 | if num <= 0: 18 | return [] 19 | 20 | result = [] 21 | n = int(num ** 0.5) 22 | 23 | while n > 1: 24 | tmp = divmod(num, n) 25 | if tmp[1] == 0: 26 | if isprime(n): 27 | result.append(n) 28 | else: 29 | result += prime_split(n) 30 | 31 | if isprime(tmp[0]): 32 | result.append(tmp[0]) 33 | else: 34 | result += prime_split(tmp[0]) 35 | 36 | break 37 | else: 38 | n -= 1 39 | return sorted(result) 40 | 41 | def isperfect(num): 42 | factors = prime_split(num) 43 | factors.insert(0, 1) 44 | return sum(factors) == num 45 | 46 | while True: 47 | num = int(raw_input('input a number: ')) 48 | print isperfect(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-8-N!.py: -------------------------------------------------------------------------------- 1 | def factorial(N): # N! = 1*2*3...*N 2 | if N <= 0: 3 | return None 4 | return reduce(lambda x,y:x*y, range(1,N+1)) 5 | 6 | while True: 7 | num = int(raw_input('input a number: ')) 8 | print factorial(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/8-9-Fibonacci.py: -------------------------------------------------------------------------------- 1 | def Fibonacci(N): 2 | if N <= 2: 3 | return 1 4 | tmp = [1,1] 5 | for x in range(2,N): 6 | tmp.append(tmp[-2]+tmp[-1]) 7 | 8 | return tmp[-1] 9 | 10 | while True: 11 | num = int(raw_input('input a number: ')) 12 | print Fibonacci(num) -------------------------------------------------------------------------------- /08_Conditionals and Loops/check-user-passwd-3-times.py: -------------------------------------------------------------------------------- 1 | passwdList = ['aaaa', 'bbb', 'cccc'] 2 | valid = False 3 | count = 3 4 | while count > 0: 5 | input = raw_input("enter password: ") 6 | # check for valid passwd 7 | for eachPasswd in passwdList: 8 | if input == eachPasswd: 9 | valid = True 10 | break 11 | if not valid: # (or valid == 0) 12 | print ">>ERROR: invalid input" 13 | count -= 1 14 | continue 15 | else: 16 | print ">>INFP: password correct!" 17 | break 18 | -------------------------------------------------------------------------------- /08_Conditionals and Loops/note08_Conditionals and Loops.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍条件和循环语句的用法、迭代器、列表解析和生成器表达式。 3 | 4 | # 知识点 5 | ## 8.1 if-elif-else 6 | switch-case的替代方案: 7 | 8 | - 使用字典替代让代码更优雅。 9 | - 使用if-elif-else替代 10 | 11 | 使用映射对象(比如字典)的一个最大好处就是它的搜索操作比类似 if-elif-else 语句或是 for 循环这样的序列查询要快很多. 12 | 13 | ## 8.2 条件表达式 14 | 15 | - 表达式:`X if C else Y` 16 | - 旧的写法:`smaller = (x < y and [x] or [y])[0]` 17 | - 新的写法:`smaller = x if x < y else y` 18 | 19 | ## 8.3 for循环 20 | 在列表解析和生成器表达式中, 它会自动地调用迭代器的 `next()` 方法, 捕获 `StopIteration` 异常并结束循环(所有这一切都是在内部发生的). 21 | 22 | ### 8.3.1 迭代器 23 | 迭代器对象有一个 `next()` 方法, 调用后返回下一个条目. 所有条目迭代完后, 迭代器引发一 个 `StopIteration` 异常告诉程序循环结束. for 语句在内部调用` next() `并捕获异常 24 | 25 | ### 8.3.2 xrange() 26 | 27 | - 当范围列表很大时, `xrange()` 比 `range()` 更为适合 28 | - `xrange()`不会在内存里创建列表的完整拷贝. 它只被用在for循环中, 在for循环外使用它没有意义。 29 | - 它的性能远高出`range()`, 因为它不生成整个列表, 而是返回一个可迭代对象(不是列表也不是一个迭代器) 30 | 31 | ### 8.3.3 与序列相关的内建函数 32 | - `sorted()` 和 `zip()`:返回一个序列 33 | - `reversed()` 和 `enumerate()`:返回迭代器 34 | 35 | ## 8.4 pass语句 36 | `pass` 语句表示不做任何事情,即 NOP(No OPeration) 。从汇编语言中借用这个概念,可以用来标记未来要完成的代码。 37 | 38 | ## 8.5 else语句 39 | 在 while 和 for 循环中使用 else 语句时, 只要 for 循环是正常结束的(不是通过 break ), else 子句就会执行 40 | 41 | ## 8.6 迭代器和 iter() 函数 42 | 43 | 用for循环对字典进行迭代,遍历的是keys 44 | 用for循环对文件进行迭代,遍历的是每一行数据。 45 | 46 | ```python 47 | >>> s = 'abc' 48 | >>> fetch = iter(s) 49 | >>> fetch 50 | 51 | >>> while True: 52 | ... try: 53 | ... i = fetch.next() 54 | ... except StopIteration: 55 | ... break 56 | ... print i 57 | ``` 58 | **注意:** 59 | 在迭代序列的时候删除元素,会立即反映到所迭代的item上(序号发生变化),所以最好不要这样操作——因为:迭代器是与实际对象绑定在一起的。 60 | 61 | ## 8.7 列表解析 62 | 63 | - `[x**2 for x in range(6)]` 64 | - 过滤出被2整除不为0的元素:`filter(lambda x: x % 2, seq)` 或 `[x for x in seq if x % 2]` 65 | - 3行5列元组:`[(x+1, y+1) for x in range(3) for y in range(5)]` 66 | - 计算一个文本文件总的非空白字符数:`sum([len(word) for line in f for word in line.split()])` 67 | - `map(lambda x:x+x,range(5))` #lambda 函数,各项+本身 68 | - `reduce(lambda x,y:x+y,range(1,3),5)` #lambda 函数,5是初始值, 5+(1+2) 69 | 70 | **列表解析的性能缺陷**:必须生成所有的数据用以创建整个列表,当数据量很大时影响迭代效率。 71 | 72 | ## 8.8 生成器表达式 73 | **生成器**:和列表解析非常相似,但并不真正创建列表, 而是返回一个生成器,这个生成器在每次计算出一个item后,就把这个item产出(yield)。生成器表达式使用了"延迟计算"(lazy evaluation), 所以它在使用内存上更有效。 74 | 75 | 76 | e.g.1 计算一个文本文件的非空白字符总数: 77 | `sum(len(word) for line in open('data.txt') for word in line.split())` 78 | 79 | 80 | e.g.2 计算一个大文本文件的最长行的字符数: 81 | `max(len(line) for line in open('data.txt'))` 82 | 83 | 84 | e.g.3 交叉配对例子: 85 | ```python 86 | rows = [1, 2, 3, 17] 87 | def cols(): # example of simple generator 88 | yield 56 89 | yield 2 90 | yield 1 91 | 92 | x_product_pairs = ((i, j) for i in rows for j in cols()) 93 | for pair in x_product_pairs: 94 | print pair 95 | ``` 96 | 97 | 98 | # 练习 99 | ** 8.1 ** 100 | 101 | (a)CE (b)DE (c)BE 102 | 103 | ** 8.2 ** 104 | 105 | *略* 106 | 107 | ** 8.3 ** 108 | 109 | (a) `range(10)` 110 | (b) `range(3, 19, 3)` 111 | (c) `range(-20, 861, 220)` 112 | 113 | **8–4. 素数. 我们在本章已经给出了一些代码来确定一个数字的最大约数或者它是否是一个素数. 请把相关代码转换为一个返回值为布尔值的函数,函数名为 isprime() . 如果输入的是一个素数, 那么返回 True , 否则返回 False。** 114 | ```python 115 | def isprime(num): 116 | if num < 2: 117 | return False 118 | elif num == 2: 119 | return True 120 | 121 | n = int(num ** 0.5) 122 | while n > 1: 123 | if num % n == 0: 124 | return False 125 | else: 126 | n -= 1 127 | else: 128 | return True 129 | ``` 130 | 131 | **8–5. 约数. 完成一个名为 getfactors() 的函数. 它接受一个整数作为参数, 返回它所有约数的列表, 包括 1 和它本身。** 132 | ```python 133 | def getfactors(num): 134 | if num <= 0: 135 | return [] 136 | 137 | result = set([]) 138 | n = int(num ** 0.5) 139 | while n > 0: 140 | tmp = divmod(num, n) 141 | if tmp[1] == 0: 142 | result.add(n) 143 | result.add(tmp[0]) 144 | n -= 1 145 | 146 | return sorted([x for x in result]) 147 | ``` 148 | 149 | **8–6. 素因子分解. 以刚才练习中的 isprime() 和 getfactors() 函数为基础编写一个函 数, 它接受一个整数作为参数, 返回该整数所有素数因子的列表. 这个过程叫做求素因子分解, 它输出的所有因子之积应该是原来的数字. 注意列表里可能有重复的元素. 例如输入 20 , 返回结果应该是 [2, 2, 5] .** 150 | ```python 151 | def prime_split(num): 152 | if num <= 1: 153 | return [] 154 | if isprime(num): 155 | return [num] 156 | 157 | factors = getfactors(num) 158 | if len(factors) > 2: 159 | result = [] 160 | maxF = factors[-2] 161 | tmp = divmod(num, maxF) 162 | result += prime_split(maxF) 163 | result += prime_split(tmp[0]) 164 | return sorted(result) 165 | else: 166 | return [] 167 | ``` 168 | 169 | **8–7. 全数. 完全数被定义为这样的数字: 它的约数(不包括它自己)之和为它本身. 例如: 6 的约数是 1, 2, 3, 因为 1 + 2 + 3 = 6 , 所以 6 被认为是一个完全数. 编写一个名为 isperfect()的函数, 它接受一个整数作为参数, 如果这个数字是完全数, 返回 1 ; 否则返回 0 .** 170 | ```python 171 | def isperfect(num): 172 | factors = prime_split(num) 173 | factors.insert(0, 1) 174 | return sum(factors) == num 175 | ``` 176 | 177 | **8–8. 阶乘. 一个数的阶乘被定义为从 1 到该数字所有数字的乘积. N 的阶乘简写为 N! . 写一个函数, 指定 N, 返回 N! 的值.** 178 | ```python 179 | def factorial(N): # N! = 1*2*3...*N 180 | if N <= 0: 181 | return None 182 | return reduce(lambda x,y:x*y, range(1,N+1)) 183 | ``` 184 | 185 | **8–9. Fibonacci 数列. Fibonacci 数列形如 1, 1, 2, 3, 5, 8, 13, 21, 等等. 也就是说, 下一个值是序列中前两个值之和. 写一个函数, 给定 N , 返回第 N 个 Fibonacci 数字. 例如, 第 1 个 Fibonacci 数字是 1 , 第 6 个是 8 .** 186 | ```python 187 | def Fibonacci(N): 188 | if N <= 2: 189 | return 1 190 | tmp = [1,1] 191 | for x in range(2,N): 192 | tmp.append(tmp[-2]+tmp[-1]) 193 | return tmp[-1] 194 | ``` 195 | 196 | **8–10. 文本处理. 统计一句话中的元音, 辅音以及单词(以空格分割)的个数. 忽略元音和辅音的特殊情况, 如 "h", "y", "qu" 等. 附加题: 编写处理这些特殊情况的代码.** 197 | 198 | *略。* 199 | 200 | **8–11. 文本处理. 要求输入一个姓名列表,输入格式是“Last Name, First Name,” 即 姓, 逗号, 名. 编写程序处理输入, 如果用户输入错误, 比如“ First Name Last Name,” , 请纠正这 些错误, 并通知用户. 同时你还需要记录输入错误次数. 当用户输入结束后, 给列表排序, 然后以 "姓 , 名" 的顺序显示.** 201 | ```python 202 | num = int(raw_input('Enter total number of names: ')) 203 | i = 0 204 | error_count = 0 205 | names = [] 206 | while i < num: 207 | name = raw_input('Please enter name %d: ' % i) 208 | if ', ' not in name: 209 | print '>> Wrong format... should be Last, First.' 210 | error_count += 1 211 | print '>> You have done this %d time(s) already. Fixing input. . .' % error_count 212 | nameTmp = name.split() 213 | name = nameTmp[0] + ', ' + nameTmp[1] 214 | names.append(name) 215 | i += 1 216 | else: 217 | print 'The sorted list (by last name) is:' 218 | names.sort() 219 | for name in names: 220 | print '\t%s' % name 221 | ``` 222 | 223 | **8-12** 224 | 225 | *略。* 226 | 227 | **8–13. 程序执行性能. 在 8.5.2 节里, 我们介绍了两种基本的迭代序列方法:** 228 | **(1) 通过序列项 (2) 通过序列索引遍历. 该小节的末尾我们指出后一种方法在序列很长的时候性能不佳. (在我的系统下, 性能差了将近两倍[83%]) 你认为它的原因是什么?** 229 | 230 | 后者通过序列索引遍历时,在序列很长的时候`len()`方法计算序列的大小时会比较耗时,导致性能不佳。 231 | -------------------------------------------------------------------------------- /09_File and Input Output/9-1-filter.py: -------------------------------------------------------------------------------- 1 | f = open('test.txt') 2 | for line in f: 3 | if line is not None and line[0] != '#': 4 | print line, 5 | 6 | -------------------------------------------------------------------------------- /09_File and Input Output/9-11_favourite_URL.py: -------------------------------------------------------------------------------- 1 | import re, os 2 | 3 | def checkurl(url): 4 | regex = re.compile( 5 | r'^(?:http|ftp)s?://' # http:// or https:// 6 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... 7 | r'localhost|' # localhost... 8 | r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip 9 | r'(?::\d+)?' # optional port 10 | r'(?:/?|[/?]\S+)$', re.IGNORECASE) 11 | if regex.match(url): 12 | return True 13 | else: 14 | return False 15 | 16 | def geturl(): 17 | name = raw_input('pls input a url name:') 18 | while 1: 19 | url = raw_input('pls input a url address:') 20 | if checkurl(url): 21 | break 22 | else: 23 | print 'wrong url format,pls input again' 24 | mark = raw_input('pls input a url mark:') 25 | folder = raw_input('pls input a url folder:') 26 | return (name, url, mark, folder) 27 | 28 | def load(filename): 29 | f = open(filename, 'a+') 30 | bmlist = f.readlines() 31 | f.close() 32 | return bmlist 33 | 34 | def save(bmlist, filename): 35 | f = open(filename, 'w+') 36 | for line in bmlist: 37 | if len(line) == 0: 38 | continue 39 | f.write(line) 40 | f.close() 41 | 42 | def add(bmlist, name, url, mark, folder='default'): 43 | bookmark = name + ';' + url + ';' + mark + ';' + folder + os.linesep 44 | if bookmark not in bmlist: 45 | bmlist.append(bookmark) 46 | 47 | def modify(bmlist, index, name, url, mark, folder): 48 | bmlist[index] = name + ';' + url + ';' + mark + ';' + folder + os.linesep 49 | 50 | def delbm(bmlist, index): 51 | bmlist.pop(index) 52 | 53 | def findbk(bmlist, fname, furl): 54 | for i, item in enumerate(bmlist): 55 | (name, url, mark, folder) = item.split(';') 56 | if fname and furl: 57 | if (fname in name) and (furl in url): 58 | return i 59 | if fname and (fname in name): 60 | return i 61 | if furl and (furl in url): 62 | return i 63 | else: 64 | return -1 65 | 66 | def output2html(bmlist): 67 | for i, item in enumerate(bmlist): 68 | (name, url, mark, folder) = item.split(';') 69 | os.mkdir(folder.strip()) 70 | filename = name.strip() + '.html' 71 | f = open(filename, 'w+') 72 | fmt = '%d\t%s\t%s\t%s\t%s
' 73 | f.write('bookmark') 74 | content = fmt % (i + 1, name, r'http:\\' + url, url, mark, folder) 75 | f.write(content) 76 | f.write('') 77 | f.close() 78 | os.rename(filename, folder.strip() + os.sep + filename) 79 | 80 | def show_menu(): 81 | bmlist = load(r'url.txt') 82 | while True: 83 | print '0. quit' 84 | print '1. add a url bookmark' 85 | print '2. modify a url bookmark' 86 | print '3. delete a url bookmark' 87 | print '4. find a url bookmark' 88 | print '5. output url bookmark as html' 89 | print '\n' 90 | 91 | iInput = input("please input operation num: ") 92 | 93 | if iInput < 0 or iInput > 5: 94 | print 'Error input operation, try again.\n' 95 | continue 96 | elif 0 == iInput: 97 | save(bmlist, r'url.txt') 98 | break 99 | elif 1 == iInput: 100 | data = geturl() 101 | add(bmlist, *data) 102 | elif 2 == iInput: 103 | index = int(raw_input('bookmark index:')) 104 | data = geturl() 105 | modify(bmlist, index, *data) 106 | elif 3 == iInput: 107 | index = int(raw_input('bookmark index:')) 108 | delbm(bmlist, index) 109 | elif 4 == iInput: 110 | name = raw_input('url name:') 111 | url = raw_input('url address:') 112 | index = findbk(bmlist, name, url) 113 | if index == -1: 114 | print 'not found' 115 | else: 116 | print bmlist[index] 117 | elif 5 == iInput: 118 | output2html(bmlist) 119 | 120 | if __name__ == '__main__': 121 | show_menu() 122 | 123 | 124 | -------------------------------------------------------------------------------- /09_File and Input Output/9-12_sysadmin_pickle_shelve.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import hashlib, os 3 | import pickle as p 4 | import shelve as s 5 | 6 | db = {} 7 | 8 | def newuser(): 9 | value = [] 10 | prompt = 'login name desired again: ' 11 | while True: 12 | name = raw_input(prompt).lower() 13 | if not name.isalnum() and '' in name: 14 | print 'name format error' 15 | continue 16 | else: 17 | if db.has_key(name): 18 | prompt = 'name taken,try another: ' 19 | continue 20 | else: 21 | break 22 | pwd = raw_input('login passwd desired: ') 23 | m = hashlib.md5() 24 | m.update(pwd) 25 | value.append(m.hexdigest()) 26 | value.append(datetime.now()) 27 | db[name] = value 28 | print 'new user is %s, register time is %s' % (name, db[name][1]) 29 | 30 | 31 | def olduser(): 32 | name = raw_input('login name desired again: ').lower() 33 | pwd = raw_input('login passwd desired: ') 34 | m = hashlib.md5() 35 | m.update(pwd) 36 | passwd = db.get(name) 37 | if passwd[0] == m.hexdigest(): 38 | newtime = datetime.now() 39 | if (newtime - db[name][1]).days == 0 and (newtime - db[name][1]).seconds < 14400: 40 | print 'you already logged in at %s: ' % (db[name][1]) 41 | else: 42 | passwd[1] = newtime 43 | print 'welcome back %s, login time is %s' % (name, passwd[1]) 44 | 45 | else: 46 | print 'login incorrect' 47 | 48 | 49 | def removeuser(): 50 | print db 51 | name = raw_input('input a user name to remove: ').lower() 52 | if name in db: 53 | db.pop(name) 54 | else: 55 | print 'input error' 56 | 57 | 58 | def userlogin(): 59 | while True: 60 | name = raw_input('login name desired: ').lower() 61 | if not name.isalnum() and '' in name: 62 | print 'name format error' 63 | continue 64 | else: 65 | if not db.has_key(name): 66 | print 'user name is not in db' 67 | answer = raw_input('register a new user? y/n').lower() 68 | if 'y' == answer: 69 | newuser() 70 | break 71 | elif 'n' == answer: 72 | break 73 | else: 74 | print 'user name is already in db' 75 | olduser() 76 | break 77 | 78 | 79 | def output_use_file(): 80 | print db 81 | f = open('account.txt', 'w') 82 | for key in db: 83 | user = key + ':' + db[key][0] + ':' + str(db[key][1]) + os.linesep 84 | f.write(user) 85 | f.close() 86 | 87 | 88 | def output_use_pickle(): 89 | accountfile = 'pickle.data' 90 | f = open(accountfile, 'w') 91 | p.dump(db, f) 92 | f.close() 93 | 94 | f = open(accountfile) 95 | accountdb = p.load(f) 96 | print accountdb 97 | 98 | 99 | def output_use_shelve(): 100 | accountfile = 'shelve.data' 101 | accountdb = s.open(accountfile, 'c') 102 | accountdb['data'] = db 103 | accountdb.close() 104 | 105 | accountdb = s.open(accountfile, 'r') 106 | print accountdb['data'] 107 | 108 | 109 | def adminlogin(): 110 | while True: 111 | name = raw_input('login name desired: ').lower() 112 | if not name.isalnum() and '' in name: 113 | print 'name format error' 114 | continue 115 | else: 116 | pwd = raw_input('login passwd desired: ') 117 | if name == 'root' and pwd == 'root': 118 | print 'welcom admin' 119 | break 120 | else: 121 | print 'user name or passwd is wrong,input again' 122 | if len(db) == 0: 123 | print 'there is nothing you can do' 124 | else: 125 | answer = raw_input('output all account? y/n').lower() 126 | if 'y' == answer: 127 | # output_use_file() 128 | output_use_pickle() 129 | # output_use_shelve() 130 | elif 'n' == answer: 131 | print 'bye' 132 | 133 | 134 | def showmenu(): 135 | prompt = """ 136 | (A)dmin Login 137 | (U)ser Login 138 | (R)emove a existing user 139 | (Q)uit 140 | Enter choice:""" 141 | 142 | done = False 143 | while not done: 144 | chosen = False 145 | while not chosen: 146 | try: 147 | choice = raw_input(prompt).strip()[0].lower() 148 | except (EOFError, KeyboardInterrupt): 149 | choice = 'q' 150 | print '\nYou picked: [%s]' % choice 151 | if choice not in 'aurq': 152 | print 'invalid option,try again' 153 | else: 154 | chosen = True 155 | 156 | if choice == 'q': 157 | done = True 158 | if choice == 'r': 159 | removeuser() 160 | if choice == 'u': 161 | userlogin() 162 | if choice == 'a': 163 | adminlogin() 164 | 165 | 166 | if __name__ == '__main__': 167 | showmenu() 168 | -------------------------------------------------------------------------------- /09_File and Input Output/9-15_copyfile.py: -------------------------------------------------------------------------------- 1 | def copyfile(src, dst): 2 | with open(dst, 'a') as f: 3 | with open(src) as s: 4 | for item in s: 5 | f.write(item) 6 | 7 | if __name__ == '__main__': 8 | src = raw_input('copy from: ') 9 | dst = raw_input('copy to: ') 10 | copyfile(src, dst) -------------------------------------------------------------------------------- /09_File and Input Output/9-20_gzip.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | 3 | def compress(zipfile, filename): 4 | obj = gzip.open(zipfile, 'wb') 5 | with open(filename, 'rb') as f: 6 | obj.writelines(f) 7 | obj.close() 8 | 9 | def decompress(zipfile, filename): 10 | obj = gzip.open(zipfile, 'rb') 11 | content = obj.read() 12 | with open(filename, 'wb') as f: 13 | f.write(content) 14 | 15 | if __name__ == '__main__': 16 | compress('compress.gzip', 'test.txt') 17 | decompress('compress.gzip', 'decompress.txt') 18 | -------------------------------------------------------------------------------- /09_File and Input Output/9-21_zipfile.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | 3 | def create_zipfile(zipname, filename1, filename2): 4 | z = zipfile.ZipFile(zipname, 'w') 5 | z.write(filename1) 6 | z.write(filename2) 7 | z.close() 8 | 9 | def add_zipfile(zipname, filename): 10 | z = zipfile.ZipFile(zipname, 'a') 11 | z.write(filename) 12 | z.close() 13 | 14 | def extract_zipfile(zipname, filename): 15 | z = zipfile.ZipFile(zipname, 'r') 16 | z.extract(filename) 17 | z.close() 18 | 19 | def zipfile_filelist(zipname): 20 | z = zipfile.ZipFile(r'test.zip', 'r') 21 | z.printdir() 22 | z.close() 23 | 24 | if __name__ == '__main__': 25 | create_zipfile(r'test.zip', r'test.txt', r'test1.txt') 26 | add_zipfile(r'test.zip', r'test2.txt') 27 | extract_zipfile(r'test.zip', r'test1.txt') 28 | zipfile_filelist(r'test.zip') 29 | 30 | -------------------------------------------------------------------------------- /09_File and Input Output/9-22_lszip.py: -------------------------------------------------------------------------------- 1 | import zipfile, os, time 2 | 3 | filename = raw_input('zip file name:') 4 | print 'zip file size: %d bytes' % (os.stat(filename).st_size) 5 | 6 | z = zipfile.ZipFile(filename, 'r') 7 | print 'filename\tdatetime\tsize\tcompress size\trate' 8 | for info in z.infolist(): 9 | t = time.ctime(time.mktime(tuple(list(info.date_time) + [0, 0, 0]))) 10 | print '%s\t%s\t%d\t%d\t%.2f%%' % ( 11 | info.filename, t, info.file_size, info.compress_size, float(info.compress_size) / info.file_size * 100) 12 | 13 | z.close() 14 | -------------------------------------------------------------------------------- /09_File and Input Output/9-23_tarfile.py: -------------------------------------------------------------------------------- 1 | import tarfile 2 | 3 | def create_tarfile(tarname, filename1, filename2): 4 | t = tarfile.open(tarname, 'w:bz2') # w:gz 5 | t.add(filename1) 6 | t.add(filename2) 7 | t.close() 8 | 9 | def extract_tarfile(tarname, des_dir): 10 | t = tarfile.open(tarname, 'r') 11 | t.extractall(des_dir) 12 | t.close() 13 | 14 | if __name__ == '__main__': 15 | create_tarfile(r'test.tar.bz2', r'test.txt', r'test1.txt') 16 | extract_tarfile(r'test.tar.bz2', r'F:\extract_tarfile') -------------------------------------------------------------------------------- /09_File and Input Output/9-24_movefile_in_zip.py: -------------------------------------------------------------------------------- 1 | import tarfile 2 | import zipfile 3 | import os 4 | 5 | def movefile(src, dst, filename): 6 | if src.endswith('.zip') and dst.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')): 7 | zipobj = zipfile.ZipFile(src, 'r') 8 | content = zipobj.read(filename) 9 | zipobj.close() 10 | 11 | with open(filename, 'w') as f: 12 | f.write(content) 13 | 14 | tar = tarfile.open(dst, 'r') 15 | ls = tar.getnames() 16 | tar.extractall() 17 | tar.close() 18 | 19 | mode = 'w:gz' if dst.endswith(('tar.gz', '.tgz')) else 'w:bz2' 20 | tar = tarfile.open(dst, mode) 21 | for name in ls + [filename]: 22 | tar.add(name) 23 | os.remove(name) 24 | tar.close() 25 | elif src.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')) and dst.endswith('.zip'): 26 | tar = tarfile.open(src, 'r') 27 | tar.extract(filename) 28 | tar.close() 29 | 30 | zipobj = zipfile.ZipFile(dst, 'a') 31 | zipobj.write(filename) 32 | zipobj.close() 33 | os.remove(filename) 34 | 35 | if __name__ == '__main__': 36 | movefile(r'test.zip', r'test2.tar.gz', r'test2.txt') 37 | movefile(r'test2.tar.gz', r'test.zip', r'test2.txt') 38 | -------------------------------------------------------------------------------- /09_File and Input Output/9-25_multi_tar_zip.py: -------------------------------------------------------------------------------- 1 | import tarfile 2 | import zipfile 3 | import os 4 | 5 | def extract(path, filename): 6 | if filename.endswith('.zip'): 7 | with zipfile.ZipFile(filename, 'r') as f: 8 | f.extractall(path) 9 | elif filename.endswith(('.tgz', '.tar.gz', '.bz2', '.tbz', 'tar')): 10 | with tarfile.open(filename, 'r') as f: 11 | f.extractall(path) 12 | 13 | def decompress(target, *files): 14 | if not os.path.exists(target): 15 | os.mkdir(target) 16 | extract(target, files[0]) 17 | for name in files[1:]: 18 | dirname = os.path.splitext(os.path.basename(name))[0] 19 | dirname = '.\\' + target + '\\' + dirname 20 | os.mkdir(dirname) 21 | extract(dirname, name) 22 | 23 | if __name__ == '__main__': 24 | os.chdir(r'.\wx') 25 | decompress('test', 'test.zip', 'test2.tar.gz', 'test.tar.bz2') 26 | -------------------------------------------------------------------------------- /09_File and Input Output/9-8-cmd-dir-module.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | 3 | def get_module_attr(module_name) : 4 | module = importlib.import_module(module_name) # module = __import__(m) 5 | attrs = dir(module) 6 | res = [] 7 | 8 | for attr in attrs: 9 | value = getattr(module, attr) 10 | res.append({'name':attr, 'type':type(value), 'value':value}) 11 | return res 12 | 13 | for attr in get_module_attr('math'): 14 | print 'name: %s\t type: %s\t value: %s' % (attr['name'], attr['type'], attr['value']) 15 | 16 | -------------------------------------------------------------------------------- /09_File and Input Output/9-9_python_stdlib_doc.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from warnings import catch_warnings 4 | 5 | pymodules = {} 6 | path = r'D:\Python27\Lib' 7 | suffix = '.py' 8 | 9 | pyfiles = [f for f in os.listdir(path) if f.endswith(suffix)] 10 | for f in pyfiles: 11 | # splitext return: (filename, extension) 12 | module_name = os.path.splitext(f)[0] 13 | try: 14 | module = importlib.import_module(module_name) 15 | pymodules[module_name] = module.__doc__ 16 | except ImportError, e: 17 | continue 18 | 19 | hasdoc = [] 20 | nodoc = [] 21 | for module in pymodules: 22 | if pymodules[module]: 23 | hasdoc.append(module) 24 | else: 25 | nodoc.append(module) 26 | 27 | print 'module has no doc:' 28 | for key in nodoc: 29 | print key + '|', 30 | 31 | print '*' * 50 32 | 33 | print 'module has doc:' 34 | for key in hasdoc: 35 | print '[', key, ']' 36 | print pymodules[key] 37 | print '-' * 30 38 | -------------------------------------------------------------------------------- /09_File and Input Output/9.7-eg-ospathex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | for tmpdir in ('F:\movie', r'F:\movie'): 5 | if os.path.isdir(tmpdir): 6 | break 7 | else: 8 | print 'no temp directory available' 9 | tmpdir = '' 10 | 11 | if tmpdir: 12 | os.chdir(tmpdir) 13 | cwd = os.getcwd() 14 | print '*** current temporary directory' 15 | print cwd 16 | 17 | print '*** creating example directory...' 18 | os.mkdir('example') 19 | os.chdir('example') 20 | cwd = os.getcwd() 21 | print '*** new working directory:' 22 | print cwd 23 | print '*** original directory listing:' 24 | print os.listdir(cwd) 25 | 26 | print '*** creating test file...' 27 | fobj = open('test', 'w') 28 | fobj.write('foo\n') 29 | fobj.write('bar\n') 30 | fobj.close() 31 | print '*** updated directory listing:' 32 | print os.listdir(cwd) 33 | 34 | print "*** renaming 'test' to 'filetest.txt'" 35 | os.rename('test', 'filetest.txt') 36 | print '*** updated directory listing:' 37 | print os.listdir(cwd) 38 | 39 | path = os.path.join(cwd, os.listdir (cwd)[0]) 40 | print '*** full file pathname' 41 | print path 42 | 43 | print '*** (pathname, basename) ==' 44 | print os.path.split(path) 45 | print '*** (filename, extension) ==' 46 | print os.path.splitext(os.path.basename(path)) 47 | 48 | print '*** displaying file contents:' 49 | fobj = open(path) 50 | for eachLine in fobj: 51 | print eachLine, 52 | fobj.close() 53 | 54 | print '*** deleting test file' 55 | os.remove(path) 56 | print '*** updated directory listing:' 57 | print os.listdir(cwd) 58 | os.chdir(os.pardir) 59 | print '*** deleting test directory' 60 | os.rmdir('example') 61 | print '*** DONE' 62 | -------------------------------------------------------------------------------- /09_File and Input Output/note09_Files and Input Output.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 本章介绍了文件对象(内建函数、内建方法、属性), 标准文件, 访问文件系统的方法, 文件执行,初步认识持久存储和标准库中与文件有关的模块。 3 | 4 | # 知识点 5 | ## 9.1 文件内建函数 6 | ### 9.1.1 open() 7 | `open()` 的基本语法: 8 | `file_object = open(file_name, access_mode='r', buffering=-1)` 9 | 10 | 所有 POSIX 兼容系统, 包括 Linux , 都会忽略 "b" 二进制访问模式。 11 | 12 | ### 9.1.2 file() 13 | 建议使用 open() 来读写文件, 而在处理文件对象时使用 file() , 例如: `if instance(f, file) ` 14 | 15 | ## 9.2 文件内建方法 16 | ### 9.2.1 输入 17 | `file.xreadlines()` 不是一次性读取取所有的行, 而是每次读取一块, 所以用在 for 循环时可以减少对内存的占用,和使用 `iter(file)`、`for eachLine in file` 的效果是一样的。 18 | ### 9.2.2 输出 19 | - 输入方法 `read()` 或者 `readlines()` 从文件中读取行时, Python 并不会删除行结束符; 20 | - 输出方法 `write()` 或 `writelines()` 不会自动加入行结束符,需要在向文件写入数据前自行加入行结束符。 21 | 22 | ### 9.2.3 文件内移动 23 | - `seek(offset=0)`:在文件中移动文件指针到不同的位置. offset 字节代表相对于某个位置偏移量。 24 | - `tell()`:返回当前位置的偏移量。 25 | - `truncate(size=file.tell())`:截取文件到最大 size 字节, 默认为当前文件位置 26 | 27 | ### 9.2.4 os 模块内建方法 28 | - `os.linesep()`:行分隔符 29 | - `os.sep()`:路径名分隔符 30 | 31 | ## 9.3 文件内建属性 32 | - `f.closed` 33 | - `f.mode` 34 | - `f.encoding` 35 | - `f.name` 36 | 37 | ## 9.4 标准文件 38 | - 标准输入:`sys.stdin` 39 | - 标准输出:`sys.stdout` 40 | - 标准错误:`sys.stderr` 41 | 42 | ## 9.5 commandline参数 43 | - argc 和 argv 分别代表参数个数(argument count)和参数向量(argument vector). 44 | - `sys.argv[0]` 是程序的名称. 45 | - 处理命令行参数的模块:getopt模块、optparse模块 46 | 47 | ## 9.6 文件系统 48 | ### 9.6.1 os 模块 49 | - `read()/write()` 根据文件描述符读取/写入数据 50 | - `remove()/unlink()` Delete file 删除文件 51 | - `mkdir()/makedirs()` 创建目录/创建多层目录 52 | - `rmdir()/removedirs()` 删除目录/删除多层目录 53 | 54 | ### 9.6.2 os.path 模块 55 | - `basename()` 去掉目录路径, 返回文件名 56 | - `dirname()` 去掉文件名, 返回目录路径 57 | - `exists()` 指定路径(文件或目录)是否存在 58 | - `isabs()` 指定路径是否为绝对路径 59 | - `isdir()` 指定路径是否存在且为一个目录 60 | - `isfile()` 指定路径是否存在且为一个文件 61 | - `islink()` 指定路径是否存在且为一个符号链接 62 | 63 | ## 9.7 永久存储模块 64 | ### 9.7.1 pickle 和 marshal 模块 65 | 1. marshal 和 pickle 模块可以实现最小化永久性转换并储存 Python 对象. 66 | 2. **数据的序列化**(扁平化、顺序化):将(复杂)对象转换为一个二进制数据集合的过程, 使之可以将数据集合保存起来或通过网络发送。 67 | 3. **数据的反序列化**:把二进制数据集合恢复原来的对象格式的过程。 68 | 4. cPickle 是 pickle 的一个更快的 C 语言编译版本. 69 | 70 | 71 | **marshal 和 pickle 模块的区别:** 72 | 73 | - marshal 只能处理简单的 Python 对象(数字, 序列, 映射, 以及代码对象) 74 | - pickle 可以处理递归对象, 被不同地方多次引用的对象, 以及用户定义的类和实例 75 | 76 | ### 9.7.2 DBM 风格的模块 77 | - db系列的模块使用传统的**DBM格式写入数据:只能储存字符串,不能对Python对象进行序列化。** 78 | - DBM的多种实现:dbhash/bsddb,dbm,gdbm,以及dumbdbm等,这些模块为对象提供了一个**命名空间**,这些对象同时具备字典对象和文件对象的特点。 79 | 80 | ### 9.7.3 shelve 模块 81 | - shelve模块使用anydbm模块寻找合适的DBM模块,然后使用cPickle来完成对储存转换过程。 82 | - shelve模块允许对数据库文件进行并发的读访问,但不允许共享读/写访问。 83 | - shelve模块提供了字典式的文件对象访问功能, 进一步减少工作. 84 | 85 | **shelve模块与储存转换模块、永久性储存模块之间的关系:** 86 | ![](index_files/aaa.PNG) 87 | 88 | ## 9.8 相关模块 89 | - base64 提供二进制字符串和文本字符串间的编码/解码操作 90 | - binascii 提供二进制和 ASCII 编码的二进制字符串间的编码/解码操作 91 | - glob/fnmatch 提供 Unix 样式的通配符匹配的功能 92 | - fileinput 提供多个文本文件的行迭代器 93 | - getopt/optparse 提供了命令行参数的解析 94 | - tarfile 读写 TAR 归档文件, 支持压缩文件 95 | - gzip/zlib 读写 GNU zip( gzip) 文件(压缩需要 zlib 模块) 96 | - bz2 访问 BZ2 格式的压缩文件 97 | - zipfilec 用于读取 ZIP 归档文件的工具 98 | 99 | 100 | # 练习 101 | 102 | **9–1. 文件过滤. 显示一个文件的所有行, 忽略以井号( # )开头的行. 这个字符被用做 Python , Perl, Tcl, 等大多脚本文件的注释符号. 附加题: 处理不是第一个字符开头的注释.** 103 | ```python 104 | for line in open('test.txt'): 105 | if line is not None and line[0] != '#': 106 | print line, 107 | ``` 108 | 109 | **9–2. 文件访问. 提示输入数字 N 和文件 F, 然后显示文件 F 的前 N 行.** 110 | 111 | *略。* 112 | 113 | **9–3. 文件信息. 提示输入一个文件名, 然后显示这个文本文件的总行数.** 114 | ```python 115 | sum(1 for x in open('test.txt')) 116 | ``` 117 | 118 | **9–4. 文件访问. 写一个逐页显示文本文件的程序. 提示输入一个文件名, 每次显示文本文件的 25 行, 暂停并向用户提示"按任意键继续", 按键后继续执行.** 119 | *略。* 120 | 121 | **9–5. 考试成绩. 改进你的考试成绩问题(练习 5 -3 和 6-4), 要求能从多个文件中读入考试成绩. 文件的数据格式由你自己决定.** 122 | 123 | *略。* 124 | 125 | **9–6. 文件比较. 写一个比较两个文本文件的程序. 如果不同, 给出第一个不同处的行号和列号.** 126 | 127 | *略。* 128 | 129 | **9–7. 解析文件. Win32 用户: 创建一个用来解析 Windows .ini 文件的程序. POSIX 用户: 创建一个解析 /etc/serves 文件的程序. 其它平台用户: 写一个解析特定结构的系统配置文件的程序.** 130 | 131 | 思路:忽略以#号开头的行,类似9-1。 132 | 133 | **9–8. 模块研究. 提取模块的属性资料. 提示用户输入一个模块名(或者从命令行接受输入). 然后使用 dir() 和其它内建函数提取模块的属性, 显示它们的名字, 类型, 值.** 134 | ```python 135 | import importlib 136 | 137 | def get_module_attr(module_name) : 138 | module = importlib.import_module(module_name) # module = __import__(m) 139 | attrs = dir(module) 140 | res = [] 141 | 142 | for attr in attrs: 143 | value = getattr(module, attr) 144 | res.append({'name':attr, 'type':type(value), 'value':value}) 145 | return res 146 | 147 | for attr in get_module_attr('math'): 148 | print 'name: %s\t type: %s\t value: %s' % (attr['name'], attr['type'], attr['value']) 149 | ``` 150 | 151 | **9–9. Python 文档字符串. 进入 Python 标准库所在的目录. 检查每个 .py 文件看是否有 __doc__ 字符串, 如果有, 对其格式进行适当的整理归类. 你的程序执行完毕后, 应该会生成一个漂亮的清单. 里边列出哪些模块有文档字符串, 以及文档字符串的内容. 清单最后附上那些没有文档字符串模块的名字. 附加题: 提取标准库中各模块内全部类(class)和函数的文档.** 152 | ```python 153 | import os 154 | import importlib 155 | from warnings import catch_warnings 156 | 157 | pymodules = {} 158 | path = r'D:\Python27\Lib' 159 | suffix = '.py' 160 | 161 | pyfiles = [f for f in os.listdir(path) if f.endswith(suffix)] 162 | for f in pyfiles: 163 | # splitext return: (filename, extension) 164 | module_name = os.path.splitext(f)[0] 165 | try: 166 | module = importlib.import_module(module_name) 167 | pymodules[module_name] = module.__doc__ 168 | except ImportError, e: 169 | continue 170 | 171 | hasdoc = [] 172 | nodoc = [] 173 | for module in pymodules: 174 | if pymodules[module]: 175 | hasdoc.append(module) 176 | else: 177 | nodoc.append(module) 178 | 179 | print 'module has no doc:' 180 | for key in nodoc: 181 | print key + '|', 182 | 183 | print '*' * 50 184 | 185 | print 'module has doc:' 186 | for key in hasdoc: 187 | print '[', key, ']' 188 | print pymodules[key] 189 | print '-' * 30 190 | 191 | ``` 192 | 193 | **9–10. 家庭理财. 创建一个家庭理财程序. 你的程序需要处理储蓄, 支票, 金融市场, 定期存款等多种帐户. 为每种帐户提供一个菜单操作界面, 要有存款, 取款, 借, 贷等操作. 另外还要提供一个取消操作选项. 用户退出这个程序时相关数据应该保存到文件里去(出于备份的目的, 程序执行过程中也要备份.)** 194 | 195 | *略。* 196 | 197 | **9–11. Web 站点地址. ** 198 | 199 | **a) 编写一个 URL 书签管理程序. 使用基于文本的菜单, 用户可以添加, 修改或者删除书签数据项. 书签数据项中包含站点的名称, URL 地址, 以及一行简单说明(可选). 另外提供检索功能, 可以根据检索关键字在站点名称和 URL 两部分查找可能的匹配. 程序退出时把数据保存到一个磁盘文件中去; 再次执行时候加载保存的数据.** 200 | 201 | **b)改进 a) 的解决方案, 把书签输出到一个合法且语法正确的 HTML 文件(.html 或 htm )中, 这样用户就可以使用浏览器查看自己的书签清单. 另外提供创建"文件夹"功能, 对相关的书签进行 分组管理. ** 202 | 203 | 附加题: 请阅读 Python 的 re 模块了解有关正则表达式的资料, 使用正则表达式对用户输入 的 URL 进行验证 204 | 205 | ```python 206 | import re, os 207 | 208 | def checkurl(url): 209 | regex = re.compile( 210 | r'^(?:http|ftp)s?://' # http:// or https:// 211 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... 212 | r'localhost|' # localhost... 213 | r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip 214 | r'(?::\d+)?' # optional port 215 | r'(?:/?|[/?]\S+)$', re.IGNORECASE) 216 | if regex.match(url): 217 | return True 218 | else: 219 | return False 220 | 221 | def geturl(): 222 | name = raw_input('pls input a url name:') 223 | while 1: 224 | url = raw_input('pls input a url address:') 225 | if checkurl(url): 226 | break 227 | else: 228 | print 'wrong url format,pls input again' 229 | mark = raw_input('pls input a url mark:') 230 | folder = raw_input('pls input a url folder:') 231 | return (name, url, mark, folder) 232 | 233 | def load(filename): 234 | f = open(filename, 'a+') 235 | bmlist = f.readlines() 236 | f.close() 237 | return bmlist 238 | 239 | def save(bmlist, filename): 240 | f = open(filename, 'w+') 241 | for line in bmlist: 242 | if len(line) == 0: 243 | continue 244 | f.write(line) 245 | f.close() 246 | 247 | def add(bmlist, name, url, mark, folder='default'): 248 | bookmark = name + ';' + url + ';' + mark + ';' + folder + os.linesep 249 | if bookmark not in bmlist: 250 | bmlist.append(bookmark) 251 | 252 | def modify(bmlist, index, name, url, mark, folder): 253 | bmlist[index] = name + ';' + url + ';' + mark + ';' + folder + os.linesep 254 | 255 | def delbm(bmlist, index): 256 | bmlist.pop(index) 257 | 258 | def findbk(bmlist, fname, furl): 259 | for i, item in enumerate(bmlist): 260 | (name, url, mark, folder) = item.split(';') 261 | if fname and furl: 262 | if (fname in name) and (furl in url): 263 | return i 264 | if fname and (fname in name): 265 | return i 266 | if furl and (furl in url): 267 | return i 268 | else: 269 | return -1 270 | 271 | def output2html(bmlist): 272 | for i, item in enumerate(bmlist): 273 | (name, url, mark, folder) = item.split(';') 274 | os.mkdir(folder.strip()) 275 | filename = name.strip() + '.html' 276 | f = open(filename, 'w+') 277 | fmt = '%d\t%s\t%s\t%s\t%s
' 278 | f.write('bookmark') 279 | content = fmt % (i + 1, name, r'http:\\' + url, url, mark, folder) 280 | f.write(content) 281 | f.write('') 282 | f.close() 283 | os.rename(filename, folder.strip() + os.sep + filename) 284 | 285 | def show_menu(): 286 | bmlist = load(r'url.txt') 287 | while True: 288 | print '0. quit' 289 | print '1. add a url bookmark' 290 | print '2. modify a url bookmark' 291 | print '3. delete a url bookmark' 292 | print '4. find a url bookmark' 293 | print '5. output url bookmark as html' 294 | print '\n' 295 | 296 | iInput = input("please input operation num: ") 297 | 298 | if iInput < 0 or iInput > 5: 299 | print 'Error input operation, try again.\n' 300 | continue 301 | elif 0 == iInput: 302 | save(bmlist, r'url.txt') 303 | break 304 | elif 1 == iInput: 305 | data = geturl() 306 | add(bmlist, *data) 307 | elif 2 == iInput: 308 | index = int(raw_input('bookmark index:')) 309 | data = geturl() 310 | modify(bmlist, index, *data) 311 | elif 3 == iInput: 312 | index = int(raw_input('bookmark index:')) 313 | delbm(bmlist, index) 314 | elif 4 == iInput: 315 | name = raw_input('url name:') 316 | url = raw_input('url address:') 317 | index = findbk(bmlist, name, url) 318 | if index == -1: 319 | print 'not found' 320 | else: 321 | print bmlist[index] 322 | elif 5 == iInput: 323 | output2html(bmlist) 324 | 325 | if __name__ == '__main__': 326 | show_menu() 327 | ``` 328 | 329 | **9–12. 用户名和密码. 回顾练习 7-5 , 修改代码使之可以支持"上次登录时间". 请参阅 time 模块中的文档了解如 何记录用户上次登录的时间. 另外提供一个"系统管理员", 它可以导出所有用户的用户名, 密码 (如果想要的话,你可以把密码加密), 以及"上次登录时间".** 330 | 331 | **a) 数 据 应 该 保 存 在 磁 盘 中 , 使 用 冒 号 ( : ) 分 割 , 一 次 写 入 一 行 , 例 如 `"joe:boohoo:953176591.145"`, 文件中数据的行数应该等于你系统上的用户数.** 332 | 333 | **b) 进一步改进你的程序, 不再一次写入一行, 而使用 pickle 模块保存整个数据对象. 请参 阅 pickle 模块的文档了解如何序列化/扁平化对象, 以及如何读写保存的对象. 一般来说, 这个 解决方案的代码行数要比a) 的少.** 334 | 335 | **c) 使用 shelve 模块替换 pickle 模块, 由于可以省去一些维护代码,这个解决方案的代码比 b) 的更少** 336 | 337 | ```python 338 | from datetime import datetime 339 | import hashlib, os 340 | import pickle as p 341 | import shelve as s 342 | 343 | db = {} 344 | 345 | def newuser(): 346 | value = [] 347 | prompt = 'login name desired again: ' 348 | while True: 349 | name = raw_input(prompt).lower() 350 | if not name.isalnum() and '' in name: 351 | print 'name format error' 352 | continue 353 | else: 354 | if db.has_key(name): 355 | prompt = 'name taken,try another: ' 356 | continue 357 | else: 358 | break 359 | pwd = raw_input('login passwd desired: ') 360 | m = hashlib.md5() 361 | m.update(pwd) 362 | value.append(m.hexdigest()) 363 | value.append(datetime.now()) 364 | db[name] = value 365 | print 'new user is %s, register time is %s' % (name, db[name][1]) 366 | 367 | 368 | def olduser(): 369 | name = raw_input('login name desired again: ').lower() 370 | pwd = raw_input('login passwd desired: ') 371 | m = hashlib.md5() 372 | m.update(pwd) 373 | passwd = db.get(name) 374 | if passwd[0] == m.hexdigest(): 375 | newtime = datetime.now() 376 | if (newtime - db[name][1]).days == 0 and (newtime - db[name][1]).seconds < 14400: 377 | print 'you already logged in at %s: ' % (db[name][1]) 378 | else: 379 | passwd[1] = newtime 380 | print 'welcome back %s, login time is %s' % (name, passwd[1]) 381 | 382 | else: 383 | print 'login incorrect' 384 | 385 | 386 | def removeuser(): 387 | print db 388 | name = raw_input('input a user name to remove: ').lower() 389 | if name in db: 390 | db.pop(name) 391 | else: 392 | print 'input error' 393 | 394 | 395 | def userlogin(): 396 | while True: 397 | name = raw_input('login name desired: ').lower() 398 | if not name.isalnum() and '' in name: 399 | print 'name format error' 400 | continue 401 | else: 402 | if not db.has_key(name): 403 | print 'user name is not in db' 404 | answer = raw_input('register a new user? y/n').lower() 405 | if 'y' == answer: 406 | newuser() 407 | break 408 | elif 'n' == answer: 409 | break 410 | else: 411 | print 'user name is already in db' 412 | olduser() 413 | break 414 | 415 | 416 | def output_use_file(): 417 | print db 418 | f = open('account.txt', 'w') 419 | for key in db: 420 | user = key + ':' + db[key][0] + ':' + str(db[key][1]) + os.linesep 421 | f.write(user) 422 | f.close() 423 | 424 | 425 | def output_use_pickle(): 426 | accountfile = 'pickle.data' 427 | f = open(accountfile, 'w') 428 | p.dump(db, f) 429 | f.close() 430 | 431 | f = open(accountfile) 432 | accountdb = p.load(f) 433 | print accountdb 434 | 435 | 436 | def output_use_shelve(): 437 | accountfile = 'shelve.data' 438 | accountdb = s.open(accountfile, 'c') 439 | accountdb['data'] = db 440 | accountdb.close() 441 | 442 | accountdb = s.open(accountfile, 'r') 443 | print accountdb['data'] 444 | 445 | 446 | def adminlogin(): 447 | while True: 448 | name = raw_input('login name desired: ').lower() 449 | if not name.isalnum() and '' in name: 450 | print 'name format error' 451 | continue 452 | else: 453 | pwd = raw_input('login passwd desired: ') 454 | if name == 'root' and pwd == 'root': 455 | print 'welcom admin' 456 | break 457 | else: 458 | print 'user name or passwd is wrong,input again' 459 | if len(db) == 0: 460 | print 'there is nothing you can do' 461 | else: 462 | answer = raw_input('output all account? y/n').lower() 463 | if 'y' == answer: 464 | # output_use_file() 465 | output_use_pickle() 466 | # output_use_shelve() 467 | elif 'n' == answer: 468 | print 'bye' 469 | 470 | 471 | def showmenu(): 472 | prompt = """ 473 | (A)dmin Login 474 | (U)ser Login 475 | (R)emove a existing user 476 | (Q)uit 477 | Enter choice:""" 478 | 479 | done = False 480 | while not done: 481 | chosen = False 482 | while not chosen: 483 | try: 484 | choice = raw_input(prompt).strip()[0].lower() 485 | except (EOFError, KeyboardInterrupt): 486 | choice = 'q' 487 | print '\nYou picked: [%s]' % choice 488 | if choice not in 'aurq': 489 | print 'invalid option,try again' 490 | else: 491 | chosen = True 492 | 493 | if choice == 'q': 494 | done = True 495 | if choice == 'r': 496 | removeuser() 497 | if choice == 'u': 498 | userlogin() 499 | if choice == 'a': 500 | adminlogin() 501 | 502 | 503 | if __name__ == '__main__': 504 | showmenu() 505 | 506 | ``` 507 | 508 | **9–13. 命令行参数 a) 什么是命令行参数, 它们有什么用? b) 写一个程序, 打印出所有的命令行参数.** 509 | ```python 510 | import sys 511 | print str(sys.argv) 512 | ``` 513 | 514 | **9–14. 记录结果. 修改你的计算器程序(练习 5-6 )使之接受命令行参数.** 515 | *略。* 516 | 517 | **9–15. 复制文件. 提示输入两个文件名(或者使用命令行参数). 把第一个文件的内容复制到第二个文件中去.** 518 | ```python 519 | def copyfile(src, dst): 520 | with open(dst, 'a') as f: 521 | with open(src) as s: 522 | for item in s: 523 | f.write(item) 524 | 525 | if __name__ == '__main__': 526 | src = raw_input('copy from: ') 527 | dst = raw_input('copy to: ') 528 | copyfile(src, dst) 529 | ``` 530 | 531 | **9–16. 文本处理. 人们输入的文字常常超过屏幕的最大宽度. 编写一个程序, 在一个文本文件中查找长度大于 80 个字符的文本行. 从最接近 80 个字符的单词断行, 把剩余文件插入到 下一行处.** 532 | 533 | *略。* 534 | 535 | **9–17. 文本处理. 创建一个原始的文本文件编辑器. 你的程序应该是菜单驱动的, 有如下这些选项: ** 536 | 1) 创建文件(提示输入文件名和任意行的文本输入), 537 | 2) 显示文件(把文件的内容显示到屏幕), 538 | 3) 编辑文件(提示输入要修改的行, 然后让用户进行修改), 539 | 4) 保存文件, 540 | 5) 退出. 541 | 542 | *略。* 543 | 544 | **9–18. 搜索文件. 提示输入一个字节值(0 - 255)和一个文件名. 显示该字符在文件中出现的次数.** 545 | ```python 546 | def counts(filename, value): 547 | ch = chr(value) 548 | with open(filename, 'rb') as f: 549 | total = sum(item.count(ch) for item in f) 550 | return total 551 | 552 | if __name__ == '__main__': 553 | filename = raw_input('file name: ') 554 | value = int(raw_input('value: ')) 555 | print counts(filename, value) 556 | ``` 557 | 558 | **9–19. 创建文件. 创建前一个问题的辅助程序. 创建一个随机字节的二进制数据文件, 但某一特定字节会在文件中出现指定的次数. 该程序接受三个参数:** 559 | 1) 一个字节值( 0 - 255 ), 560 | 2) 该字符在数据文件中出现的次数, 以及 561 | 3) 数据文件的总字节长度. 562 | 你的工作就是生成这个文件, 把给定的字节随机散布在文件里, 并且要求保证给定字符在文件中只出现指定的次数, 文件应精确地达到要求的长度. 563 | ```python 564 | from random import randint 565 | 566 | def create(filename, value, total, maxlen): 567 | assert 0 <= value <= 255 568 | ls = [chr(randint(0, 255)) for i in xrange(maxlen-total)] 569 | ch = chr(value) 570 | for i in xrange(total-ls.count(ch)): 571 | ls.insert(randint(0, len(ls)-1), ch) 572 | for i in xrange(maxlen - len(ls)): 573 | ls.insert(randint(0, len(ls)-1), chr(randint(0, value-1))) 574 | with open(filename, 'wb') as f: 575 | f.write(''.join(ls)) 576 | 577 | if __name__ == '__main__': 578 | filename = raw_input('file name: ') 579 | value = int(raw_input('value: ')) 580 | total = int(raw_input('total: ')) 581 | maxlen = int(raw_input('max length of file: ')) 582 | create(filename, value, total, maxlen) 583 | ``` 584 | 585 | **9–20. 压缩文件. 写一小段代码, 压缩/解压缩 gzip 或 bzip 格式的文件. 可以使用命令行下的 gzip 或 bzip2 以及 GUI 程序 PowerArchiver , StuffIt , 或 WinZip 来确认你的 Python支持这两个库.** 586 | ```python 587 | import gzip 588 | 589 | def compress(zipfile, filename): 590 | obj = gzip.open(zipfile, 'wb') 591 | with open(filename, 'rb') as f: 592 | obj.writelines(f) 593 | obj.close() 594 | 595 | def decompress(zipfile, filename): 596 | obj = gzip.open(zipfile, 'rb') 597 | content = obj.read() 598 | with open(filename, 'wb') as f: 599 | f.write(content) 600 | 601 | if __name__ == '__main__': 602 | compress('compress.gzip', 'test.txt') 603 | decompress('compress.gzip', 'decompress.txt') 604 | 605 | ``` 606 | 607 | **9–21. ZIP 归档文件. 创建一个程序, 可以往 ZIP 归档文件加入文件, 或从中提取文件,有可能的话, 加入创建ZIP 归档文件的功能.** 608 | ```python 609 | import zipfile 610 | 611 | def create_zipfile(zipname, filename1, filename2): 612 | z = zipfile.ZipFile(zipname, 'w') 613 | z.write(filename1) 614 | z.write(filename2) 615 | z.close() 616 | 617 | def add_zipfile(zipname, filename): 618 | z = zipfile.ZipFile(zipname, 'a') 619 | z.write(filename) 620 | z.close() 621 | 622 | def extract_zipfile(zipname, filename): 623 | z = zipfile.ZipFile(zipname, 'r') 624 | z.extract(filename) 625 | z.close() 626 | 627 | def zipfile_filelist(zipname): 628 | z = zipfile.ZipFile(r'test.zip', 'r') 629 | z.printdir() 630 | z.close() 631 | 632 | if __name__ == '__main__': 633 | create_zipfile(r'test.zip', r'test.txt', r'test1.txt') 634 | add_zipfile(r'test.zip', r'test2.txt') 635 | extract_zipfile(r'test.zip', r'test1.txt') 636 | zipfile_filelist(r'test.zip') 637 | ``` 638 | 639 | **9–22. ZIP 归档文件. unzip -l 命令显示出的 ZIP 归档文件很无趣. 创建一个 Python 脚本 lszip.py , 使它可以显示额外信息: 压缩文件大小, 每个文件的压缩比率(通过比较压缩 前后文件大小), 以及完成的 time.ctime() 时间戳, 而不是只有日期和 HH:MM . 提示: 归档文件的 date_time 属性并不完整, 无法提供给 time.mktime() 使用....这由你自己决定.** 640 | ```python 641 | import zipfile, os, time 642 | 643 | filename = raw_input('zip file name:') 644 | print 'zip file size: %d bytes' % (os.stat(filename).st_size) 645 | 646 | z = zipfile.ZipFile(filename, 'r') 647 | print 'filename\tdatetime\tsize\tcompress size\trate' 648 | for info in z.infolist(): 649 | t = time.ctime(time.mktime(tuple(list(info.date_time) + [0, 0, 0]))) 650 | print '%s\t%s\t%d\t%d\t%.2f%%' % ( 651 | info.filename, t, info.file_size, info.compress_size, float(info.compress_size) / info.file_size * 100) 652 | 653 | z.close() 654 | ``` 655 | 656 | **9–23. TAR 归档文件. 为 TAR 归档文件建立类似上个问题的程序. 这两种文件的不同之处在于 ZIP 文件通常是压缩的, 而 TAR 文件不是, 只是在 gzip 和 bzip2 的支持下才能完成压缩工作. 加入任意一种压缩格式支持.附加题: 同时支持 gzip 和 bzip2 .** 657 | ```python 658 | import tarfile 659 | 660 | def create_tarfile(tarname, filename1, filename2): 661 | t = tarfile.open(tarname, 'w:gz') # w:bz2 662 | t.add(filename1) 663 | t.add(filename2) 664 | t.close() 665 | 666 | def extract_tarfile(tarname, des_dir): 667 | t = tarfile.open(tarname, 'r') 668 | t.extractall(des_dir) 669 | t.close() 670 | 671 | if __name__ == '__main__': 672 | create_tarfile(r'test.tar.gz', r'test.txt', r'test1.txt') 673 | extract_tarfile(r'test.tar.gz', r'F:\extract_tarfile') 674 | ``` 675 | 676 | 677 | **9–24. 归档文件转换.参考前两个问题的解决方案, 写一个程序, 在 ZIP (.zip) 和TAR/gzip (.tgz/.tar.gz) 或 TAR/bzip2 (.tbz/.tar.bz2) 归档文件间移动文件. 文件可能是已经存在的, 必要时请创建文件.** 678 | ```python 679 | import tarfile 680 | import zipfile 681 | import os 682 | 683 | def movefile(src, dst, filename): 684 | if src.endswith('.zip') and dst.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')): 685 | zipobj = zipfile.ZipFile(src, 'r') 686 | content = zipobj.read(filename) 687 | zipobj.close() 688 | 689 | with open(filename, 'w') as f: 690 | f.write(content) 691 | 692 | tar = tarfile.open(dst, 'r') 693 | ls = tar.getnames() 694 | tar.extractall() 695 | tar.close() 696 | 697 | mode = 'w:gz' if dst.endswith(('tar.gz', '.tgz')) else 'w:bz2' 698 | tar = tarfile.open(dst, mode) 699 | for name in ls + [filename]: 700 | tar.add(name) 701 | os.remove(name) 702 | tar.close() 703 | elif src.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')) and dst.endswith('.zip'): 704 | tar = tarfile.open(src, 'r') 705 | tar.extract(filename) 706 | tar.close() 707 | 708 | zipobj = zipfile.ZipFile(dst, 'a') 709 | zipobj.write(filename) 710 | zipobj.close() 711 | os.remove(filename) 712 | 713 | if __name__ == '__main__': 714 | movefile(r'test.zip', r'test.tar.gz', r'test2.txt') 715 | movefile(r'test.tar.gz', r'test.zip', r'test2.txt') 716 | ``` 717 | 718 | **9–25. 通用解压程序. 创建一个程序, 接受任意数目的归档文件以及一个目标目录做为参数. 归档文件格式可以是 .zip, .tgz, .tar.gz, .gz, .bz2, .tar.bz2, .tbz 中的一种或几种. 程序会把第一个归档文件解压后放入目标目录, 把其它归档文件解压后放入以对应文件名命名的目录下 (不包括扩展名). 例如输入的文件名为 header.txt.gz 和 data.tgz ,目录为 incoming , header.txt 会被解压到 incoming 而 data.tgz 中的文件会被放入 incoming/data .** 719 | ```python 720 | import tarfile 721 | import zipfile 722 | import os 723 | 724 | def extract(path, filename): 725 | if filename.endswith('.zip'): 726 | with zipfile.ZipFile(filename, 'r') as f: 727 | f.extractall(path) 728 | elif filename.endswith(('.tgz', '.tar.gz', '.bz2', '.tbz', 'tar')): 729 | with tarfile.open(filename, 'r') as f: 730 | f.extractall(path) 731 | 732 | def decompress(target, *files): 733 | if not os.path.exists(target): 734 | os.mkdir(target) 735 | extract(target, files[0]) 736 | for name in files[1:]: 737 | dirname = os.path.splitext(os.path.basename(name))[0] 738 | dirname = '.\\' + target + '\\' + dirname 739 | os.mkdir(dirname) 740 | extract(dirname, name) 741 | 742 | if __name__ == '__main__': 743 | decompress('test', 'test.zip', 'test2.tar.gz', 'test.tar.bz2') 744 | ``` 745 | -------------------------------------------------------------------------------- /10_Errors and Exceptions/10-8-safe_input.py: -------------------------------------------------------------------------------- 1 | def safe_input(msg): 2 | data = None 3 | try: 4 | data = raw_input(msg) 5 | except (EOFError, KeyboardInterrupt): 6 | pass 7 | return data 8 | 9 | if __name__ == '__main__': 10 | print safe_input("please input: ") 11 | -------------------------------------------------------------------------------- /10_Errors and Exceptions/10-9-safe_sqrt.py: -------------------------------------------------------------------------------- 1 | import math, cmath 2 | 3 | def safe_sqrt(num): 4 | try: 5 | result = math.sqrt(num) 6 | except ValueError: 7 | result = cmath.sqrt(num) 8 | return result 9 | 10 | if __name__ == '__main__': 11 | print safe_sqrt(123) 12 | print safe_sqrt(-123) -------------------------------------------------------------------------------- /10_Errors and Exceptions/10–6-myopen.py: -------------------------------------------------------------------------------- 1 | def myopen(path, access='r'): 2 | try: 3 | f = open(path, access) 4 | except (IOError, ValueError): 5 | return None 6 | return f 7 | 8 | if __name__ == '__main__': 9 | print myopen("test.txt", 'abc') -------------------------------------------------------------------------------- /10_Errors and Exceptions/carddata.txt: -------------------------------------------------------------------------------- 1 | previous balance 2 | 25 3 | debits 4 | 21.64 5 | 541.24 6 | 25 7 | credits 8 | -25 9 | -541.24 10 | finance charge/late fees 11 | 7.30 12 | 5 -------------------------------------------------------------------------------- /10_Errors and Exceptions/cardlog.txt: -------------------------------------------------------------------------------- 1 | account log: 2 | ignored: could not convert string to float: # carddata.txt 3 | ignored: could not convert string to float: previous balance 4 | data... processed 5 | ignored: could not convert string to float: debits 6 | data... processed 7 | data... processed 8 | data... processed 9 | ignored: could not convert string to float: credits 10 | data... processed 11 | data... processed 12 | ignored: could not convert string to float: finance charge/late fees 13 | data... processed 14 | data... processed 15 | -------------------------------------------------------------------------------- /10_Errors and Exceptions/cardrun.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | def safe_float(obj): 5 | 'safe version of float()' 6 | try: 7 | retval = float(obj) 8 | except (ValueError, TypeError), diag: 9 | retval = str(diag) 10 | return retval 11 | 12 | def main(): 13 | 'handles all the data processing' 14 | log = open('cardlog.txt', 'w') 15 | try: 16 | ccfile = open('carddata.txt', 'r') 17 | except IOError, e: 18 | log.write('no txns this month\n') 19 | log.close() 20 | return 21 | 22 | txns = ccfile.readlines() # transactions 23 | ccfile.close() 24 | total = 0.00 25 | log.write('account log:\n') 26 | 27 | for eachTxn in txns: 28 | result = safe_float(eachTxn) 29 | if isinstance(result, float): 30 | total += result 31 | log.write('data... processed\n') 32 | else: 33 | log.write('ignored: %s' % result) 34 | print '$%.2f (new balance)' % (total) 35 | log.close() 36 | 37 | 38 | # 仅在非导入时启动 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /10_Errors and Exceptions/myexc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, socket, errno, types, tempfile 4 | 5 | class NetworkError(IOError): 6 | pass 7 | 8 | class FileError(IOError): 9 | pass 10 | 11 | def updArgs(args, newarg=None): 12 | if isinstance(args, IOError): 13 | myargs = [] 14 | myargs.extend([arg for arg in args]) 15 | else: 16 | myargs = list(args) 17 | 18 | if newarg: 19 | myargs.append(newarg) 20 | 21 | return tuple(myargs) 22 | 23 | 24 | def fileArgs(file, mode, args): 25 | if args[0] == errno.EACCES and \ 26 | 'access' in dir(os): 27 | perms = '' 28 | permd = {'r': os.R_OK, 'w': os.W_OK, 29 | 'x': os.X_OK} 30 | pkeys = permd.keys() 31 | pkeys.sort() 32 | pkeys.reverse() 33 | 34 | for eachPerm in 'rwx': 35 | if os.access(file, permd[eachPerm]): 36 | perms += eachPerm 37 | else: 38 | perms += '-' 39 | 40 | if isinstance(args, IOError): 41 | myargs = [] 42 | myargs.extend([arg for arg in args]) 43 | else: 44 | myargs = list(args) 45 | 46 | myargs[1] = "'%s' %s (perms: '%s')" % (mode, myargs[1], perms) 47 | myargs.append(args.filename) 48 | 49 | else: 50 | myargs = args 51 | 52 | return tuple(myargs) 53 | 54 | 55 | def myconnect(sock, host, port): 56 | try: 57 | sock.connect((host, port)) 58 | 59 | except socket.error, args: 60 | myargs = updArgs(args) # conv inst2tuple 61 | if len(myargs) == 1: # no #s on some errs 62 | myargs = (errno.ENXIO, myargs[0]) 63 | 64 | raise NetworkError, \ 65 | updArgs(myargs, host + ':' + str(port)) 66 | 67 | 68 | def myopen(file, mode='r'): 69 | try: 70 | fo = open(file, mode) 71 | except IOError, args: 72 | raise FileError, fileArgs(file, mode, args) 73 | 74 | return fo 75 | 76 | 77 | def testfile(): 78 | file = mktemp() 79 | f = open(file, 'w') 80 | f.close() 81 | 82 | for eachTest in ((0, 'r'), (0100, 'r'), 83 | (0400, 'w'), (0500, 'w')): 84 | try: 85 | os.chmod(file, eachTest[0]) 86 | f = myopen(file, eachTest[1]) 87 | 88 | except FileError, args: 89 | print "%s: %s" % \ 90 | (args.__class__.__name__, args) 91 | else: 92 | print file, "opened ok... perm ignored" 93 | f.close() 94 | 95 | os.chmod(file, 0777) # enable all perms 96 | os.unlink(file) 97 | 98 | 99 | def testnet(): 100 | s = socket.socket(socket.AF_INET, 101 | socket.SOCK_STREAM) 102 | 103 | for eachHost in ('deli', 'www'): 104 | try: 105 | myconnect(s, 'deli', 8080) 106 | except NetworkError, args: 107 | print "%s: %s" % \ 108 | (args.__class__.__name__, args) 109 | 110 | if __name__ == '__main__': 111 | testfile() 112 | testnet() 113 | -------------------------------------------------------------------------------- /10_Errors and Exceptions/note10_Errors and Exceptions.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 第10章介绍了什么是异常,如何生成异常,异常处理,Python对异常的支持,如何创建自定义的异常类、断言。 3 | 4 | 5 | # 知识点 6 | ## 10.1 什么是异常 7 | ### 10.1.1 错误 8 | 当 Python 检测到一个错误时,解释器会指出当前语句已经无法继续执行下去, 这时候就出现了异常。 9 | 10 | - **语法错误**:必须在编译前解决。 11 | - **逻辑错误**:运行时出现,如:非法的输入、边界值溢出等。 12 | 13 | 14 | ### 10.1.2 异常 15 | 异常是程序出现错误而在正常控制流以外采取的行为。 16 | 17 | 该行为分为**两个阶段**: 18 | 19 | 1. **产生异常**:当符合异常的产生条件时,解释器就会触发一个异常。异常可以是Python解释器触发的,也可以是coder手动触发的。 20 | 2. **处理异常**:异常引发后,指示程序如何执行,是忽略错误继续执行还是终止程序。 21 | 22 | 23 | **异常处理的好处**:使得程序具有在运行时管理错误的能力,在错误发生时采取可靠的补救措施。 24 | 25 | 26 | ## 10.2 Python 中的常见异常 27 | 28 | - `ZeroDivisionError`:除数为零 29 | - `NameError`:访问没有定义的变量 30 | - `SyntaxError`:唯一不是在运行时发生的异常,语法错误导致程序编译失败。 31 | - `IndexError`:请求的索引超出序列范围 32 | - `KeyError`:请求一个不存在的字典关键字 33 | - `IOError`:输入/输出错误 34 | - `AttributeError`:访问未知的对象属性 35 | 36 | 37 | ## 10.3 检测和处理异常 38 | 检测和处理异常有两种主要形式: `try-except` 和 `try-finally`。 39 | 40 | 41 | ### 10.3.1 try-except 语句 42 | ```python 43 | try: 44 | try_suite # watch for exceptions here 45 | except Exception[, reason]: 46 | except_suite # exception-handling code 47 | ``` 48 | 49 | `try-except`的处理流程: 50 | 51 | 1. 异常被引发后,`try` 语句块中异常发生点后的剩余语句不会被执行; 52 | 2. Python 解释器搜索对应的异常处理器,一旦找到就开始执行处理器中的代码; 53 | 3. 如果没有找到合适的异常处理器, 那么异常就自底向上移交给调用者去处理(最顶层也无法处理则退出程序)。 54 | 55 | 56 | ### 10.3.2 带有多个 `except` 的 `try` 语句 57 | ```python 58 | def safe_float(obj): 59 | try: 60 | retval = float(obj) 61 | except ValueError: 62 | retval = 'could not convert non-number to float' 63 | except TypeError: 64 | retval = 'object type cannot be converted to float' 65 | return retval 66 | ``` 67 | 68 | 69 | ### 10.3.3 同时处理多个异常的 `except` 语句 70 | ```python 71 | def safe_float(obj): 72 | try: 73 | retval = float(obj) 74 | except (ValueError, TypeError): 75 | retval = 'argument must be a number or numeric string' 76 | return retval 77 | ``` 78 | 79 | 80 | ### 10.3.4 异常的层次结构 81 | ``` 82 | |- BaseException 83 | |- KeyboardInterrupt 84 | |- SystemExit 85 | |- Exception 86 | |- (all other current built-in exceptions) 87 | ``` 88 | 89 | 90 | ### 10.3.5 异常参数 91 | ```python 92 | # multiple exceptions 93 | except (Exception1,..., ExceptionN)[, reason]: 94 | suite_for_Exception1_to_ExceptionN_with_Argument 95 | ``` 96 | 97 | 98 | 上面的`reason`是一个包含异常代码诊断信息的类**实例**,异常参数自身会组成一个元组并存储为`reason`的属性`args`。 99 | 100 | ------------ 101 | 102 | 获取异常的错误信息: 103 | ```python 104 | def safe_float(object): 105 | try: 106 | retval = float(object) 107 | except (ValueError, TypeError), e: 108 | retval = str(e) 109 | return retval 110 | ``` 111 | 112 | 113 | ### 10.3.6 `else` 子句 114 | 115 | `else`子句执行的条件:`try`范围中的所有代码没有引发异常。 116 | 117 | ```python 118 | try: 119 | module.function() 120 | except: 121 | print 'error' 122 | else: 123 | print 'success' 124 | ``` 125 | 126 | 127 | ### 10.3.7 `finally` 子句 128 | `finally` 子句:是无论是否有异常发生,都会执行的一段代码。 129 | 130 | 131 | ### 10.3.8 `try-finally` 语句 132 | ```python 133 | try: 134 | A 135 | finally: 136 | B 137 | ``` 138 | 当在 `try` 范围中产生一个异常时会立即跳转到 `finally` 语句,当 `finally` 语句执行完毕后会继续向上一层引发异常。 139 | 140 | 141 | ### 10.3.9 `try-except-else-finally` 语句 142 | ```python 143 | try: 144 | A 145 | except MyException: 146 | B 147 | else: 148 | C 149 | finally: 150 | D 151 | ``` 152 | 153 | 结果:A-C-D[正常] 或 A-B-D[异常] 154 | 155 | ### 10.3.10 `try`语句后跟子句的总结 156 | 157 | - 有`else`子句则一定要有`except`子句 158 | - `finally`子句前可以没有`except`或`else`子句 159 | 160 | 161 | ## 10.4 上下文管理 162 | ### 10.4.1 with 语句 163 | `with` 语句用来简化`try-except-finally`语句,仅工作于支持上下文管理协议(context management protocol)的对象。 164 | 165 | ```python 166 | with open('/etc/passwd', 'r') as f: 167 | for eachLine in f: 168 | # ...do stuff with eachLine or f... 169 | ``` 170 | ### 10.4.2 上下文管理协议 171 | 上下文管理器必须实现的方法: 172 | 173 | - `__context__()`:提供上下文对象 174 | - `__enter__()`:完成 with 语句块执行前的准备工作 175 | - `__exit__()`:当 with 语句块执行结束调用 176 | 177 | 178 | `contextlib` 模块的 `functions/decorators`,可以方便地创建上下文管理器。 179 | 180 | 181 | ## 10.5 字符串作为异常(过时) 182 | Python 1.5 前,标准的异常是基于字符串实现的,但是从现在起建议使用异常类。 183 | 184 | 185 | ## 10.6 触发异常 186 | 通过 `raise` 语句,可手动触发异常。 187 | 188 | 189 | 语法:`raise [SomeException [, args [, traceback]]]` 190 | - `SomeException`:字符串/异常类/实例 191 | - `args`:异常的参数是一个元组 192 | - `traceback`:用于exception—normally的追踪对象 193 | 194 | 195 | 例子: 196 | ```python 197 | try: 198 | raise IOError, 'raise test' 199 | except IOError, e: 200 | print e 201 | ``` 202 | 203 | 204 | ## 10.7 断言 205 | `assert`可用于触发异常:如果断言成功不采取任何措施,否则触发`AssertionError`。 206 | 207 | 208 | 语法:`assert expr[, args]` 209 | 210 | 实例: 211 | ```python 212 | try: 213 | assert 1 == 0, 'One does not equal zero silly!' 214 | except AssertionError, args: 215 | print '%s: %s' % (args.__class__.__name__, args) 216 | # AssertionError: One does not equal zero silly! 217 | ``` 218 | 219 | 220 | 断言的原理: 221 | ```python 222 | def assert(expr, args=None): 223 | if __debug__ and not expr: 224 | raise AssertionError, args 225 | ``` 226 | 227 | 228 | ## 10.8 标准异常 229 | 所有的标准/内建异常、自定义异常都是继承自根异常 `BaseException` 。 230 | (其他信息见 [10.2 Python中的常见异常](#10.2) 和 [10.3.4 异常的层次结构](#10.3.4)) 231 | 232 | 233 | ## 10.9 使用异常的目的与好处 234 | 235 | **目的:** 为了使程序足够健壮,可以处理理应用级别的错误(不至于灾难性地影响其执行环境),并提供用户级别的错误信息。 236 | 237 | **好处:** 异常不仅简化代码,而且简化整个错误管理体系。 238 | 239 | 240 | ## 10.10 异常和 sys 模块 241 | 通过 `sys` 模块中 `exc_info()` 获取**异常信息**。 242 | 243 | ```python 244 | import sys 245 | 246 | 247 | try: 248 | float('abc123') 249 | except: 250 | exc_tuple = sys.exc_info() 251 | print exc_tuple 252 | """ (, 253 | ValueError('could not convert string to float: abc123',), 254 | ) """ 255 | ``` 256 | 257 | 258 | `sys.exc_info()` 得到的元组是: 259 | 260 | - `exc_type`:异常类 261 | - `exc_value`:异常类的实例 262 | - `exc_traceback`:追踪对象,提供了发生异常的上下文,包含代码的执行帧、异常发生时的行号等信息。 263 | 264 | 265 | ## 10.11 异常相关的标准库 266 | - `exceptions`:内建异常 267 | - `contextlib`:使用 `with` 语句的上下文对象工具 268 | - `sys`:包含各种异常相关的对象和函数(见 `sys.ex*`) 269 | 270 | 271 | # 练习 272 | **10–1. 引发异常. 以下的哪个因素会在程序执行时引发异常? 注意这里我们问的并不是异常的原因. ** 273 | 274 | a) 用户 275 | b) 解释器 276 | c) 程序 277 | **d) 以上所有 ** 278 | e) 只有 b) 和 c) 279 | f) 只有 a) 和 c) 280 | 281 | 282 | **10–2. 引发异常. 参考上边问题的列表, 哪些因素会在运行交互解释器时引发异常?** 283 | 284 | I/O(文件不存在、读取错误)、系统错误、边界值溢出等。 285 | 286 | 287 | **10–3. 关键字. 用来引发异常的关键字有那些?** 288 | 289 | `raise`、`try` 290 | 291 | 292 | **10–4. 关键字. try-except 和 try-finally 有什么不同?** 293 | 294 | - `try-except`:当`try`代码块运行出错,`except`代码块才会被执行(匹配到相应异常时),处理完异常后`try-except`后的代码会继续往下执行; 295 | - `try-finally`:不管`try`代码块是否出现异常,最终都会执行`finally`里的代码。如果出现异常,执行完`finally`代码块后,异常会继续往上层抛。 296 | 297 | 298 | **10–5. 异常. 下面这些交互解释器下的 Python 代码段分别会引发什么异常(参阅表 10.2 给出的内建异常清单):** 299 | 300 | ``` 301 | (a) >>> if 3 < 4 then: print '3 IS less than 4!' 302 | (b) >>> aList = ['Hello', 'World!', 'Anyone', 'Home?'] 303 | >>> print 'the last string in aList is:', aList[len(aList)] 304 | (c) >>> x 305 | (d) >>> x = 4 % 0 306 | (e) >>> import math 307 | >>> i = math.sqrt(-1) 308 | ``` 309 | (a) `SyntaxError` 310 | (b) `IndexError` 311 | (c) `NameError` 312 | (d) `ZeroDivisionError` 313 | (e) `ValueError` 314 | 315 | 316 | **10–6. 改进的 open(). 为内建的 open() 函数创建一个封装. 使得成功打开文件后, 返回文件句柄; 若打开失败则返回给调用者 None , 而不是生成一个异常. 这样你打开文件时就不需要额外的异常处理语句.** 317 | 318 | ```python 319 | def myopen(path, access='r'): 320 | try: 321 | f = open(path, access) 322 | except (IOError, ValueError): 323 | return None 324 | return f 325 | 326 | 327 | if __name__ == '__main__': 328 | print myopen("test.txt", 'abc') 329 | ``` 330 | 331 | 332 | **10–7. 异常. 下面两段 Python 伪代码 a) 和 b) 有什么区别? 考虑语句 A 和 B 的上下文环境.** 333 | 334 | ```python 335 | (a) try: 336 | statement_A 337 | except . . .: 338 | . . . 339 | else: 340 | statement_B 341 | 342 | 343 | (b) try: 344 | statement_A 345 | statement_B 346 | except . . .: 347 | . . . 348 | ``` 349 | 350 | **情况1:** 如果`statement_A`执行出错,则(a)和(b)都不会执行`statement_B`; 351 | 352 | **情况2:** 如果`statement_A`执行正常,则(a)和(b)都会执行`statement_B`——**但是**,如果`statement_B`的执行也出错,(b)可以在`except`子句里捕获到异常并做处理,而(a)则只能将`statement_B`产生的异常抛给上层调用者。 353 | 354 | 355 | **10–8. 改进的 raw_input() . 本章的开头, 我们给出了一个"安全"的 float() 函数, 它建立在内建函数 float() 上, 可以检测并处理 float() 可能会引发的两种不同异常. 同样, raw_input() 函数也可能会生成两种异常, EOFError (文件末尾 EOF, 在 Unix 下是由于按下了 Ctrl+D 在 Dos 下是因为 Ctrl+Z) 或 是 KeyboardInterrupt (取消输入 , 一 般是由于按下了Ctrl+C). 请创建一个封装函数 safe_input() , 在发生异常时返回 None .** 356 | ```python 357 | def safe_input(msg): 358 | data = None 359 | try: 360 | data = raw_input(msg) 361 | except (EOFError, KeyboardInterrupt): 362 | pass 363 | return data 364 | 365 | 366 | if __name__ == '__main__': 367 | print safe_input("please input: ") 368 | ``` 369 | 370 | 371 | **10–9. 改进的 math.sqrt(). math 模块包含大量用于处理数值相关运算的函数和常量. 不幸的是, 它不能识别复数, 所以我们创建了 cmath 模块来支持复数相关运算. 请创建一个 safe_sqrt() 函数, 它封装 math.sqrt() 并能处理负值, 返回一个对应的复数.** 372 | 373 | ```python 374 | import math, cmath 375 | 376 | 377 | def safe_sqrt(num): 378 | try: 379 | result = math.sqrt(num) 380 | except ValueError: 381 | result = cmath.sqrt(num) 382 | return result 383 | 384 | 385 | if __name__ == '__main__': 386 | print safe_sqrt(123) 387 | print safe_sqrt(-123) 388 | ``` 389 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11-12_timeit.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def timeit(func, *nkwargs, **kwargs): 4 | begin = time.clock() 5 | try: 6 | retval = func(*nkwargs, **kwargs) 7 | result = (True, retval, time.clock()-begin) 8 | except Exception, diag: 9 | result = (False, str(diag)) 10 | return result 11 | 12 | def test(): 13 | funcs = (int, long, float) 14 | vals = (1234, 12.34, '1234', '12.34') 15 | 16 | for eachFunc in funcs: 17 | print '-' * 20 18 | for eachVal in vals: 19 | retval = timeit(eachFunc, eachVal) 20 | if retval[0]: 21 | print '%s(%s) =' % \ 22 | (eachFunc.__name__, `eachVal`), retval[1] 23 | print 'cost time(s): %s' % `retval[2]` 24 | else: 25 | print '%s(%s) = FAILED:' % \ 26 | (eachFunc.__name__, `eachVal`), retval[1] 27 | 28 | if __name__ == '__main__': 29 | test() 30 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11-13_mult_reduce_lambda.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def iterator_N(N): 4 | result = 1 5 | for n in range(1, N+1): 6 | result *= n 7 | return result 8 | 9 | def reduce_N(N): 10 | return reduce(lambda x, y : x * y, range(1, N+1)) 11 | 12 | def recursive_N(N): 13 | if N == 0 or N == 1: 14 | return 1 15 | else: 16 | return N * recursive_N(N-1) 17 | 18 | def timeit(func, *nkwargs, **kwargs): 19 | begin = time.clock() 20 | try: 21 | retval = func(*nkwargs, **kwargs) 22 | result = (True, retval, time.clock()-begin) 23 | except Exception, diag: 24 | result = (False, str(diag)) 25 | return result 26 | 27 | if __name__ == '__main__': 28 | funcs = (iterator_N, reduce_N, recursive_N) 29 | N = 4 30 | for func in funcs: 31 | retval = timeit(func, N) 32 | if retval[0]: 33 | print '%s(%s) =' % \ 34 | (func.__name__, `N`), retval[1] 35 | print 'cost time(s): %s' % `retval[2]` 36 | else: 37 | print '%s(%s) = FAILED:' % \ 38 | (func.__name__, `N`), retval[1] -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11-15_backward_forward.py: -------------------------------------------------------------------------------- 1 | def backward(s,i=0): 2 | if i < len(s): 3 | print s[0:i+1], 4 | backward(s,i+1) 5 | 6 | def forward(s,j=0): 7 | if j > -len(s): 8 | print s[j-1:], 9 | forward(s,j-1) 10 | 11 | if __name__=='__main__': 12 | backward('abcdefg') 13 | print 14 | forward('abcdefg') -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11-16_easyMath2.py: -------------------------------------------------------------------------------- 1 | from operator import add, sub, mul, div 2 | from random import randint, choice 3 | 4 | ops = {'+': add, '-': sub, '*': mul, '/':div} 5 | MAXTRIES = 2 6 | 7 | def doprob(): 8 | op = choice('+-*/') 9 | nums = [randint(1, 10) for i in range(2)] 10 | nums.sort(reverse=True) 11 | 12 | if (op == '/'): 13 | while nums[0] % nums[1] != 0: 14 | nums = [randint(1, 10) for i in range(2)] 15 | nums.sort(reverse=True) 16 | 17 | ans = ops[op](*nums) 18 | pr = '%d %s %d = ' % (nums[0], op, nums[1]) 19 | oops = 0 20 | while True: 21 | try: 22 | if int(raw_input(pr)) == ans: 23 | print 'correct' 24 | break 25 | if oops == MAXTRIES: 26 | print 'answer\n%s%d' % (pr, ans) 27 | else: 28 | print 'incorrect... try again' 29 | oops += 1 30 | except (KeyboardInterrupt, \ 31 | EOFError, ValueError): 32 | print 'invalid input... try again' 33 | 34 | 35 | def main(): 36 | while True: 37 | doprob() 38 | try: 39 | opt = raw_input('Again? [y]').lower() 40 | if opt and opt[0] == 'n': 41 | break 42 | except (KeyboardInterrupt, EOFError): 43 | break 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.1_easyMath.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from operator import add, sub 4 | from random import randint, choice 5 | 6 | ops = {'+': add, '-': sub} 7 | MAXTRIES = 2 8 | 9 | 10 | def doprob(): 11 | op = choice('+-') 12 | nums = [randint(1, 10) for i in range(2)] 13 | nums.sort(reverse=True) 14 | ans = ops[op](*nums) 15 | pr = '%d %s %d = ' % (nums[0], op, nums[1]) 16 | oops = 0 17 | while True: 18 | try: 19 | if int(raw_input(pr)) == ans: 20 | print 'correct' 21 | break 22 | if oops == MAXTRIES: 23 | print 'answer\n%s%d' % (pr, ans) 24 | else: 25 | print 'incorrect... try again' 26 | oops += 1 27 | except (KeyboardInterrupt, \ 28 | EOFError, ValueError): 29 | print 'invalid input... try again' 30 | 31 | 32 | def main(): 33 | while True: 34 | doprob() 35 | try: 36 | opt = raw_input('Again? [y]').lower() 37 | if opt and opt[0] == 'n': 38 | break 39 | except (KeyboardInterrupt, EOFError): 40 | break 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.2_deco.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from time import ctime, sleep 4 | 5 | def tsfunc(func): 6 | def wrappedFunc(): 7 | print '[%s] %s() called' % (ctime(), func.__name__) 8 | return func() 9 | return wrappedFunc 10 | 11 | @tsfunc 12 | def foo(): 13 | print 'foo' 14 | 15 | foo() 16 | sleep(4) 17 | 18 | for i in range(2): 19 | sleep(1) 20 | foo() 21 | 22 | ''' result: 23 | [Mon Apr 04 09:52:48 2016] foo() called 24 | foo 25 | [Mon Apr 04 09:52:53 2016] foo() called 26 | foo 27 | [Mon Apr 04 09:52:54 2016] foo() called 28 | foo 29 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.2_deco2.py: -------------------------------------------------------------------------------- 1 | from time import ctime, sleep 2 | 3 | def tsfunc(func): 4 | print '[%s] %s() call start...' % (ctime(), func.__name__) 5 | func() 6 | print '[%s] %s() call end ...' % (ctime(), func.__name__) 7 | 8 | def foo(): 9 | print 'foo' 10 | 11 | foo() 12 | sleep(1) 13 | 14 | for i in range(2): 15 | sleep(1) 16 | tsfunc(foo) 17 | 18 | ''' result: 19 | foo 20 | [Mon Apr 04 10:05:44 2016] foo() call start... 21 | foo 22 | [Mon Apr 04 10:05:44 2016] foo() call end ... 23 | [Mon Apr 04 10:05:45 2016] foo() call start... 24 | foo 25 | [Mon Apr 04 10:05:45 2016] foo() call end ... 26 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.3_numConv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def convert(func, seq): 4 | 'conv. sequence of numbers to same type' 5 | return [func(eachNum) for eachNum in seq] 6 | 7 | myseq = (123, 45.67, -6.2e8, 999999999L) 8 | print convert(int, myseq) 9 | print convert(long, myseq) 10 | print convert(float, myseq) 11 | 12 | ''' result: 13 | [123, 45, -620000000, 999999999] 14 | [123L, 45L, -620000000L, 999999999L] 15 | [123.0, 45.67, -620000000.0, 999999999.0] 16 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.4_grabWeb.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from urllib import urlretrieve 3 | 4 | def firstNonBlank(lines): 5 | for eachLine in lines: 6 | if not eachLine.strip(): 7 | continue 8 | else: 9 | return eachLine 10 | 11 | def firstLast(webpage): 12 | f = open(webpage) 13 | lines = f.readlines() 14 | f.close() 15 | print firstNonBlank(lines), 16 | lines.reverse() 17 | print firstNonBlank(lines), 18 | 19 | 20 | def download(url='http://www.baidu.com', process=firstLast): 21 | try: 22 | retval = urlretrieve(url)[0] 23 | except IOError: 24 | retval = None 25 | 26 | print retval 27 | if retval: # do some processing 28 | process(retval) 29 | 30 | 31 | if __name__ == '__main__': 32 | download() 33 | 34 | ''' result: 35 | c:\users\seven\appdata\local\temp\tmpghbwf6 36 | 百度一下,你就知道 37 | if(window.__sample_dynamic_tab){$("#s_tab").remove()}})}if(!d.match(/(msie 6)/i)){$(function(){setTimeout(function(){$.ajax({url:"http://s1.bdstatic.com/r/www/cache/static/baiduia/baiduia_b45d552b.js",cache:true,dataType:"script"})},0)})}if(bds.comm&&bds.comm.ishome&&Cookie.get("H_PS_PSSID")){bds.comm.indexSid=Cookie.get("H_PS_PSSID")}})(); 38 | ''' 39 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.5_testit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def testit(func, *nkwargs, **kwargs): 4 | try: 5 | retval = func(*nkwargs, **kwargs) 6 | result = (True, retval) 7 | except Exception, diag: 8 | result = (False, str(diag)) 9 | return result 10 | 11 | def test(): 12 | funcs = (int, long, float) 13 | vals = (1234, 12.34, '1234', '12.34') 14 | 15 | for eachFunc in funcs: 16 | print '-' * 20 17 | for eachVal in vals: 18 | retval = testit(eachFunc, eachVal) 19 | if retval[0]: 20 | print '%s(%s) =' % \ 21 | (eachFunc.__name__, `eachVal`), retval[1] 22 | else: 23 | print '%s(%s) = FAILED:' % \ 24 | (eachFunc.__name__, `eachVal`), retval[1] 25 | 26 | if __name__ == '__main__': 27 | test() 28 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.6_pfaGUI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from functools import partial 4 | import Tkinter 5 | 6 | root = Tkinter.Tk() 7 | MyButton = partial(Tkinter.Button, root, fg='white', bg='blue') 8 | b1 = MyButton(text='Button 1') 9 | b2 = MyButton(text='Button 2') 10 | qb = MyButton(text='QUIT', bg='red', command=root.quit) 11 | b1.pack() 12 | b2.pack() 13 | qb.pack(fill=Tkinter.X, expand=True) 14 | root.title('PFAs!') 15 | root.mainloop() 16 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.7_closureVars.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | output = '' 5 | w = x = y = z = 1 6 | 7 | def f1(): 8 | # 局部变量,不是全局变量 9 | x = y = z = 2 10 | 11 | def f2(): 12 | y = z = 3 13 | 14 | def f3(): 15 | z = 4 16 | print output % ('w', id(w), w) 17 | print output % ('x', id(x), x) 18 | print output % ('y', id(y), y) 19 | print output % ('z', id(z), z) 20 | 21 | clo = f3.func_closure 22 | if clo: 23 | print "f3 closure vars:", [str(c) for c in clo] 24 | else: 25 | print "no f3 closure vars" 26 | f3() 27 | 28 | clo = f2.func_closure 29 | if clo: 30 | print "f2 closure vars:", [str(c) for c in clo] 31 | else: 32 | print "no f2 closure vars" 33 | f2() 34 | 35 | # 定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量,使用函数的 func_closure 属性来追踪自由变量。 36 | # 如果 f2()使用了任何的定义在 f1()作用域的变量, 比如说, 非全局的和非 f2()的局部域的, 那么它们便是自由变量, 将会被 f1.func_closure 追踪到。 37 | clo = f1.func_closure 38 | if clo: 39 | print "f1 closure vars:", [str(c) for c in clo] 40 | else: 41 | print "no f1 closure vars" 42 | f1() 43 | 44 | ''' 45 | 假设函数 f3()已经被传入到其他一些函数,这样便可在稍后,甚至是 f2()完成 之后,调用它。 46 | 你不想要让 f2()的栈出现,因为即使我们仅仅在乎 f3()使用的自由变量,栈也会让 所有的 f2()'s 的变量保持存活。 47 | 单元维持住自由变量以便 f2()的剩余部分能被释放掉。 48 | ''' 49 | 50 | ''' 51 | 在 f3(), f2(), 或者 f1()中都是找不到变量 w 的,所以,这是个全局变量。 52 | 在 f3()或者 f2()中,找不到变量 x,所以来自 f1()的闭包变量。 53 | 相似地,y 是一个来自 f2() 的闭包变量。最后,z 是 f3()的局部变量。 54 | ''' 55 | 56 | ''' 57 | no f1 closure vars 58 | f2 closure vars: [''] 59 | f3 closure vars: ['', ''] 60 | 61 | 62 | 63 | 64 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.8_funcLog.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from time import time 4 | 5 | def logged(when): 6 | def log(f, *args, **kargs): 7 | print '''Called: 8 | function: %s 9 | args: %r 10 | kargs: %r''' % (f, args, kargs) 11 | 12 | def pre_logged(f): 13 | # wrapper是闭包 14 | def wrapper(*args, **kargs): 15 | log(f, *args, **kargs) 16 | return f(*args, **kargs) 17 | return wrapper 18 | 19 | def post_logged(f): 20 | # wrapper是闭包 21 | def wrapper(*args, **kargs): 22 | now = time() 23 | try: 24 | return f(*args, **kargs) 25 | finally: 26 | log(f, *args, **kargs) 27 | print "time delta: %s" % (time()-now) 28 | return wrapper 29 | 30 | try: 31 | return {"pre": pre_logged, 32 | "post": post_logged}[when] 33 | except KeyError, e: 34 | raise ValueError(e), 'must be "pre" or "post"' 35 | 36 | @logged("post") 37 | def hello(name): 38 | print "Hello,", name 39 | 40 | hello("World!") 41 | 42 | ''' 43 | Hello, World! 44 | Called: 45 | function: 46 | args: ('World!',) 47 | kargs: {} 48 | time delta: 0.0 49 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/11.9_scope.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | j, k = 1, 2 3 | 4 | def proc1(): 5 | j, k = 3, 4 6 | print "j == %d and k == %d" % (j, k) 7 | k = 5 8 | 9 | def proc2(): 10 | j = 6 11 | proc1() 12 | print "j == %d and k == %d" % (j, k) 13 | 14 | k = 7 15 | proc1() 16 | print "j == %d and k == %d" % (j, k) 17 | 18 | j = 8 19 | proc2() 20 | print "j == %d and k == %d" % (j, k) 21 | 22 | ''' 23 | j == 3 and k == 4 24 | j == 1 and k == 7 25 | j == 3 and k == 4 26 | j == 6 and k == 7 27 | j == 8 and k == 7 28 | ''' -------------------------------------------------------------------------------- /11_Functions and Functional Programming/dest.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from operator import add, sub 3 | from random import randint, choice 4 | ops = {'+': add, '-': sub} 5 | MAXTRIES = 2 6 | def doprob(): 7 | op = choice('+-') 8 | nums = [randint(1, 10) for i in range(2)] 9 | nums.sort(reverse=True) 10 | ans = ops[op](*nums) 11 | pr = '%d %s %d = ' % (nums[0], op, nums[1]) 12 | oops = 0 13 | while True: 14 | try: 15 | if int(raw_input(pr)) == ans: 16 | print 'correct' 17 | break 18 | if oops == MAXTRIES: 19 | print 'answer\n%s%d' % (pr, ans) 20 | else: 21 | print 'incorrect... try again' 22 | oops += 1 23 | except (KeyboardInterrupt, \ 24 | EOFError, ValueError): 25 | print 'invalid input... try again' 26 | def main(): 27 | while True: 28 | doprob() 29 | try: 30 | opt = raw_input('Again? [y]').lower() 31 | if opt and opt[0] == 'n': 32 | break 33 | except (KeyboardInterrupt, EOFError): 34 | break 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /11_Functions and Functional Programming/note11_Functions and Functional Programming.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 介绍函数的创建、调用方式,内部函数、函数装饰器、函数参数的定义和传递、函数式编程、变量作用域、闭包。 3 | 4 | # 知识点 5 | 6 | ## 11.1 什么是函数? 7 | 8 | - 函数是对程序逻辑进行结构化或过程化的一种编程方法,以实现代码的复用。 9 | - python 的过程就是函数,因为解释器会隐式地返回默认值 `None`。 10 | - python 动态地确定函数返回类型,而不是进行直接的类型关联。 11 | - 可以使用 `type()` 函数作为代理,处理有不同参数类型的函数的多重声明,以模拟其他编程语言的函数重载。 12 | 13 | ## 11.2 调用函数 14 | 15 | ### 11.2.1 关键字参数 16 | 解释器能通过给出的关键字来匹配参数的值。 17 | 18 | ### 11.2.2 参数组 19 | `func(*tuple_grp_nonkw_args, **dict_grp_kw_args)`:`tuple_grp_nonkw_args` 是以元组形式体现的非keyword参数组, `dict_grp_kw_args` 是装有keyword参数的字典。 20 | 21 | ### 11.3 创建函数 22 | 先声明 `foo()`,再声明` bar()`,接着调用 `foo()`(foo()里调用bar()),而`bar()`已经存在了所以调用成功。 23 | 24 | ### 11.3.1 函数属性 25 | ```python 26 | def bar(): 27 | pass 28 | 29 | bar.__doc__ = 'Oops, forgot the doc str above' 30 | bar.version = 0.1 31 | 32 | print bar.__doc__ 33 | print bar.version 34 | ``` 35 | 36 | ### 11.3.2 内部/内嵌函数 37 | **内部函数:** python 支持静态地嵌套域,可以在函数体内创建另外一个函数。 38 | 39 | 创建方式: 40 | ```python 41 | def foo(): 42 | def bar(): 43 | print 'bar() called' 44 | 45 | print 'foo() called' 46 | bar() 47 | 48 | foo() 49 | ``` 50 | 51 | ### 11.3.3 函数装饰器 52 | - 装饰器是在函数调用之上的修饰,当仅当声明一个函数的时候,才会被额外调用。 53 | - 装饰器类似于Java的注解,是AOP(Aspect Oriented Programming,面向切面编程)的思想。 54 | 55 | ```python 56 | @deco1(deco_arg) 57 | @deco2 58 | def func(): pass 59 | ``` 60 | = `func = deco1(deco_arg)(deco2(func))` 61 | 62 | 63 | **用途:** 64 | 65 | - 引入日志 66 | - 增加计时逻辑来检测性能 67 | - 给函数加入事务的能力 68 | 69 | ## 11.4 传递函数 70 | ```python 71 | def bar(argfunc): 72 | argfunc() 73 | 74 | bar(foo) 75 | ``` 76 | 77 | ## 11.5 形式参数 78 | ### 11.5.1 位置参数 79 | 位置参数必须以在被调用函数中定义的准确顺序来传递。 80 | 81 | ### 11.5.2.默认参数 82 | 所有的位置参数必须出现在任何一个默认参数之前。 83 | 84 | ```python 85 | >>> def taxMe(cost, rate=0.0825): 86 | ... return cost + (cost * rate) 87 | >>> taxMe(100) 88 | 108.25 89 | >>> taxMe(100, 0.05) 90 | 105.0 91 | ``` 92 | 93 | 94 | ## 11.6 可变长度的参数 95 | 在函数调用中使用`*`和`**`符号,允许函数接收在函数声明中定义的形参之外的参数。 96 | 97 | ### 11.6.1.非关键字可变长参数(元组) 98 | ```python 99 | def tupleVarArgs(arg1, arg2='defaultB', *theRest): 100 | 'display regular args and non-keyword variable args' 101 | print 'formal arg 1:', arg1 102 | print 'formal arg 2:', arg1 103 | for eachXtrArg in theRest: 104 | print 'another arg:', eachXtrArg 105 | 106 | >>> tupleVarArgs('abc', 123, 'xyz', 456.789) 107 | formal arg 1: abc 108 | formal arg 2: 123 109 | another arg: xyz 110 | another arg: 456.789 111 | ``` 112 | 113 | ### 11.6.2.关键字变量参数(Dictionary) 114 | 关键字变量参数必须为函数定义的最后一个参数:`def function_name([formal_args,][*vargst,] **vargsd)` 115 | 116 | ```python 117 | def newfoo(arg1, arg2, *nkw, **kw): 118 | 'display regular args and all variable args' 119 | print 'arg1 is:', arg1 120 | print 'arg2 is:', arg2 121 | for eachNKW in nkw: 122 | print 'additional non-keyword arg:', eachNKW 123 | for eachKW in kw.keys(): 124 | print "additional keyword arg '%s': %s" % \ 125 | (eachKW, kw[eachKW]) 126 | 127 | newfoo('wolf', 3, 'projects', freud=90, gamble=96) 128 | ''' 129 | arg1 is: wolf 130 | arg2 is: 3 131 | additional non-keyword arg: projects 132 | additional keyword arg 'gamble': 96 133 | additional keyword arg 'freud': 90 134 | ''' 135 | ``` 136 | 137 | ### 11.6.3 调用带有可变长参数对象函数 138 | ```python 139 | aTuple = (6, 7, 8) 140 | aDict = {'z': 9} 141 | newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict) 142 | ''' 143 | arg1 is: 1 144 | arg2 is: 2 145 | additional non-keyword arg: 3 146 | additional non-keyword arg: 6 147 | additional non-keyword arg: 7 148 | additional non-keyword arg: 8 149 | additional keyword arg 'y': 5 150 | additional keyword arg 'x': 4 151 | additional keyword arg 'z': 9 152 | ''' 153 | ``` 154 | 155 | ## 11.7 函数式编程 156 | ### 11.7.1 匿名函数与 lambda 157 | - 用 lambda 关键字创造匿名函数。 158 | - **lambda 表达式的目的在于优化性能,在调用时绕过函数的栈分配。** 159 | 160 | ```python 161 | a = lambda x, y=2 : x+y 162 | 163 | >>> a(3) 164 | 5 165 | >>> a(3,4) 166 | 7 167 | ``` 168 | ### 11.7.2 内建函数 apply()、filter()、map()、reduce() 169 | 170 | #### 11.7.2.1 apply() 171 | `apply()`在1.6版本以前用来支持函数的可变参数,已过时。 172 | 173 | #### 11.7.2.2 filter() 174 | `filter(bool_func, seq)`: `bool_func`为序列`seq`的每个元素调用所给定的布尔函数,将每次 filter 返回的非零(true)值元素添加到返回列表中。 175 | 176 | ```python 177 | def odd(n): 178 | return n % 2 179 | filter(odd, allNums) 180 | ``` 181 | = `filter(lambda n : n % 2, allNums)` 182 | = `[n for n in allNums if n%2]` 183 | = 184 | ```python 185 | >>> from random import randint as ri 186 | >>> print [n for n in [ri(1,99) for i in range(9)] if n%2] 187 | [39, 7, 51, 83] 188 | ``` 189 | 190 | #### 11.7.2.3 map() 191 | `map()` 将函数调用“映射”到每个序列的元素上,并返回一个含有所有返回值的列表。 192 | 193 | **`map()` 与单个序列:** 194 | ```python 195 | >>> map((lambda x: x+2), [0, 1, 2, 3, 4, 5]) 196 | [2, 3, 4, 5, 6, 7] 197 | >>> map(lambda x: x**2, range(6)) 198 | [0, 1, 4, 9, 16, 25] 199 | >>>[x**2 for x in range(6)] 200 | [0, 1, 4, 9, 16, 25] 201 | ``` 202 | 203 | **`map()` 与多个序列:** 204 | ```python 205 | >>> map(lambda x, y: x + y, [1,3,5], [2,4,6]) 206 | [3, 7, 11] 207 | >>> 208 | >>> map(lambda x, y: (x+y, x-y), [1,3,5], [2,4,6]) 209 | [(3, -1), (7, -1), (11, -1)] 210 | >>> 211 | >>> map(None, [1,3,5], [2,4,6]) 212 | [(1, 2), (3, 4), (5, 6)] 213 | >>> 214 | >>> zip([1,3,5], [2,4,6]) 215 | [(1, 2), (3, 4), (5, 6)] 216 | ``` 217 | 218 | #### 11.7.2.4 reduce() 219 | `reduce()`通过取出序列的头两个元素,将他们传入二元函数来获得一个单一的值来实现。 220 | 221 | `reduce(func, [1, 2, 3])` <=> `func(func(1, 2), 3)` 222 | 223 | ```python 224 | >>> reduce((lambda x,y: x+y), range(5)) 225 | 10 226 | ``` 227 | 228 | ### 11.7.3 偏函数应用 229 | - Currying概念将函数式编程的概念和默认参数以及可变参数结合在一起。一个带 n 个参数, curried 的函数固化第一个参数为固定参数, 并返回另一个带 n-1 个参数函数对象。 230 | - Currying 能泛化成为偏函数应用(PFA:Partial Function Application), 这种函数将任意数量(顺序)的参数的函数转化成另一个带剩余参数的函数对象。 231 | - PFA 是在 python2.5 的时候被引入的,通过 functools 模块能很好的给用户调用。 232 | 233 | ```python 234 | >>> from operator import add, mul 235 | >>> from functools import partial 236 | >>> add1 = partial(add, 1) # add1(x) == add(1, x) 237 | >>> mul100 = partial(mul, 100) # mul100(x) == mul(100, x) 238 | >>> 239 | >>> add1(10) 240 | 11 241 | >>> mul100(10) 242 | 1000 243 | ``` 244 | 245 | **将二进制字符串转换成为整数:** `baseTwo(x) == int(x, base=2)` 246 | ```python 247 | >>> baseTwo = partial(int, base=2) 248 | >>> baseTwo.__doc__ = 'Convert base 2 string to an int.' 249 | >>> baseTwo('10010') 250 | 18 251 | ``` 252 | 253 | ## 11.8 变量作用域 254 | ### 11.8.1 全局变量与局部变量 255 | - 当搜索一个变量名的时候,Python解释器先从局部作用域开始搜索; 256 | - 如果在局部作用域内没有找到改变量名,就会在全局域搜索这个变量名; 257 | - 如果在全局域也找不到则抛出 `NameError` 异常。 258 | 259 | ### 11.8.2 global 语句 260 | `global` 将局部变量变为全局变量。 261 | ```python 262 | >>> is_this_global = 'xyz' 263 | >>> def foo(): 264 | ... global is_this_global 265 | ... this_is_local = 'abc' 266 | ... is_this_global = 'def' 267 | ... print this_is_local + is_this_global 268 | ... 269 | >>> foo() 270 | abcdef 271 | >>> print is_this_global 272 | def 273 | ``` 274 | 275 | 276 | ### 11.8.3 闭包 277 | - 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量存在进行引用,那么内部函数就被认为是 closure。 278 | - 定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量,使用函数的 func_closure 属性来追踪自由变量。 279 | - closurs 多用于 GUI 或者在很多 API 支持回调函数的事件驱动编程中。回调是函数,闭包也是函数,但是他们能携带一些额外的作用域。 280 | - 对自由变量的引用是存储在cell对象里(cell是在作用域结束后使自由变量的引用存活的一种基础方法) 281 | 282 | ```python 283 | def counter(start_at=0): 284 | count = [start_at] 285 | def incr(): 286 | count[0] += 1 287 | return count[0] 288 | return incr 289 | count = counter(5) 290 | print count() 291 | print count() 292 | 293 | count2 = counter(100) 294 | print count2() 295 | print count() 296 | 297 | ''' result: 298 | 6 299 | 7 300 | 101 301 | 8 302 | ''' 303 | ``` 304 | 305 | ### 11.8.4 作用域和 lambda 306 | ```python 307 | x = 10 308 | def foo(): 309 | y = 5 310 | bar = lambda z:x+z 311 | print bar(y) # 15 312 | y = 8 313 | print bar(y) # 18 314 | foo() 315 | ``` 316 | 317 | ## 11.9 递归 318 | ```python 319 | def factorial(n): 320 | if n == 0 or n == 1: # 0! = 1! = 1 321 | return 1 322 | else: 323 | return (n * factorial(n-1)) 324 | ``` 325 | 326 | ## 11.10 生成器 327 | - 挂起返回出中间值并多次继续的协同程序被称为生成器。 328 | - 当等待一个生成器的时候,生成器能立刻返回控制。 329 | - 在调用的生成器能挂起(返回一个结果)之前,调用生成器返回一个结果而不是阻塞等待那个结果返回。 330 | - 那就是 yield 语句返回一个值给调用者并暂停执行。当生成器的 next()方法被调用的时候,它会准确地从离开地方继续。 331 | 332 | ### 11.10.1 简单的生成器 333 | ```python 334 | from random import randint 335 | def randGen(aList): 336 | while len(aList) > 0: 337 | yield aList.pop(randint(0, len(aList))) 338 | 339 | for item in randGen(['rock', 'paper', 'scissors']): 340 | print item 341 | 342 | ''' 343 | scissors 344 | rock 345 | paper 346 | ''' 347 | ``` 348 | 349 | ### 11.10.2 加强的生成器特性 350 | 在 python2.5 中,一些加强特性加入到生成器中,除了 `next()`来获得下个生成的值,还可以将值回送`send()`给生成器,以及要求生成器退出`close()`。 351 | 352 | ```python 353 | def counter(start_at=0): 354 | count = start_at 355 | while True: 356 | val = (yield count) 357 | if val is not None: 358 | count = val 359 | else: 360 | count += 1 361 | 362 | >>> count = counter(5) 363 | >>> count.next() 364 | 5 365 | >>> count.next() 366 | 6 367 | >>> count.send(9) 368 | 9 369 | >>> count.next() 370 | 10 371 | >>> count.close() 372 | >>> count.next() 373 | Traceback (most recent call last): 374 | File "", line 1, in 375 | StopIteration 376 | ``` 377 | 378 | # 练习 379 | **11–1.参数。比较下面 3 个函数:** 380 | 381 | *略。* 382 | 383 | **11-2.函数。结合你对练习 5-2 的解,以便你创建一个带相同对数字并同时返回一它们之和以及产物的结合函数。** 384 | 385 | *略。* 386 | 387 | **11-3 函数。在这个练习中,我们将实现 max()和 min()内建函数。** 388 | 389 | (a) 写一个带两个元素的 max2() 和 min2()函数,分别返回一个较大和较小元素。举例来说,max2(4,8)和 min2(4,8)会各自每次返回 8 和 4。 390 | 391 | (b) 创建使用了在 a 部分中的解来重构 max()和 min()的新函数 my_max()和 my_min().这些函数分别返回非空队列中一个最大和最小值。它们也能带一个参数集合作为输入。用数字和字符串来测试你的解。 392 | 393 | ```python 394 | def max2(num1, num2): 395 | return num1 if num1 >= num2 else num2 396 | 397 | def my_max(seq): 398 | return reduce(max2, seq) 399 | 400 | print my_max([1,5,2,7,3]) 401 | ``` 402 | 403 | **11–4. 返回值。给你在 5-13 的解创建一个补充函数。创建一个带以分为单位的总时间以及返回一个以小时和分为单位的等价的总时间。** 404 | 405 | *略。* 406 | 407 | **11–5. 默认参数。更新你在练习 5-7 中创建的销售税脚本以便让销售税率不再是函数输入的必要之物。 创建使用你地方税率的默认参数如果在调用的时候没有值传入。** 408 | 409 | *略。* 410 | 411 | **11–6. 变长参数。下一个称为 printf()的函数。有一个值参数,格式字符串。剩下的就是根据格式化字符串上的值,要显示在标准输出上的可变参数,格式化字符串中的值允许特别的字符串格式操作指示符,如%d, %f, etc。提示:解是很琐碎的—-无需实现字符串操作符功能性,但你需要显示用字符串格式化操作(%)** 412 | 413 | *略。* 414 | 415 | **11-7. 用map()进行函数式编程。给定一对同一大小的列表,如[1,2,3]和[‘abc’,’def,‘ghi’, …,将两个标归并为一个由每个列表元素组成的元组的单一的表,以使我们的结果看起来像这样:{[(1,‘abc’), (2,‘def’), (3,‘ghi’), …]。(虽然这问题在本质上和第6章的一个问题相似,那时两个解没有直接的联系)然后创建用zip内建函数创建另一个解。** 416 | 417 | ```python 418 | print map(None, [1,2,3], ['abc','def','ghi']) 419 | print zip([1,2,3], ['abc','def','ghi']) 420 | ``` 421 | 422 | **11–8. 用 filer()进行函数式编程.使用练习 5-4 你给出的代码来决定闰年。更新你的代码一 边他成为一个函数如果你还没有那么做的话。然后写一段代码来给出一个年份的列表并返回一个只 有闰年的列表。然后将它转化为用列表解析。** 423 | 424 | ```python 425 | years = [1900,2000,2004] 426 | print filter(lambda year:(year%4==0 and year%100!=0) or (year%400==0),years) 427 | print [year for year in years if (year%4==0 and year%100!=0) or (year%400==0)] 428 | ``` 429 | 430 | **11–9. 用 reduce()进行函数式编程。复习 11.7.2 部分,阐述如何用 reduce()数字集合的累加的代码。修改它,创建一个叫 average()的函数来计算每个数字集合的简单的平均值。** 431 | 432 | ```python 433 | def average(arr): 434 | return reduce((lambda x,y : x+y), arr) / float(len(arr)) 435 | 436 | print average([1,23,4,3]) 437 | ``` 438 | 439 | **11–10.用 filter()进行函数式编程。在 unix 文件系统中,在每个文件夹或者目录中都有两个 特别的文件:'.'表示现在的目录,'..'表示父目录。给出上面的知识,看下 os.listdir()函数的文档并描述这段代码做了什么:`files = filter(lambda x: x and x[0] != '.', os. listdir(folder))`** 440 | 441 | 列出folder目录下所有非隐藏文件(文件名以`.`开头) 442 | 443 | **11–11.用 map()进行函数式编程。写一个使用文件名以及通过除去每行中所有排头和最尾的空白来“清洁“文件。在原始文件中读取然后写入一个新的文件,创建一个新的或者覆盖掉已存在的。 给你的用户一个选择来决定执行哪一个。将你的解转换成使用列表解析。** 444 | 445 | `lines = filter(lambda line : line.strip()+'\n', open('test.txt'))` 446 | 447 | **11–12. 传递函数。给在这章中描述的 testit()函数写一个姊妹函数。timeit()会带一个函数对象(和参数一起)以及计算出用了多少时间来执行这个函数,而不是测试执行时的错误。返回下面的状态:函数返回值,消耗的时间。你可以用 time.clock()或者 time.time(),无论哪一个给你提供了较高的精度。 (一般的共识是在 POSIX 上用 time.time(), 在 win32 系统上用 time.clock()) 注意:timeit()函数与 timeit 模块不相关(在 python2.3 中引入)** 448 | 449 | ```python 450 | import time 451 | 452 | def timeit(func, *nkwargs, **kwargs): 453 | begin = time.clock() 454 | try: 455 | retval = func(*nkwargs, **kwargs) 456 | result = (True, retval, time.clock()-begin) 457 | except Exception, diag: 458 | result = (False, str(diag)) 459 | return result 460 | 461 | def test(): 462 | funcs = (int, long, float) 463 | vals = (1234, 12.34, '1234', '12.34') 464 | 465 | for eachFunc in funcs: 466 | print '-' * 20 467 | for eachVal in vals: 468 | retval = timeit(eachFunc, eachVal) 469 | if retval[0]: 470 | print '%s(%s) =' % \ 471 | (eachFunc.__name__, `eachVal`), retval[1] 472 | print 'cost time(s): %s' % `retval[2]` 473 | else: 474 | print '%s(%s) = FAILED:' % \ 475 | (eachFunc.__name__, `eachVal`), retval[1] 476 | 477 | if __name__ == '__main__': 478 | test() 479 | ``` 480 | 481 | **11–13.使用 reduce()进行函数式编程以及递归。在第 8 章中,我们看到 N 的阶乘或者 N!作为 从 1 到 N 所有数字的乘积。** 482 | 483 | (a)用一分钟写一个带 x,y 并返回他们乘积的名为 mult(x,y)的简单小巧的函数。 484 | `def mult(x, y): return x * y` 485 | 486 | (b)用你在 a 中创建的 mult()函数以及 reduce 来计算阶乘。 487 | `reduce(mult, range(1, 4+1))` 488 | 489 | (c)彻底抛弃掉 mult()的使用,用 lamda 表达式替代。 490 | `reduce(lambda x, y : x * y, range(1, 4+1))` 491 | 492 | (d)在这章中,我们描绘了一个递归解决方案来找到 N!用你在上面问题中完成的 timeit()函数, 并给三个版本阶乘函数计时(迭代的,reduce()以及递归) 493 | 494 | ```python 495 | import time 496 | 497 | def iterator_N(N): 498 | result = 1 499 | for n in range(1, N+1): 500 | result *= n 501 | return result 502 | 503 | def reduce_N(N): 504 | return reduce(lambda x, y : x * y, range(1, N+1)) 505 | 506 | def recursive_N(N): 507 | if N == 0 or N == 1: 508 | return 1 509 | else: 510 | return N * recursive_N(N-1) 511 | 512 | def timeit(func, *nkwargs, **kwargs): 513 | begin = time.clock() 514 | try: 515 | retval = func(*nkwargs, **kwargs) 516 | result = (True, retval, time.clock()-begin) 517 | except Exception, diag: 518 | result = (False, str(diag)) 519 | return result 520 | 521 | if __name__ == '__main__': 522 | funcs = (iterator_N, reduce_N, recursive_N) 523 | N = 4 524 | for func in funcs: 525 | retval = timeit(func, N) 526 | if retval[0]: 527 | print '%s(%s) =' % \ 528 | (func.__name__, `N`), retval[1] 529 | print 'cost time(s): %s' % `retval[2]` 530 | else: 531 | print '%s(%s) = FAILED:' % \ 532 | (func.__name__, `N`), retval[1] 533 | ``` 534 | 535 | **11–15.递归。从写练习 6-5 的解,用递归向后打印一个字符串。用递归向前以及向后打印一个字符串。** 536 | ```python 537 | def backward(s,i=0): 538 | if i < len(s): 539 | print s[0:i+1], 540 | backward(s,i+1) 541 | 542 | def forward(s,j=0): 543 | if j > -len(s): 544 | print s[j-1:], 545 | forward(s,j-1) 546 | 547 | if __name__=='__main__': 548 | backward('abcdefg') 549 | print 550 | forward('abcdefg') 551 | ``` 552 | 553 | **11–16. 更新 easyMath.py。这个脚本,如例子 11.1 描绘的那样,以入门程序来帮助年轻人强化他们的数学技能。通过加入乘法作为可支持的操作来更进一步提升这个程序。额外的加分:也加入除法;这比较难做些因为你要找到有效的整数除数。幸运的是,已经有代码来确定分子比分母大, 所以不需要支持分数。** 554 | 555 | ```python 556 | from operator import add, sub, mul, div 557 | from random import randint, choice 558 | 559 | ops = {'+': add, '-': sub, '*': mul, '/':div} 560 | MAXTRIES = 2 561 | 562 | def doprob(): 563 | op = choice('+-*/') 564 | nums = [randint(1, 10) for i in range(2)] 565 | nums.sort(reverse=True) 566 | 567 | if (op == '/'): 568 | while nums[0] % nums[1] != 0: 569 | nums = [randint(1, 10) for i in range(2)] 570 | nums.sort(reverse=True) 571 | 572 | ans = ops[op](*nums) 573 | pr = '%d %s %d = ' % (nums[0], op, nums[1]) 574 | oops = 0 575 | while True: 576 | try: 577 | if int(raw_input(pr)) == ans: 578 | print 'correct' 579 | break 580 | if oops == MAXTRIES: 581 | print 'answer\n%s%d' % (pr, ans) 582 | else: 583 | print 'incorrect... try again' 584 | oops += 1 585 | except (KeyboardInterrupt, \ 586 | EOFError, ValueError): 587 | print 'invalid input... try again' 588 | 589 | 590 | def main(): 591 | while True: 592 | doprob() 593 | try: 594 | opt = raw_input('Again? [y]').lower() 595 | if opt and opt[0] == 'n': 596 | break 597 | except (KeyboardInterrupt, EOFError): 598 | break 599 | 600 | 601 | if __name__ == '__main__': 602 | main() 603 | ``` 604 | 605 | **11–17.定义** 606 | **(a) 描述偏函数应用和 currying 之间的区别。** 607 | 608 | 偏函数解决这样的问题:如果我们有函数是多个参数的,我们希望能固定其中某几个参数的值。 609 | 610 | Currying解决的是一个完全不同的问题:如果我们有几个单参数函数,并且这是一种支持一等函数(first-class)的语言,如何去实现一个多参数函数?函数加里化是一种实现多参数函数的方法。 611 | 612 | **(b) 偏函数应用和闭包之间有什么区别?** 613 | 614 | 闭包:一个可以使用另外一个函数作用域中的变量的函数。 615 | 616 | 偏函数:偏应用函数就是缺少部分或全部参数的函数 617 | 618 | **(c) 最后,迭代器和生成器是怎么区别开的?** 619 | 620 | 生成器是迭代器的真子集。 621 | 622 | **11–18. 同步化函数调用。复习下第6章中当引入浅拷贝和深拷贝的时候,提到的丈夫和妻子情形(6. 20小结)。他们共用了一个普通账户,同时对他们银行账户访问时会发生不利影响。创建一个程序,让调用改变账户收支的函数必需同步。换句话说,在任意给定时刻只能有个一进程或者线程来执行函数。一开始你试着用文件,但是一个真正的解决方法是用装饰器和在threading或者mutex模块中的同步指令。你看看第17章来获得更多的灵感。** 623 | 624 | *略。* 625 | -------------------------------------------------------------------------------- /12_Modules/12-1_Namespaces_versus_variable_scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdouchufu/Core-Python-Programming/0c89e3d4dde557761ddb8330316a3fa44ca69829/12_Modules/12-1_Namespaces_versus_variable_scope.png -------------------------------------------------------------------------------- /12_Modules/12.6_namespace.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | print '\ncalling foo()...' 3 | aString = 'bar' 4 | anInt = 42 5 | print "foo()'s globals:", globals().keys() 6 | print "foo()'s locals:", locals().keys() 7 | 8 | print "__main__'s globals:", globals().keys() 9 | print "__main__'s locals:", locals().keys() 10 | foo() 11 | 12 | ''' 13 | __main__'s globals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] 14 | __main__'s locals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] 15 | 16 | calling foo()... 17 | foo()'s globals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] 18 | foo()'s locals: ['anInt', 'aString'] 19 | ''' -------------------------------------------------------------------------------- /12_Modules/cli4vof.py: -------------------------------------------------------------------------------- 1 | # command-line interface for a very outstanding feature 2 | def cli4vof(): 3 | import omh4cli 4 | omh4cli.cli_util() 5 | -------------------------------------------------------------------------------- /12_Modules/note12_Modules.md: -------------------------------------------------------------------------------- 1 | # 本章大纲 2 | 本章介绍 Python 模块和如何把数据从模块中导入到编程环境中。**模块是用来组织 Python 代码的方法,而包则是用来组织模块的。** 3 | 4 | # 知识点 5 | ## 12.1 什么是模块 6 | 自我包含并且有组织的代码片断就是模块( module ),模块是按照逻辑来组织 Python 代码。 7 | 8 | ## 12.2 模块和文件 9 | 文件是在物理层上组织模块的方法。 10 | 一个文件被看作是一个独立模块, 一个模块也可以被看作是一个文件。 11 | 12 | ### 12.2.2 搜索路径和路径搜索 13 | **路径搜索**:查找某个文件的操作。 14 | **搜索路径**:去查找一组目录。(导入模块错误就是搜索路径下不存在该模块) 15 | 16 | `sys.path`:查看当前的搜索路径。 17 | `sys.modules`:当前导入了哪些模块和它们来自什么地方。 18 | 19 | ## 12.3 命名空间 20 | *命名空间 是标识符(变量名)到对象的映射。* 21 | 22 | **Python 解释器加载 命名空间 的顺序:** 23 | 24 | 1. 先加载内建命名空间`__builtins__`; 25 | 2. 随后加载执行模块的全局命名空间,它在模块开始执行后变为活动命名空间; 26 | 27 | -------------------- 28 | 29 | **`__builtins__` 和 `__builtin__` 的区别:** 30 | 31 | - `__builtins__` 模块包含内建函数,异常以及其他属性。 32 | - 在标准 Python 执行环境下, `__builtins__` 包含 `__builtin__` 的所有name。 33 | 34 | ### 12.3.1 命名空间与变量作用域比较 35 | 如果在执行期间调用了一个函数,那么将创建出第三个命名空间, 即 **局部命名空间**。 36 | ![Namespaces_versus_variable_scope](./12-1_Namespaces_versus_variable_scope.png "Namespaces_versus_variable_scope") 37 | 38 | ### 12.3.2 变量查找顺序 39 | 局部命名空间 → 全局命名空间 → 内建命名空间 40 | 41 | ### 12.3.3 无限制的命名空间 42 | 随时都可以给函数添加任何属性。 43 | 44 | ## 12.4 导入模块 45 | ### 12.4.1 模块导入顺序 46 | - Python 标准库模块 47 | - Python 第三方模块 48 | - 应用程序自定义模块 49 | 50 | ### 12.4.2 from-import 语句 51 | 使用`from module import name1[, name2[,... nameN]]`导入指定的模块属性。 52 | 53 | ### 12.4.3 多行导入 54 | 不提倡使用 `from Tkinter import *` 语句 55 | 56 | ### 12.4.4 扩展的 import 语句(`as`) 57 | ``` python 58 | import Tkinter as tk 59 | from cgi import FieldStorage as form 60 | ``` 61 | 62 | ## 12.5 模块导入的特性 63 | ### 12.5.1 载入时执行模块 64 | 65 | - 加载模块会导致这个模块被"执行",也就是被导入模块的顶层代码(包括设定全局变量以及类和函数的声明)将直接被执行。 66 | - **尽可能把代码封装到函数中,只把函数和模块定义放入模块的顶层是良好的模块编程习惯。** 67 | 68 | ### 12.5.2 导入(import )和加载(load) 69 | - 一个模块只被加载一次, 无论它被导入多少次(防止多重导入时代码被多次执行)。 70 | - 加载只在第一次导入模块时发生。 71 | 72 | ### 12.5.3 `from module import *`的使用场合 73 | - 目标模块中的属性非常多, 反复键入模块名很不方便。(如:Tkinter、NumPy、socket) 74 | - 在交互解释器下,为了减少输入模块名的次数。 75 | 76 | ### 12.5.4 被导入到导入者作用域的名字 77 | 只从模块导入名字的副作用是那些名字会成为局部命名空间的一部分。 78 | 79 | ### 12.5.5 关于 `__future__` 80 | 体验Python新特性:`from __future__ import new_feature` 81 | 82 | ### 12.5.6 警告框架 83 | 警告框架的两个组件: 84 | 85 | - **warnings 模块**:是一些警告异常类的集合。 `Warning` 直接从 `Exception` 继承,是所有警告(`UserWarning`、`DeprecationWarning`、`SyntaxWarning`、 `RuntimeWarning`)的基类。 86 | - **警告过滤器**:收集关于警告的信息(行号、 警告原因等),控制是否忽略警告、是否显示自定义的格式、或者转换为错误(生成一个异常)。 87 | 88 | ### 12.5.7 从 ZIP 文件中导入模块 89 | - 如果搜索路径中存在一个包含 Python 模块(`.py`、`.pyc,`、 `.pyo` 文件)的 `.zip` 文件,导入时会把 ZIP 文件当作目录处理,在文件中搜索模块。 90 | - 如果要导入的 ZIP 文件只包含 `.py` 文件, 那么 Python 不会为其添加对应的 `.pyc` 文件(一个 ZIP 归档没有匹配的 `.pyc` 文件时, 导入速度会相对慢一点)。 91 | 92 | ## 12.6 模块内建函数 93 | ### 12.6.1 `__import__()` 94 | 95 | ```python 96 | # __import__(module_name[, globals[, locals[, fromlist]]]) 97 | sys = __import__('sys') 98 | ``` 99 | 100 | ### 12.6.2 `globals()` 和 `locals()` 101 | - 内建函数`globals()` 和 `locals()`以字典形式返回调用者的全局命名空间和局部命名空间。 102 | - 在全局命名空间下,`globals()` 和 `locals()`返回相同的字典,因为此时的局部命名空间就是全局空间。 103 | 104 | ### 12.6.3 `reload()` 105 | *reload() 内建函数:重新导入一个已经导入过的模块。* 106 | 107 | **`reload()`使用要求:** 108 | 109 | - 模块必须是全部导入(不是使用 `from-import`),而且它已被成功导入。 110 | - 参数是模块自身而不是模块名字符串。 111 | ```python 112 | import sys 113 | reload(sys) 114 | ``` 115 | 116 | ## 12.7 包 117 | *包是一个有层次的文件目录结构,定义了一个由模块和子包组成的 Python 应用程序执行环境。* 118 | 119 | ### 12.7.1 目录结构 120 | - 导入包中的模块:`from package.module import sub_module` 121 | - 模块下的`__init__.py` 文件,用于初始化模块。 122 | 123 | ### 12.7.2 from-import all 124 | 125 | 包支持 from-import all 语句:`from package.module import *` 126 | 【`__init__.py` 中的 `__all__` 变量包含执行该语句时应该导入的模块名字符串列表。】 127 | 128 | ### 12.7.3 绝对导入 129 | 所有的导入都被认为是绝对的,即这些被导入的模块必须通过Python路径(`sys.path`或是`PYTHONPATH`)来访问。 130 | 131 | ### 12.7.4 相对导入 132 | ```python 133 | from Phone.Mobile.Analog import dial # 绝对导入 134 | from .Analog import dial # 相对导入 135 | from ..Fax import G3.dial # 相对导入 136 | ``` 137 | 138 | ## 12.8 模块的其他特性 139 | ### 12.8.1 自动载入的模块 140 | `sys.modules` 变量包含当前载入到解释器的模块组成的字典(key为模块名,value为模块路径) 141 | 142 | ### 12.8.2 阻止属性导入 143 | 如果不想让某个模块属性被 `from module import *` 导入 ,可以在不想导入的属性名称加上一个下划线` _ `。 144 | 145 | ### 12.8.3 不区分大小的导入 146 | 指定 `PYTHONCASEOK` 环境变量,以不区分大小写地导入模块。 147 | 148 | ### 12.8.4 源代码编码 149 | Python默认的编码格式是ASCII,在模块头部可以指定编码格式。 150 | ```python 151 | #!/usr/bin/env python 152 | # -*- coding: UTF-8 -*- 153 | ``` 154 | 155 | ### 12.8.5 循环导入 156 | `omh4cli.py`代码: 157 | ```python 158 | from cli4vof import cli4vof 159 | 160 | # command line interface utility function 161 | def cli_util(): 162 | pass 163 | 164 | # overly massive handlers for the command line interface 165 | def omh4cli(): 166 | cli4vof() 167 | 168 | omh4cli() 169 | ``` 170 | `cli4vof.py`代码: 171 | 172 | ```python 173 | # command-line interface for a very outstanding feature 174 | def cli4vof(): 175 | import omh4cli 176 | omh4cli.cli_util() 177 | ``` 178 | 执行命令:`python omh4cli.py`,不会因为循环导入模块而运行失败。 179 | 180 | ### 12.8.5 模块执行的方式 181 | - 通过命令行或 shell:`python hello.py` 182 | - 通过内建函数`execfile()`:`execfile('C:/hello.py')` 183 | - 通过模块导入:`import hello` 184 | - 通过解释器的 -m 选项:`python -m SimpleHTTPServer` 185 | 186 | ## 12.9 相关模块 187 | - `imp`:这个模块提供了一些底层的导入者功能。 188 | - `modulefinder`:该模块允许你查找 Python 脚本所使用的所有模块。 189 | - `pkgutil`:该模块提供了多种把 Python 包打包为一个"包"文件(`*.pkg`)分发的方法。 190 | - `site`:和 `*.pth` 文件配合使用, 指定包加入 Python 路径的顺序。 191 | - `zipimport` :使用该模块导入 ZIP 归档文件中的模块。 192 | - `distutils`:该模块提供了对建立、 安装、分发 Python 模块和包的支持。 193 | 194 | # 练习 195 | **12–1. 路径搜索和搜索路径。 路径搜索和搜索路径之间有什么不同?** 196 | 197 | 参考:12.2.2 搜索路径和路径搜索 198 | 199 | **12–2. 导入属性。 假设你的模块 mymodule 里有一个 foo() 函数。** 200 | 201 | **(a)** 把这个函数导入到你的命名空间有哪两种方法? 202 | 203 | - `from mymodule import foo` 204 | - `import mymodule` 205 | 206 | **(b)** 这两种方法导入后的命名空间有什么不同? 207 | 208 | 第一种方式导入后的命名空间是局部命名空间(可直接使用`foo()`),第二种导入后的命名空间是全局命名空间(通过指定模块名才能使用:`mymodule.foo()`) 209 | 210 | **12–3. 导入. "import module" 和 "fromn module import *" 有什么不同?** 211 | `fromn module import *`导入后的命名空间是局部命名空间,可以直接使用module 模块的所有属性。 212 | 213 | **12–4. 命名空间和变量作用域。命名空间和变量作用域有什么不同?** 214 | 215 | 命名空间(Namespaces)是名称(标识符)到对象的映射。所有局部空间的名称都在局部作用范围内。局部作用范围以外的的所有名称都在全局作用范围内。 局部命名空间和作用域会随函数调用不断变化,全局命名空间不变。 216 | 217 | **12–5. 使用 `__import__()`.** 218 | 219 | **(a)** 使用 `__import__()` 把一个模块导入到你的命名空间。 你最后使用了什么样的语法? 220 | 221 | `sys = __import__('sys')` 222 | 223 | **(b)** 和上边相同,使用 `__import__()` 从指定模块导入特定的名字。 224 | `__import__('os',globals(),locals(),['path','pip'])` 225 | 226 | **12–6. 扩展导入。创建一个 importAs() 函数. 这个函数可以把一个模块导入到你的命名空间, 但使用你指定的名字, 而不是原始名字。 例如, 调用wname=importAs('mymodule') 会导入mymodule , 但模块和它的所有元素都通过新名称 newname 或 newname.attr 访问。 这是 Python2.0 引入的扩展导入实现的功能。** 227 | 228 | ```python 229 | def importAs(module_name): 230 | return __import__(module_name) 231 | 232 | sys = importAs('sys') 233 | print sys.path 234 | ``` 235 | 236 | **12–7. 导入钩子。 研究 PEP 302 的导入钩子机制. 实现你自己的导入机制, 允许编码你的模块(encryption, bzip2, rot13, 等), 这样解释器会自动解码它们并正确导入。你可以参看 zip 文件导入的实现 (参阅 第 12.5.7 节)。** 237 | 238 | *略。* 239 | 240 | 241 | -------------------------------------------------------------------------------- /12_Modules/omh4cli.py: -------------------------------------------------------------------------------- 1 | from cli4vof import cli4vof 2 | 3 | # command line interface utility function 4 | def cli_util(): 5 | pass 6 | 7 | # overly massive handlers for the command line interface 8 | def omh4cli(): 9 | cli4vof() 10 | 11 | omh4cli() 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Core-Python-Programming 2 | My study notes and code from "Core Python Programming" 3 | 4 | [http://blog.csdn.net/column/details/core-python-program.html](http://blog.csdn.net/column/details/core-python-program.html "http://blog.csdn.net/column/details/core-python-program.html") 5 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | def importAs(module_name): 2 | return __import__(module_name) 3 | 4 | sys = importAs('sys') 5 | print sys.path --------------------------------------------------------------------------------