十三、装饰器&迭代器&生成器与异常处理

一、装饰器

1.什么是装饰器

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

2.装饰器的实现

  • 1.定义一个函数(接收一个参数)来作为装饰器函数,并在装饰器函数体内定义一个函数,用该函数来接收参数并实现装饰内容,最后用装饰器函数返回该函数
  • 2.在需要使用装饰器的目标函数上使用注解@装饰器函数名即可
  • 3.可以叠加使用装饰器,执行顺序为从下到上(由内向外)
  • 4.Python代码是自上而下一次执行的,所以如果装饰器和被装饰函数在同一个.py文件内,则装饰器函数必须在被装饰函数的前面
  • 示例
def log_in(func):
    def work(name, age):
        print("start...in...")
        func(name, age)
        print("end...in...")
    return work


def log_out(func):
    def work(name, age):
        print("start...out...")
        func(name, age)
        print("end...out...")
    return work


@log_in
@log_out
def hello(name, age):
    print("hello {0}, you are {1} years old".format(name, age))


if __name__ == "__main__":
    hello("Schuyler", 22)

在这里插入图片描述

3.带参数的装饰器

  • 1.有时候需要对不同的函数进行不同的装饰,此时可以通过给装饰器传递不同的参数来达到不同的装饰效果
  • 2.带参数的装饰器实现方式很简单,就是将上文实现的装饰器放入一个函数中,用该函数来接收装饰器的参数,最后由该函数来返回装饰器即可
  • 示例
def log(info):
    def log_in(func):
        def work(a, b):
            print("start...{}...".format(info))
            result = func(a, b)
            print("end...{}...".format(info))
            return result
        return work
    return log_in


@log("sum")
def hello(a, b):
    return a + b


if __name__ == "__main__":
    print("the result is {}".format(hello(3, 2)))

在这里插入图片描述

4.装饰器之@wraps

  • 1.被装饰函数的一些基础默认属性如__name____doc__等等,会在装饰之后被覆盖掉,为了能让这些属性正常的显示,可以使用@wraps(func)注解
  • 2.@wraps注解需要通过from functools import wraps导入
from functools import wraps


def log(info):
    def log_in(func):
        @wraps(func)
        def work(a, b):
            print("start...{}...".format(info))
            result = func(a, b)
            print("end...{}...".format(info))
            return result
        return work
    return log_in


@log("sum")
def hello(a, b):
    """hello函数的注释文档"""
    return a + b


if __name__ == "__main__":
    print(hello.__doc__)
  • 3.@wraps(func)注解是Python提供的,会将装饰器实现函数的默认属性重新赋值为被装饰函数的默认属性,等效于下列所示
def log(info):
    def log_in(func):
        def work(a, b):
            print("start...{}...".format(info))
            result = func(a, b)
            print("end...{}...".format(info))
            work.__doc__ = func.__doc__
            work.__name__ = func.__name__
            # ......还有很多属性,这里不写出来了
            return result
        return work
    return log_in

5.类装饰器

  • 1.类装饰器的实现,其实就是定义一个函数来接收类,对类进行装饰后再返回该类
  • 2.在需要被装饰类上使用注解@类装饰器名
def new_func(self):
    print("我是一只猫咪,我叫{}".format(self.name))


def say_info(cls):
    cls.say_info = new_func
    # cls.say_info = lambda self: print("我是一只猫咪,我叫{}".format(self.name))
    return cls


@say_info
class Cat(object):
    def __init__(self, name):
        self.name = name


if __name__ == "__main__":
    cat = Cat("小白白")
    cat.say_info()

在这里插入图片描述

  • 2.带参数的类装饰器
def say(info):
    def say_info(cls):
        cls.say_info = lambda self: print("我是一只{0},我叫{1}".format(info, self.name))
        return cls
    return say_info


@say("小狗")
class Cat(object):
    def __init__(self, name):
        self.name = name


if __name__ == "__main__":
    cat = Cat("小白白")
    cat.say_info()

在这里插入图片描述


二、迭代器与生成器

1.什么是迭代器

  • 1.实现了__iter__方法的对象是可迭代对象,通过dir我们可以看到,列表、元祖、字典等都是可迭代对象,一个健康的可迭代对象在调用__iter__()方法后应该能得到一个迭代器
  • 2.一般而言,可以被for ... in ...循环的都是可迭代对象
  • 3.实现了__next__方法的对象便是迭代器对象,迭代器对象也实现了__iter__方法用来返回自己,__next__方法用来返回下一个值
  • 4.如果迭代器没有可供返回的值,则会出现StopIteration异常
  • 5.综上所述,可迭代对象不一定是迭代器对象,迭代器对象一定是可迭代对象
class Miter:
    """我就是一个迭代器"""
    v = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.v += 1
        return self.v


if __name__ == "__main__":
    m = Miter()
    for i in m:
        print(i)

2.next()

  • python提供了内置的next(迭代器对象)函数,效果与迭代器对象.__next__()相同

