Python学习笔记#10 面向对象的应用(异常处理、文件备份)

1.装饰器
(1) 用于拓展原来函数功能的一种函数
(2) 返回函数的函数
(3) 在不用更改原函数代码的前提下给函数增加新的功能
例:

def log(func):
    """记录函数执行的日志"""
    def wrapper():
        print('开始执行')
        func()
        print('执行完毕')
    return wrapper

@log
def hello():
    """简单功能模拟"""
    print("Hello PyCharm")

if __name__ == '__main__':
    hello()

输出 -->

开始执行
Hello PyCharm
执行完毕

2.带参数的装饰器
(1)

def log(name = None):

    def decorator(func):
        def wrapper():
            print('{0}开始执行'.format(name))
            func()
            print('{0}执行完毕'.format(name))
        return wrapper
    return decorator

@log('装饰器函数')
def hello():
    """简单功能模拟"""
    print("Hello PyCharm")

if __name__ == '__main__':
    hello()

输出 -->

装饰器函数开始执行
Hello PyCharm
装饰器函数执行完毕

(2) 带魔法参数的装饰器 :
PS: 当函数的参数不确定时,可以使用*args 和**kwargs。
*args:表示的就是将实参中按照位置传值,多出来的值都给args,且以元组的方式呈现
**kwargs:表示的就是形参中按照关键字传值把多余的传值以字典的方式呈现

def log(name=None):

    def decorator(func):
        def wrapper(*args, **kwargs):
            print('{0}开始执行'.format(name))
            print(args, kwargs)
            func(*args, **kwargs)
            print('{0}执行完毕'.format(name))
        return wrapper
    return decorator

@log('from add')
def add(a, b):
    return a + b

if __name__ == '__main__':
    add(5, 6)

输出 -->

from add开始执行
(5, 6) {}
from add执行完毕

(3) @wraps
当装饰器装饰一个函数时,实际上就是将被装饰的函数作为一个参数传入装饰器函数,返回一个闭包,而闭包内部调用这个函数,再另外多做一些事情。然后再由我们的函数名字去接收那个返回值的闭包的引用,达到了我们装饰的目的,所以此时,我们的函数的__name__等属性其实已经改变,变成了我们闭包的私有属性。
functools.wraps()这个函数,作为一个装饰器去装饰闭包,并且给这个装饰器传入一个参数,这个参数是闭包外的装饰器装饰的那个外部的被装饰函数,此时,外部我们的自定义的函数的私有属性如_name__、_doc__等还是为我们自定义的函数本身的私有属性,而不会变成闭包的私有属性。
@wraps 可以确保我们调用文档、查看帮助时, 查找到的是我们原函数的属性, 而非所用装饰器的属性

(4) 类的装饰器

def eat(cls):
    """吃东西装饰器"""
    cls.eat = lambda self: print("{0}:我要吃东西".format(self.name))
    return cls

@eat
class Cat(object):
    """猫类"""
    def __init__(self, name):
        self.name = name

if __name__ == '__main__':
    cat = Cat('小黑')
    cat.eat()

输出 -->

小黑:我要吃东西

3.迭代器与生成器
(1) 迭代器
1 实现了__iter__的对象是可迭代的, 而实现了方法__next__的对象是迭代器
2 调用方法__next__( 或next() )时, 迭代器返回下一个值
3 如果迭代器没有可供返回的值, 触发Stoplteration异常
(2) 生成器
1 生成器是一种使用普通函数语法定义的迭代器
2 包含yield语句的函数都被称为生成器
每次使用yield生成一个值后, 函数都将冻结, 即在此停止执行;
被重新唤醒后, 函数将从停止的地方开始继续执行。
3 不使用return返回一个值, 而是可以生成多个值, 每次一个

4.文件备份

import os
import os.path

class FileBackup(object):
    """
    文本文件备份
    """
    def __init__(self, src, dist):
        """
        构造方法
        :param src: 目录 需要备份的文件目录
        :param dist: 目录 备份后的目录
        """
        self.src = src
        self.dist = dist

    def read_files(self):
        """读取src目录下的所有文件"""
        ls = os.listdir(self.src)
        print("ls:{0}".format(ls))
        for l in ls:
            # 循环处理每一个文件/文件夹
            self.backup_file(l)

    def backup_file(self, file_name):
        """
        处理备份
        :param file_name: 文件/文件夹目录
        """
        # 1 判断dist是否存在. 若不存在, 则创建该目录
        if not os.path.exists(self.dist):
            os.makedirs(self.dist)
            print('指定的目录不存在, 创建完成')
        # 2 判断文件是否为我们要备份的文件
        # 拼接文件的完整路径
        full_src_path = os.path.join(self.src, file_name)
        full_dist_path = os.path.join(self.dist, file_name)

        # 首先要判断是否为文件夹, 然后可以借助文件的后缀名进行判断
        if os.path.isfile(full_src_path) and os.path.splitext(file_name)[-1].lower() == '.txt':
            print("full_src_path:{0}".format(full_src_path))
            # 3 读取文件内容
            with open(full_dist_path, 'w', encoding='utf-8') as f_dist:
                print(">>开始备份【{0}】".format(file_name))
                with open(full_src_path, 'r', encoding='utf-8') as f_src:
                    while True:
                        rest = f_src.read(100)
                        if not rest:
                            break
                        # 4 把读取到的文件内容写入到新的文件中
                        f_dist.write(rest)
                    f_dist.flush()
                print(">>>备份完成【{0}】".format(file_name))
        else:
            print("文件类型不符合备份要求, 跳过>>")

if __name__ == '__main__':
    # 要备份的文件目录地址
    # src_path = 'E:\\PyCharmWP\\src'
    # 备份后的目录地址
    # dist_path = 'E:\\PyCharmWP\\dict'

    base_path = os.path.dirname(os.path.abspath(__file__))
    print("base_path:{0}".format(base_path))
    # 要备份的文件目录地址
    src_path = os.path.join(base_path, 'src')
    print("src_path:{0}".format(src_path))
    # 备份后的目录地址
    dist_path = os.path.join(base_path, 'dist')
    print("dist_path:{0}".format(dist_path))

    bak = FileBackup(src_path, dist_path)
    bak.read_files()

5.异常处理
Python学习笔记#10 面向对象的应用(异常处理、文件备份)
自定义异常:

class ApiException(Exception):
    """我的自定义异常"""
    err_code = ''
    err_msg = ''

    def __init__(self, err_code=None, err_msg=None):

        self.err_code = self.err_code if self.err_code else err_code
        self.err_msg = self.err_msg if self.err_msg else err_msg

    def __str__(self):
        return 'Error: {0} - {1}'.format(self.err_code, self.err_msg)


class BadPramsException(ApiException):
    """参数不正确"""
    err_code = '400002'
    err_msg = '两个参数都必须是整数'


def divide(num1, num2):
    # 两个数必须为整数
    if not isinstance(num1, int) or not isinstance(num2, int):
        raise BadPramsException()
    # 除数不能为0
    if num2 == 0:
        raise ApiException('400000', '除数不能为0')
    return num1 / num2


if __name__ == '__main__':
    try:
        rest = divide(5, 's')
        print(rest)
    except BadPramsException as e:
        print("----------")
        print(e)
    except ApiException as err:
        print("出错了")
        print(err)

输出:Error: 400002 - 两个参数都必须是整数