很多人推荐的 Python 教程
递归函数
汉诺塔的 Python 实现
|
|
迭代
判断一个对象是否可迭代
|
|
对可迭代对象进行下标循环
|
|
同时引用多个变量
这在 Python 中非常常见且简单:
|
|
List comprehensions 列表生成器
|
|
generator
生成器
generator
保存的是算法,一边计算,一边循环。
定义生成器有多种方法:
方法一:将列表解析式的 []
改为 ()
。
|
|
generator
也是可迭代对象:
|
|
方法二: yield
函数是顺序执行,遇到 return
语句或执行完所有语句返回。但是使用 yield
,执行到 yield
就停止执行,下一次从 yield
之后第一条语句开始执行。
|
|
执行结果:
|
|
函数式编程
特点之一是:允许把函数本身作为参数传入另一个函数,还允许返回一个函数。由于 Python 允许使用变量,所以不是纯函数式编程语言。
高阶函数
把函数作为参数传入,这样的函数称为高阶函数。
map()
函数
内置的 map()
,第一个参数是任意函数,第二个参数是 Iterable
,而返回 Iterator
。(注意第二个参数和返回值的区别)
|
|
匿名函数
|
|
冒号前为参数,冒号后表达式的值就是要 return
的值。
装饰器
|
|
实际上解释器会将其解释为:
|
|
偏函数
functools.partial
的作用是:把函数的某些参数固定,返回一个新函数,使任务更简单。
|
|
即等价于:
|
|
使用模块
|
|
这就是 Python 模块的标准文件模版。
其中:
|
|
当在命令行中运行该文件时,Python 解释器把 __name__
置为 __main__
如果该文件被当成模块导入,则该条件表达式为 False
。
不应该被外部引用的函数名或变量名前往往有 _
。
面向对象编程
不希望外部访问的对象(变量或函数)前加前缀 __
。
获取对象信息
用 type(object)
来判断类型。
普通变量
|
|
函数等
|
|
另外:
|
|
并且还可以判断变量是否是某些类型的一种:
|
|
使用 dir()
用 dir()
获取一个对象的所有属性和方法。
|
|
形似于 __xxx__
这样的变量都是有特殊用途的。比如 __len__
方法返回长度。以下代码是等价的。
|
|
自己写的类,如果也想用 len()
方法,就要自己写一个 __len__()
。
|
|
限制实例属性
Python 是动态语言,创建类的实例之后可以给实例绑定属性和方法,只对该实例有效,对该类的其他实例无效。
先定义一个类:
|
|
然后给实例绑定一个属性:
|
|
还可以给实例绑定一个方法:
|
|
这个方法对该类的其他实例无效:
|
|
但是可以给类绑定方法,就用于该类的所有实例。
|
|
如果想要限制实例的属性:Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class实例能添加的属性:
|
|
需要注意的是 __slots__
限制只对当前类起作用,对其子类是不起作用的,而且只能限制添加属性,不能限制通过添加方法的方式添加属性。
编写可迭代的类
|
|
错误、调试和测试
错误处理
Python 内置了一套 try...except...finally...
的错误处理机制。
try
|
|
如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即 except
语句块,执行完 except
后,如果有 finally
语句块,则执行finally
语句块,至此,执行完毕。
如果没有发生,则 except
语句块不被执行。
但是 finally
如果有,则一定会被执行。
如果发生了不同类型的错误,应该由不同的 except
语句块处理。可以有多个 except
来捕获不同类型的错误。此外,如果没有错误发生,可以在 except
语句块后面加一个 else
,当没有错误发生时,会自动执行 else
语句:
|
|
Python的错误其实也是class,所有的错误类型都继承自BaseException,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。
|
|
第二个 except
永远也捕获不到 UnicodeError
,因为 UnicodeError
是 ValueError
的子类,如果有,也被第一个 except
给捕获了。
记录错误
使用 logging
模块可以很容易地记录错误信息,而且在打印出错误信息之后可以继续执行。
没有使用 logging
|
|
结果如下:
|
|
从上往下可以看到整个错误的调用函数链。
如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。
使用 logging
|
|
结果如下,同样会出错,但打印完错误信息会继续执行并且正常退出。
|
|
抛出错误
|
|
执行,最后跟踪到自定义的错误:
|
|
最后,我们来看另一种错误处理的方式:
|
|
捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。raise
语句如果不带参数,就会把当前错误原样抛出。此外,在 except
中 raise
一个 Error
,还可以把一种类型的错误转化成另一种类型:
调试
断言
|
|
assert
的意思是,表达式 n != 0
的值应该是 True
,否则,根据程序运行的逻辑,后面的代码肯定会出错。如果断言失败,assert
语句本身就会抛出 AssertionError
:
|
|
启动 Python 解释器的时候可以用参数 -O
来关闭 assert
,关闭后,可以把 assert
语句当成 pass
看。
logging
logging
允许你指定记录信息的级别,有 debug
,info
,warning
,error
等几个级别,当我们指定 level=INFO
时,logging.debug
就不起作用了。同理,指定 level=WARNING
后,debug
和 info
就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
pdb
启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。我们先准备好程序:
|
|
然后启动:
|
|
以参数 -m pdb
启动后,pdb
定位到下一步要执行的代码 -> s = '0'
。输入命令 l
(注意这里是小写字母 L,不是数字 1)来查看代码:
|
|
输入命令 n
可以单步执行代码,任何时候都可以输入命令 p 变量名
来查看变量::
|
|
输入命令 q
结束调试,退出程序。
|
|
运行代码,程序会自动在 pdb.set_trace()
暂停并进入 pdb
调试环境,可以用命令 p
查看变量,或者用命令 c
继续运行。
单元测试
单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。
以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。
编写一个 Dict
类,这个类的行为和 dict
一致,但是可以通过属性来访问,用起来就像下面这样:
|
|
代码如下:
|
|
编写单元测试,需要引入 Python 自带的 unitteste
模块,编写 mydict_test.py
如下:
|
|
编写单元测试时,需要编写一个测试类,从 unittest.TestCase
继承。
以 test
开头的方法就是测试方法,不以 test
开头的方法不被认为是测试方法,测试的时候不会被执行。
对每一类测试都需要编写一个 test_xxx()
方法。由于 unittest.TestCase
提供了很多内置的条件判断,最常用的断言就是 assertEqual()
。
|
|
另一种重要的断言就是期待抛出制定类型的 Error,比如通过 d['empty']
访问不存在的 key 时,断言会抛出 keyError
:
|
|