第三课、面向对象的应用(异常处理、文件备份)
一、课程介绍
1.1 课程概要
章节概要
- 迭代器
- 生成器
- 实战:模拟range函数效果
二、装饰器的介绍与应用
2.1 什么是装饰器
装饰器
- 用于拓展原来函数功能的一种函数
- 返回函数的函数
- 在不用更改原函数的代码前提下给函数增加新的功能
如果没有装饰器
1 def hello(): 2 """简单功能模拟""" 3 print('hello world') 4 5 6 def test(): 7 print('test..') 8 9 10 def hello_wrapper(): 11 """新的函数,包裹原来的hello""" 12 print('开始执行hello') 13 hello() 14 print('结束执行') 15 16 17 def test_wrapper(): 18 """新的函数,包裹原来的hello""" 19 print('开始执行hello') 20 test() 21 print('结束执行') 22 23 24 if __name__ == '__main__': 25 # hello() 26 hello_wrapper()
实现装饰器
1 def log(func): 2 """记录函数执行的日志""" 3 def wrapper(): 4 print('start...') 5 func() 6 print('end...') 7 return wrapper 8 9 10 def log_in(func): 11 """记录函数执行的日志""" 12 def wrapper(): 13 print('开始进入。。。') 14 func() 15 print('结束...') 16 return wrapper 17 18 19 @log 20 def hello(): 21 # """简单功能模拟""" 22 print('hello world') 23 24 25 @log 26 @log_in 27 def test(): 28 print('test..') 29 30 31 if __name__ == '__main__': 32 # hello() 33 test()
2.2 带参数的装饰器
1 def log(name=None): 2 """记录函数执行的日志""" 3 4 def decorator(func): 5 def wrapper(*args, **kwargs): 6 print('{0}.start...'.format(name)) 7 print(args) 8 print(kwargs) 9 rest = func(*args, **kwargs) 10 print('{0}.end...'.format(name)) 11 return rest 12 return wrapper 13 return decorator 14 15 16 @log('hello') 17 def hello(): 18 # """简单功能模拟""" 19 print('hello world') 20 21 22 @log('test') 23 def test(): 24 print('test..') 25 26 27 @log('from add') 28 def add(a, b, *args, **kwargs): 29 return a+b 30 31 32 if __name__ == '__main__': 33 # hello() 34 # test() 35 rest = add(5, 6, k=5, v=6) 36 print(rest)
2.3 带参数的装饰器之wraps
1 from functools import wraps 2 3 4 def log(name=None): 5 """记录函数执行的日志""" 6 7 def decorator(func): 8 @wraps(funcs) 9 def wrapper2(*args, **kwargs): 10 """装饰器内部的函数""" 11 print('{0}.start...'.format(name)) 12 print('__warpper:{0}'.format(func.__name__)) 13 print('__warpper:{0}'.format(func.__doc__)) 14 rest = func(*args, **kwargs) 15 print('{0}.end...'.format(name)) 16 return rest 17 # wrapper2.__doc__ = func.__doc__ 18 # wrapper2.__name__ = func.__name__ 19 return wrapper2 20 return decorator 21 22 23 @log('hello') 24 def hello(): 25 """简单功能模拟""" 26 print('hello world') 27 28 29 if __name__ == '__main__': 30 print('doc:{0}'.format(hello.__doc__)) 31 print('name:{0}'.format(hello.__name__)) 32 hello()
2.4 类的装饰器
1 def f(self): 2 print('{0}>我要吃东西'.format(self.name)) 3 print('0000000000') 4 5 6 def eat(cls): 7 """吃东西装饰器""" 8 # cls.eat = lambda self: print('{0}>我要吃东西'.format(self.name)) 9 cls.eat = f 10 return cls 11 12 13 @eat 14 class Cat(object): 15 """猫类""" 16 def __init__(self, name): 17 self.name = name 18 19 20 if __name__ == '__main__': 21 cat = Cat('小黑') 22 cat.eat()
三、迭代器与生成器
3.1 迭代器
迭代器介绍
- 迭代(iterate)意味着重复多次,就像循环那样(list,tuple)
- 实现了方法__iter__的对象时可迭代的,而实现了方法__next__的对象是迭代器
- 调用方法__next__时(或next()),迭代器返回其下一个值
- 如果迭代器没有可供返回的值,触发Stoplteration异常
从迭代器创建序列
- 通过对可迭代对象调用内置函数iter,可获得一个迭代器
- 举例
![]()
迭代器效果演示
- 生成12345的平方
-
1 class PowNumber(object): 2 """ 3 迭代器 4 生成1,2,3,4,5,...数的平方 5 """ 6 value = 0 7 8 def __next__(self): 9 self.value += 1 10 if self.value > 10: 11 raise StopIteration 12 return self.value * self.value 13 14 def __iter__(self): 15 return self 16 17 18 if __name__ == '__main__': 19 pow = PowNumber() 20 # print(pow.__next__()) 21 # print(pow.__next__()) 22 # print(pow.__next__()) 23 # print(pow.__next__()) 24 # print(pow.__next__()) 25 print(next(pow)) 26 print(next(pow)) 27 print(next(pow)) 28 print(next(pow)) 29 # 循环迭代器 30 for i in pow: 31 print(i)
3.2 生成器
生成器介绍
- 生成器是一种普通函数语法定义的迭代器
- 包含yield语句的函数都被称为生成器
- 不使用return返回一个值,而是可以生成多个值,每次一个
- 每次使用yield生成一个值后,函数都将冻结,即在此停止执行
- 被重新唤醒后,函数将从停止的地方继续执行
-
1 def pow(): 2 yield 1 3 yield 2 4 yield 3 5 yield 4 6 7 8 def pow_number(): 9 return (x * x for x in [1, 2, 3, 4, 5]) 10 11 12 def pow_number2(): 13 for x in [1, 2, 3, 4, 5]: 14 yield x * x 15 16 17 if __name__ == '__main__': 18 # rest = pow() 19 # print(next(rest)) 20 # print(next(rest)) 21 # print(next(rest)) 22 # print(next(rest)) 23 # print(next(rest)) 24 # for i in rest: 25 # print(i) 26 27 rest = pow_number2() 28 print(next(rest)) 29 print(next(rest)) 30 print(next(rest)) 31 print(next(rest)
3.3 模拟range函数
1 def use_range(): 2 """python内置的range函数""" 3 for i in range(5, 10): 4 print(i) 5 6 7 class IterRange(object): 8 """使用迭代器来模拟range函数""" 9 def __init__(self, start, end): 10 self.start = start 11 self.end = end 12 13 def __next__(self): 14 self.start += 1 15 if self.start >= self.end: 16 raise StopIteration 17 return self.start 18 19 def __iter__(self): 20 return self 21 22 23 class GenRange(object): 24 def __init__(self, start, end): 25 self.start = start 26 self.end = end 27 28 def get_num(self): 29 while True: 30 if self.start >= self.end - 1: 31 break 32 self.start += 1 33 yield self.start 34 35 36 def get_num1(start, end): 37 start -= 1 38 while True: 39 if start >= end - 1: 40 break 41 start += 1 42 yield start 43 44 45 if __name__ == '__main__': 46 use_range() 47 print('----------') 48 iter = IterRange(5, 10) 49 # print(next(iter)) 50 # print(next(iter)) 51 # print(next(iter)) 52 # print(next(iter)) 53 # print(next(iter)) 54 l = list(iter) 55 print(l) 56 57 print('__________') 58 gen = GenRange(5, 10).get_num() 59 print(gen) 60 # print(next(gen)) 61 # print(next(gen)) 62 # print(next(gen)) 63 # print(next(gen)) 64 # print(next(gen)) 65 print(list(gen)) 66 67 print('----------') 68 gen_f = get_num1(5, 10) 69 print(gen_f) 70 print(list(gen_f))
四、实战:文件备份
1 import os 2 import os.path 3 4 5 class FileBackup(object): 6 """ 7 文本文件备份 8 """ 9 10 def __init__(self, src, dist): 11 """ 12 构造方法 13 :param src:目录 需要备份前的文件目录 14 :param dist:目录 备份后的目录 15 """ 16 self.src = src 17 self.dist = dist 18 19 def read_files(self): 20 """ 21 读取src目录下的所有文件 22 """ 23 ls = os.listdir(self.src) 24 print(ls) 25 for l in ls: 26 # 循环处理每一个文件/文件夹 27 self.backup_file(l) 28 29 def backup_file(self, file_name): 30 """ 31 处理备份 32 :param file_name: 33 :return: 34 """ 35 # 1. 判断dist是否存在,如果不存在,要创建这个目录 36 if not os.path.exists(self.dist): 37 os.makedirs(self.dist) 38 print('指定的目录不存在,创建完成') 39 40 # 2. 判断文件是否为我们要备份的文件 41 42 # 拼接文件的完整路径 43 full_src_path = os.path.join(self.src, file_name) 44 full_dist_path = os.path.join(self.dist, file_name) 45 46 # 首先要判断是否为文件夹,然后借助于文件的后缀名进行判断 47 if os.path.isfile(full_src_path) and os.path.splitext(full_src_path)[-1].lower() == '.txt': 48 print(full_src_path) 49 # 3. 读取文件内容 50 with open(full_dist_path, 'w', encoding='utf-8')as f_dist: 51 print('>>开始备份【{0}】'.format(file_name)) 52 53 with open(full_src_path, 'r', encoding='utf-8') as f_src: 54 while True: 55 rest = f_src.read(100) 56 if not rest: 57 break 58 # 4. 把读取到的内容写到新的文件中 59 f_dist.write(rest) 60 f_dist.flush() 61 print('>>>【{0}】备份完成'.format(file_name)) 62 else: 63 print('文件类型不符合备份要求,跳过>>') 64 65 def backup_file2(self, file_name): 66 """ 67 处理备份 68 :param file_name: 69 :return: 70 """ 71 # 1. 判断dist是否存在,如果不存在,要创建这个目录 72 if not os.path.exists(self.dist): 73 os.makedirs(self.dist) 74 print('指定的目录不存在,创建完成') 75 76 # 2. 判断文件是否为我们要备份的文件 77 78 # 拼接文件的完整路径 79 full_src_path = os.path.join(self.src, file_name) 80 full_dist_path = os.path.join(self.dist, file_name) 81 82 # 首先要判断是否为文件夹,然后借助于文件的后缀名进行判断 83 if os.path.isfile(full_src_path) and os.path.splitext(full_src_path)[-1].lower() == '.txt': 84 # 3. 读取文件内容 85 with open(full_dist_path, 'w', encoding='utf-8')as f_dist, \ 86 open(full_src_path, 'r', encoding='utf-8') as f_src: 87 print('>>开始备份【{0}】'.format(file_name)) 88 while True: 89 rest = f_src.read(100) 90 if not rest: 91 break 92 # 4. 把读取到的内容写到新的文件中 93 f_dist.write(rest) 94 f_dist.flush() 95 print('>>>【{0}】备份完成'.format(file_name)) 96 else: 97 print('文件类型不符合备份要求,跳过>>') 98 99 def backup_file(self, file_name): 100 """ 101 处理备份--代码优化 102 :param file_name: 103 :return: 104 """ 105 # 1. 判断dist是否存在,如果不存在,要创建这个目录 106 if not os.path.exists(self.dist): 107 os.makedirs(self.dist) 108 print('指定的目录不存在,创建完成') 109 110 # 2. 判断文件是否为我们要备份的文件 111 112 # 拼接文件的完整路径 113 full_src_path = os.path.join(self.src, file_name) 114 full_dist_path = os.path.join(self.dist, file_name) 115 116 # 首先要判断是否为文件夹,然后借助于文件的后缀名进行判断 117 if os.path.isfile(full_src_path) and os.path.splitext(full_src_path)[-1].lower() == '.txt': 118 print(full_src_path) 119 # 3. 读取文件内容 120 with open(full_dist_path, 'w', encoding='utf-8')as f_dist: 121 print('>>开始备份【{0}】'.format(file_name)) 122 123 with open(full_src_path, 'r', encoding='utf-8') as f_src: 124 while True: 125 rest = f_src.read(100) 126 if not rest: 127 break 128 # 4. 把读取到的内容写到新的文件中 129 f_dist.write(rest) 130 f_dist.flush() 131 print('>>>【{0}】备份完成'.format(file_name)) 132 else: 133 print('文件类型不符合备份要求,跳过>>') 134 135 136 if __name__ == '__main__': 137 # # 要备份的文件目录地址 138 # src_path = 'C:\\Users\\19342\\Desktop\\me\\py_learn\\chapter05\\src' 139 # # 备份后的目录地址 140 # dist_path = 'C:\\Users\\19342\\Desktop\\me\\py_learn\\chapter05\\dist' 141 142 # 当前代码的目录名称 143 # C:\\Users\\19342\\Desktop\\me\\py_learn\\chapter05\\ test_backup.py 144 base_path = os.path.dirname(os.path.abspath(__file__)) 145 # 要备份的文件目录地址 146 src_path = os.path.join(base_path, 'src') 147 print(src_path) 148 # 备份后的目录地址 149 dist_path = os.path.join(base_path, 'dist') 150 print(dist_path) 151 bak = FileBackup(src_path, dist_path) 152 bak.read_files()
五、异常处理
章节概要
- 异常概述
- 如何捕获异常
- 自定义异常
- 抛出异常及异常的传递
5.1 什么是异常处理
异常概述
- 每个异常都是某个类的实例
- 发生了异常如果不捕获,则程序将终止执行
- 有一些内置的异常类
内置异常类
| 类名 | 描述 |
| Exception | 几乎所有的异常类都是从它派生而来的 |
| AttributeError | 引用属性或给它赋值失败时引发 |
| OSError | 操作系统不能执行指定的任务(如打开文件)时引发,有多个子类 |
| IndexError | 使用序列中不存在的索引时引发,为LookupError的子类 |
| KeyError | 使用映射中不存在的键时引发,为LookupError的子类 |
| NameError | 找不到名称(变量)时引发 |
| SyntaxError | 代码不正确时引发 |
| TypeError | 将内置操作或函数用于类型不正确的对象时引发 |
| ValueError | 将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适 |
| ZeroDivisionError | 在除法或求模运算的第二个参数为零时引发 |
5.2 异常的捕获
如何捕获异常
- 使用try...except捕获所有异常
- 使用try...except捕获多个指定异常
- 使用try...except...finally处理必不可少的逻辑
1 def test_div(num1, num2): 2 """当除数为0时""" 3 return num1 / num2 4 5 def test_file(): 6 """读取文件""" 7 try: 8 f = open('test.txt', 'r', encoding='utf-8') 9 rest = f.read() 10 print(rest) 11 except: 12 print('error') 13 finally: 14 f.close() 15 print('closed') 16 17 18 19 if __name__ == '__main__': 20 # # 使用try...except捕获所有异常 21 # try: 22 # rest = test_div(5, 's') 23 # print(rest) 24 # except ZeroDivisionError: 25 # print('报错了,除数不能为0') 26 # except TypeError: 27 # print('报错了,请输入数字') 28 # 29 # # 使用try...except捕获多个指定异常 30 # try: 31 # rest = test_div(5, 's') 32 # print(rest) 33 # except (TypeError, ZeroDivisionError) as err: 34 # print('反正就是报错了') 35 # print(err) 36 # rest = test_div(5, 's') 37 # print(rest) 38 # 使用try...except...finally处理必不可少的逻辑 39 test_file()
5.3 自定义异常
自定义异常
- 通过继承自Exception类来自定义异常
1 # class MyException(Exception): 2 # """ 我的自定义异常 """ 3 # pass 4 5 6 class ApiException(Exception): 7 """ 我的自定义异常 """ 8 err_code = '' 9 err_msg = '' 10 11 def __init__(self, err_code=None, err_msg=None): 12 self.err_code = self.err_code if self.err_code else err_code 13 self.err_msg = self.err_msg if self.err_msg else err_msg 14 15 def __str__(self): 16 return 'Error:{0} - {1}'.format(self.err_code, self.err_msg) 17 18 19 class InvalidCtrlExec(ApiException): 20 """ 当参数不合法时触发 """ 21 err_code = '40001' 22 err_msg = '不合法的调用凭证' 23 24 25 class BadPramsException(ApiException): 26 """ 参数不正确 """ 27 err_code = '40002' 28 err_msg = '两个参数必须都是整数' 29 30 31 def divide(num1, num2): 32 """ 除法的实现 """ 33 # 两个数必须为整数 34 if not isinstance(num1, int) or not isinstance(num2, int): 35 raise BadPramsException() 36 # 除数不能为0 37 if num2 == 0: 38 raise ApiException('400001', '除数不能为0') 39 return num1 / num2 40 41 42 if __name__ == '__main__': 43 try: 44 rest = divide(5, 's') 45 print(rest) 46 except BadPramsException as e: 47 print('------------------') 48 print(e) 49 except ApiException as err: 50 print('出错了') 51 print(err)
5.4 异常的传递
抛出异常及异常的传递
- 如果在异常产生的地方不捕获,那么它会一层一层的往上传递
1 class MyException(Exception): 2 """ 自定义异常类 """ 3 pass 4 5 6 def v_for(): 7 """ 自定义函数 """ 8 for i in range(1, 100): 9 if i == 20: 10 raise MyException 11 print(i) 12 13 14 def call_v_for(): 15 """ 调用vfor函数 """ 16 print('开始调用v_for') 17 try: 18 v_for() 19 except MyException: 20 print('________') 21 print('结束调用vfor') 22 23 24 def test_rasie(): 25 print('测试函数') 26 # try: 27 call_v_for() 28 # except MyException: 29 # print('________') 30 print('测试完毕') 31 32 33 if __name__ == '__main__': 34 test_rasie()

浙公网安备 33010602011771号