若想技术精进,当然得把基础知识打得牢牢的。
廖雪峰的官方网站 python3教程,该网站提供的教程浅显易懂,还附带了讲学视频,非常适合初学者正规入门。
以下是通过廖雪峰python官方网站学习的个人查漏补缺。
主要内容包括:(面向对象高级编程) 和 (错误、调试、测试)
1.__slots__()能够限制实例属性 2.@property装饰器能够将类的方法当做属性调用 3.多重继承 4.定制类(__str__()、__iter__()、__getitem__()、__getattr__()、__call__())具体用处请仔细看 5.使用枚举类/使用元类 6.错误处理:try...excpet...finally... 7.调试 logging才是最好的方式 8.单元测试 9.文档测试
1.面向对象高级编程
数据封装、继承和多态只是面向对象程序设计中最基础的3个概念。在Python中,面向对象还有很多高级特性。
1.使用__slots__:限制实例属性
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
但是,给一个实例绑定的方法,对另一个实例是不起作用的。为了给所有实例都绑定方法,可以给class绑定方法,给class绑定方法后,所有实例均可调用:
通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
使用__slots__可以限制实例的属性,比如,只允许对Student实例添加name和age属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
2.使用@property:Python内置的@property装饰器就是负责把一个方法变成属性调用。
把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
3.多重继承
MixIn:MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。
由于Python允许使用多重继承,因此,MixIn就是一种常见的设计。只允许单一继承的语言(如Java)不能使用MixIn的设计。
4.定制类
看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。
1)__str__():返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。
2)__iter__:如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
3)__getitem__:让类使用起来更像list或tuple。
与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
4)__getattr__:当查找属性不存在时,python系统会自动调用__getattr__()方法。一个__getattr__()方法,动态返回一个属性。
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值。 注:__getattr__在API动态调用上会非常有帮助。
5)__call__:任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。
更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:
Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。
5.使用枚举类/使用元类
更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能。
6.错误处理:try...excpet...finally...
当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
调用栈:如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。
记录错误:Python内置的logging模块可以非常容易地记录错误信息。如果不捕获错误,自然可以让Python解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。
同样是出错,但程序打印完错误信息后会继续执行,并正常退出:
抛出错误:用raise语句抛出一个错误。
捕获错误目的只是记录一下,便于后续追踪。
7.调试 ---logging才是终极武器
1)用print()方法,简单粗暴的方式调试
2)使用断言assert调试,assert断言比print()容易关闭。启动Python解释器时可以用-O参数来关闭assert。
3)使用logging调试
把print()替换为logging是第3种方式,和assert比,logging不会抛出错误,而且可以输出到文件:
logging.info()可以输出一段文本。logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。
4)使用pdb调试-------是一种方法,但是调试不是很方便
第4种方式是启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。
python -m pdb python程序文件名
(Pdb) l # 输入命令字母l来查看代码
(Pdb) n # 输入命令n可以单步执行代码
(Pdb) p 变量名 # 任何时候都可以输入命令p 变量名来查看变量
(Pdb) q # 输入命令q结束调试,退出程序
这种通过pdb在命令行调试的方法理论上是万能的,但实在是太麻烦了,如果有一千行代码,要运行到第999行得敲多少命令啊。还好,我们还有另一种调试方法。
5)pdb.set_trace()方法调试
pdb.set_trace()-----不是单步执行,这个方式比直接启动pdb单步调试效率要高很多,但也高不到哪去。
6)IDE
如果要比较爽地设置断点、单步执行,就需要一个支持调试功能的IDE。目前比较好的Python IDE有:
Visual Studio Code:https://code.visualstudio.com/,需要安装Python插件。 (用过一小段时间)
PyCharm:http://www.jetbrains.com/pycharm/ (本电脑上正在使用)
另外,Eclipse加上pydev插件也可以调试Python程序。
关于调试的各种方法对比,logging才是终极武器。
8.单元测试 ---测试驱动开发
“测试驱动开发”(TDD:Test-Driven Development)。单元测试:是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。
为了编写单元测试,我们需要引入Python自带的unittest模块。编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual()。
self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
setUp与tearDown
可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。
setUp()和tearDown()方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:
class TestDict(unittest.TestCase):
def setUp(self):
print('setUp...')
def tearDown(self):
print('tearDown...')
单元测试可以有效地测试某个程序模块的行为,是未来重构代码的信心保证。
单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。
单元测试代码要非常简单,如果测试代码太复杂,那么测试代码本身就可能有bug。
单元测试通过了并不意味着程序就没有bug了,但是不通过程序肯定有bug。
9.文档测试
Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
doctest非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含doctest的注释提取出来。用户看文档的时候,同时也看到了doctest。