模块
增加了可维护性
使用模块
引用模块: import 被引用模块
然后可以访问被引用模块的所有功能
正常的函数和变量名是公开的(public)范围,如:abc,x123,PI等
__xx__是特殊变量,可以被直接引用,定义的文档注释也可以用特殊变量__doc__访问,一般不用这种变量名
类似_xx和__xx这种的函数或变量是非公开的(private),这种无法直接引用,我们可以通过其他的方式访问,比如:
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
跟Java的类范围类似:private隐藏细节,public函数提供对外访问接口
我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
安装第三方模块
一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Pillow的名称叫Pillow,因此,安装Pillow的命令就是:
pip install Pillow
面向对象编程
类和实例
类(Class)是抽象的集合,实例(Instance)是类的具体实现
定义类:(object)表示该类从哪个类继承下来的,__init__方法里面self是必须带的
class Student(object):
pass
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
创建实例:
student = Student()
student = Student("张三",90)
访问限制
如果变量名如果以__开头,就成了私有变量(private)
还是以Student类为案例
外部想访问,我们需要提供get_name, get_score等方法
如果外部想修改属性,我们也可以提供set_name, set_score等方法
有些特殊变量外部是可以直接访问的,所以用__开头的实例变量也不是一定不能从外部访问,比如__name,这是Python解释器自定义了一些常用的特殊变量
继承和多态
学过Java的都懂,看看即可
定一个class的时候,可以从现有的class继承,称新的class为子类(SubClass),被继承的类为基类、父类或超类(Base Class、SuperClass)
在上节《类和实例》中定义一个class我们继承自object,如今可以改成父类
子类继承父类的方法,并且可以重写方法,如果重写过了,同一个方法名的方法会用子类的
定义动物父类:
class Animal(object):
def run(self):
print('Animal is running...')
子类:
class Dog(Animal):
pass
子类:
class Cat(Animal):
pass
子类均可以使用父类的run()方法
dog = Dog()
dog.run()
cat = Cat()
cat.run()
也可以重写run()方法,Java里面叫重写,Python随便吧
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
再次运行就不一样了
获取对象信息
type()函数:判断对象类型
如:type(123) ==> <class 'int'>; type(123) == int ==> True
isinstance(): 判断对象类型及其子类
对于class的继承关系来说,使用type()很不方便,这时候我们使用isinstace()函数判断class类型
总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。
dir(): 获得对象的所有属性和方法
len():对象长度
getattr():获取属性
setattr():设置属性
hasattr():判断是否拥有属性
实例属性和类属性
实例属性:以下的name就是实例属性
class Student(object):
def __init__(self, name):
self.name = name
类属性:以下的name是类属性
class Student(object):
name = 'Student'
面向对象高级编程
封装、继承和多态是面向对象程序设计的3个基本概念
使用slots
动态绑定允许我们在程序运行的过程中动态给class加上功能,静态语言不允许
>>> s = Student()
>>> s.set_score(99)
>>> s.score
99
score这个属性本身没有,但可以动态设置,但是这样并不安全
如果我们要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性
Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age')
如果存在子类的话,子类实例允许的属性是子类自身的__slots__加上父类的__slots__
使用@property
学生的成绩范围是0~100,如果超出则不被允许,我们定义一个Student类
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
上面的代码稍微有些复杂,调用方法比调用属性麻烦,Python内置的@property装饰器负责把一个方法变成属性调用
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
多重继承
class Dog(Mammal, Runnable, Flyable):
pass
每继承一个父类,就有这个父类的能力
定制类
重写类的特定方法,比如__str__(),__repr__()
print方法调用__str__()
直接调用类的话,用的是__repr__()方法,我们可以自定义__repr__() = __str__(),这样不用写两遍
使用枚举类
引入依赖: from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
可以使用Month.Jan来引用常量,或者枚举所有成员:
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
使用元类
基本用不到
错误、调试和测试
错误处理
python使用try...except...finally的错误处理机制
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
except语句块有错误的时候会执行,
finally语句块一定会执行,Java的try...catch...finally模块也是如此
Python的异常基类是BaseException,下面有图展示异常结构
Python内置的logging模块可以记录错误信息:
导入依赖:import logging
使用: logging.exception(e)
logging可以把错误记录到日志文件,以供事后排查
抛出异常用raise语句:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
异常结构如下图所示:
调试
python调试代码方式有:print、assert、logging、pcb
print和pcb都没什么介绍的
assert断言
assert句式:
assert 判断函数 ,输出语句
案例:
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
失败输出:
$ python err.py
Traceback (most recent call last):
...
AssertionError: n is zero!
assert()可以通过python解释器来控制开关: -O
断言的开关"-O"是英文大写字母O
logging日志
import logging
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
logging可以设置输出等级,根据等级不同来展示信息:
import logging
logging.basicConfig(level=logging.INFO)
信息等级分为debug、info、warning、error几个级别, debug > info > warning > error,等级越高输出信息越多
单元测试
测试驱动开发:Test-Driven Development,TDD
单元测试案例:
import unittest
class TestXXX(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
引入unittest依赖,然后编写一个类继承自unittest.TestCase
编写测试方法要以test开头,不以test开头的方法不会被执行
unittest.TestCase提供了很多内置的条件判断,我们调用这些断言即可,比如:
assertEqual()、assertTrue()
最后,通过main()方法运行单元测试:
if __name__ == '__main__':
unittest.main()
也可以通过命令行运行单元测试:-m unittest mydict_test
单元测试可以写setUp()和tearDown()方法,类似Java中AOP切面的before和after,用来启动和关闭数据库
class TestDict(unittest.TestCase):
def setUp(self):
print('setUp...')
def tearDown(self):
print('tearDown...')
文档测试
Python的官方文档有很多示例代码,把这些代码拿去运行,结果与文档示例代码显示一致
但Python还可以利用文档测试doctest模块直接提取注释中的代码并运行
以下是doctest测试Dict类:
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__':
import doctest
doctest.testmod()
什么输出也没有。这说明我们编写的doctest运行都是正确的。如果程序有问题,比如把__getattr__()方法注释掉,再运行就会报错