day12-python 装饰器、迭代器、生成器

一 装饰器

1.1 什么是装饰器

装饰器本质就是一个实参高阶函数和返回值高阶函数。
装饰器是用来给函数添加功能的(在不修改原函数的基础上给函数添加功能)。

返回值高阶函数

def func1():
    def func2():
        return 'abc'
    return func2

print(func1())
print(func1()())

1.2 怎么给函数添加功能

  1. 方法一:直接修改原函数,问题:给不同的函数添加相同功能的时候代码需要写多遍
def sum1(a, b):
    time1 = time.time()
    print(a+b)
    time2 = time.time()
    print('执行时间', time2 - time1)


sum1(1234885984556194516958458945, 451665189456566541564569645)


def factorial(n):
    time1 = time.time()
    sum1 = 1
    for x in range(1, n+1):
        sum1 *= x
    print(sum1)
    time2 = time.time()
    print('执行时间:', time2 - time1)


factorial(123)

2.方法二:实参高阶函数
定义一个函数,功能是统计一个函数的执行时间

def count_execution_time(func, *args, **kwargs):
    time1 = time.time()
    func(*args, **kwargs)
    time2 = time.time()
    print('执行时间:', time2 - time1)


def func2():
    print('hello,word')


count_execution_time(func2)


# 解包
def test (a, b, c):
    print(f'a:{a}, b:{b}, c:{c}')


numes = [10, 20, 19]
test(*numes)

print(’=实参高阶函数改进’)
3. 方法三:实参高阶函数改进

def count_execution_time(func):
    def test(*args, **kwargs):
        time1 = time.time()
        func(*args, **kwargs)
        time2 = time.time()
        print(f'执行时间:{time2-time1}')
    return test
  1. 方法四:装饰器
def 函数命1(func):
    def test(*args, **kwargs):
        func(*args, **kwargs)
        添加新的功能
    return test
    
说明:函数命1 -   装饰器对应的函数名,也就是装饰器的名字(根据需要添加的新功能命名)
func    -   func指向的是需要添加新功能的函数
test    -   指向的是已经添加完新的功能的新函数
  • 练习1: 在函数结束后打印end
def print_end(func):
    def test(*args, **kwargs):
        func(*args, **kwargs)
        print(f'end')
    return test


@print_end
def func1():
    print('=============')

func1()
  • 练习2:在函数执行前打印开始
def print_start(func):
    def test(*args, **kwargs):
        print('开始')
        func(*args, **kwargs)

    return test


@print_start
def func2():
    print('=============')


func2()
  • 练习3:写一个装饰器,可以给返回值是数字的函数添加新的功能,让原函数的结果保留两位小数
def two_dici(func):
    def test(*args, **kwargs):
        result = func(*args, **kwargs)
        if type(result) in [int, float]:
            return float(f'{result:.2f}')
        return result
    return test

@two_dici
def sum3(a, b):
    return a + b

print(sum3(1, 2))

二 有参的装饰器

如果在实现装饰器新增的功能的时候需要额外的数据,那么就可以使用有参的装饰器
def 装饰器名称(装饰器的参数列表):

    def test1(func):
        def test2(*args, **kwargs):
            func(*args, **kwargs)
            添加的新功能
        return test2
    return test1
  • 练习:写一个装饰器让函数的返回值保留N位小数(返回是数字的时候)
def N_deci(n):
    def test1(func):
        def test2(*args, **kwargs):
            result = func(*args, **kwargs)
            if type(result) == float:
                nums = str(result).split('.')
                return float(f'{nums[0]}.{nums[-1][0:n]}')
            return result
        return test2
    return test1

@N_deci(4)
def sum1(a, b):
    return a + b

print(sum1(3.1415926, 10))
  • 练习:在函数结束后,打印n个*
def N_xing(n):
    def test1(func):
        def test2(*arg, **kwargs):
            result = func(*arg, **kwargs)
            for i in range(n):
                print('*', end='')
            print()
            return result
        return test2
    return test1

@N_xing(10)
def sum1(a, b):
    return a + b
print(sum1(3, 10))
  • 练习:写一个装饰器可以在原函数的返回值的基础上加上或者减去一个数(针对返回值是数字)