3.生成器

  • 1.生成器是一种使用普通函数语法定义的迭代器,是一种特殊的迭代器,因此也是可迭代对象
  • 2.包含yield语句的函数都被称为生成器
  • 3.生成器不使用return来返回值,而是可以用多个yeild语句生成多个值,一次返回一个
  • 4.每次使用yield生成一个值后,函数都将暂时冻结,被重新唤醒后,再从停止的地方开始继续执行
  • 5.在Python中,我们可以通过(x for x in range(9))得到一个生成器,这有点类似Python的三种内置生成式,但要注意这样得到的是一个生成器不是一个元祖,不能直接通过len()等方式得到长度,可以先转化为列表后再获取长度len(list(生成器))
def pow_number():
    yield 1
    yield 2
    yield 3


def pow_number2():
    return (a for a in [4, 5, 6])


def pow_number3():
    for a in [7, 8, 9]:
        yield a


if __name__ == "__main__":
    for x in pow_number():
        print(x)
    for x in pow_number2():
        print(x)
    result = pow_number3()
    print(next(result))
    print(result.__next__())

在这里插入图片描述

  • 注意示例中的pow_number3,需要获取迭代器对象后再去迭代,如果直接用pow_number3本身来迭代,那么每次都只会得到第一个值

4.用迭代器/生成器模拟range函数

class IterRange(object):
    """迭代器模拟range函数"""
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        self.temp = self.start
        self.start += 1
        if self.temp >= self.end:
            raise StopIteration
        return self.temp


def inter_range(start, end):
    """生成器模拟range"""
    temp = start -1
    while True:
        temp += 1
        if temp >= end:
            break
        yield temp


if __name__ == "__main__":
    ir = IterRange(0, 5)
    print(list(ir))
    print("-"*20)
    ir2 = inter_range(0, 5)
    print(list(ir2))

在这里插入图片描述

三、异常处理

1.异常概述

  • 1.每个异常都是某个类的实例
  • 2.异常类可以使用raise关键字抛出
  • 3.发生了异常如果不捕获,将会一层一层往外传递,直至异常被捕获或者程序终止执行
  • 4.Python有一些常用当内置异常类
    在这里插入图片描述

2.异常捕获

  • 1.try ... except ... 可以用来捕获异常并作出指定的处理方案,默认捕获所有异常
  • 2.当希望捕获指定异常时,例如捕获ZeroDivisionError异常,可以用如下语法
    try:
        print(1/0)
    except ZeroDivisionError:
        print("除数不能为0")
    
  • 3.当希望捕获多个指定异常并做同样处理时,可以用如下语法
    try:
        print(1/0)
    except (ZeroDivisionError, TypeError):
        print("出现异常")
    
  • 4.指定捕获的异常可以使用别名,便于操作
    try:
        print(1/0)
    except (ZeroDivisionError, TypeError) as e:
        print("出现异常")
        print(e)
    
    
  • 5.当需要对多个指定异常作出不同的处理时,可以设置多个except
    try:
        print(1/0)
    except ZeroDivisionError as e:
        print("除数不能为0")
        print(e)
    except TypeError as e:
        print("类型错误")
        print(e)    
    
    • 需要注意的是,一般范围较广的异常类(父类)放在最下面,这样可以防止异常提前捕获
  • 6.当需要在处理完异常后,执行某些操作,可以如下语法
    try:
        file = open("/Users/schuyler/Desktop/python-project/ok/ok1.txt", "r", encoding='utf-8')
        file.write("o")
    except io.UnsupportedOperation as e:
        print(e)
    finally:
        file.close()
        print("文件已经关闭")
    
  • 7.异常处理,也可以进行嵌套的,例如
    try:
        file = open("/Users/schuyler/Desktop/python-project/ok/ok1.txt1", "r", encoding='utf-8')
        file.write("o")
    except (io.UnsupportedOperation, FileExistsError, FileNotFoundError) as e:
        print(e)
    finally:
        try:
            file.close()
            print("文件已经关闭")
        except NameError:
            print("文件名字错误,文件无法打开")
    

3.自定义异常类

  • 自定义异常类十分简单,只需要继承Exception类即可
class MyException(Exception):
    """自定义异常父类"""
    err_code = "0"
    err_msg = "出现自定义异常"

    def __init__(self, err_code=None, err_msg=None):
        self.err_code = err_code if err_code else self.err_code
        self.err_msg = err_msg if err_msg else self.err_msg

    def __str__(self):
        return "Error: {code}-{msg}".format(code=self.err_code, msg=self.err_msg)


class DivisorZeroException(MyException):
    err_code = "3000"
    err_msg = "除数不能为0"


class ParamTypeException(MyException):
    err_code = "3001"
    err_msg = "参数必须是数字类型"


def test_div(num1, num2):
    if not str(num1).isdigit() or not str(num2).isdigit():
        raise ParamTypeException
    if num2 == 0:
        raise DivisorZeroException
    return int(num1) / int(num2)


if __name__ == "__main__":
    try:
        print(test_div(2, 0))
    except MyException as e:
        print(e)

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值