def change_num(str, num):
    def test1(func):
        def test2(*args, **kwargs):
            result = func(*args, **kwargs)
            if type(result) in [int, float]:
                if str == '-':
                    result -= num
                    return result
                if str == '+':
                    result += num
                    return result
            return result
        return test2
    return test1

@change_num('+', 10)
def factorial(n=5):
    sum1 = 1
    for i in range(1, n + 1):
        sum1 *= i
    return sum1
print(factorial())

三 迭代器

3.1 什么是迭代器

  1. 迭代器的特点
    迭代器是一种特殊的容器:a.不能直接查看所有的元素(打印看不到任何元素;不能计算长度) b.如果要读取元素的值,必须将这个元素从迭代器中拿出来,并且取出来后就再也放不进去了,迭代器中就不再有这个元素了)

  2. 创建迭代器
    ① 将其他序列转换成迭代器:iter(序列)
    ② 创建生成器(生成器的本质就是迭代器)

iter1 = iter([10, 20, 30, 40])
print(iter1)  # 
# print(len(iter1))  # TypeError: object of type 'list_iterator' has no len()

iter2 = iter('adasd')

3.2 获取迭代器中的元素(查)

注意:不管以任何方式获取到了迭代器的元素,这个元素在迭代器中就不存在了
①. 获取单个元素
next(迭代器) - 获取迭代器中最前面的一个元素

print(next(iter2))
print(next(iter2))

②. 遍历

for x in 迭代器
iter3 = iter(['name', 'age', 'hello', 'word'])
for x in iter3:
    print(x)

iter4 = iter(range(5))
next(iter4)
next(iter4)
for x in iter4:
    print(x)

四 生成器

4.1 生成器

生成器的本质就是迭代器:
生成器其实是能够产生多个数据的容器,而不是真正同时保存多个数据的容器

4.2 怎么创建生成器

调用带有yield关键字的函数就能得到一个生成器

调用普通话函数:a.执行函数体 b.获取函数返回值

def func1():
    print('abc')
    return 100


print(func1())

调用带有yield关键字的函数:a.不执行函数体 b.获取到的是生成器对象

def func2():
    yield
    print('ABC')
    return 200


result = func2()
list(result)

4.3 生成器怎么生产数据

① 一个生成器能生产多少数据? - 看执行生成器对应的函数,在函数结束的时候能够遇到几次yield,就能生产多少个数据。
② 生成器生产的数据是那些? - 看每次遇到yield,yield后面的数据是什么,产生的数据就是什么

def func3():
    yield
    print('======')
    yield
    print('+++++')


gen1 = func3()
print(gen1)
print(len(list(gen1)))


def func4(n):
    yield
    if n % 2:
        yield
    print('end')

gen2 = func4(4)
print(len(list(gen2)))

生成器生产的数据是那些?

def func5():
    yield 10
    yield 100


gen4 = func5()
print(list(gen4))

4.4 生成器产生数据的原理

调用函数创建生成器对象的时候不会执行函数体:当获取生成器中的元素的时候才会执行函数体。
获取第一个元素的时候从函数开始执行,执行遇到第一个yield就会停下来,并且将yield后面的数据作为这次获取到的元素,
下一次获取元素的时候从上一次结束的位置接着往后执行函数体直到遇到下一个yield,并且将新的yield后面的数据作为新的元素,
以此类推,如果从上次结束的位置开始到函数结束都没遇到yield,那么这个生成器就不再创建数据了,如果是next操作,这个时候会报错
注意:每次调用函数的时候都在创建新的生成器对象

def func6():
    print('第一个:')
    yield 10
    print('第二个:')
    yield 100
    print('第三个:')
    yield 'abc'


gen5 = func6()
print(next(gen5))
print(next(gen5))


def func7(n):
    for m in range(n):
        yield m * 2

gen6 = func7(10)
print(next(gen6))

练习:写一个学号的生成器,能够产生stu0001~stu9999的学号

def stu_num():
    for x in range(1, 100):
        num = str(x).rjust(4, '0')
        yield f'stu{num}'

gen7 = stu_num()
print(list(gen7))

你可能感兴趣的:(python)