一、装饰器
1.什么是装饰器

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

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


print(func1())
print(func1()())
2.怎么给函数添加功能

方法一:直接修改原函数,问题:给不同的函数添加相同功能的时候代码需要写多遍

def sum1(a, b):
    time1 = time.time()
    print(a+b)
    time2 = time.time()
    print('执行时间',time2-time1)

sum1(10, 20)


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


factorial(5)

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

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


def func2():
    print('你好,世界')



count_execution_time(func2)

方法三:实参高阶函数改进

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


def func4():
    print('**********')


func4 = count_execution_time(func4)
func4()

方法四:装饰器
def 函数名1(func):
   def test(*args,**kwargs):
     func(*args,**kwargs)
     添加新功能
   return test

说明:
函数名1 - 装饰器对应的函数名,也是装饰器的名字(根据需要添加的新功能命名)
func - func指向的就是需要添加新功能的函数
test - 指向的是已经添加完功能的新的函数

练习:写个装饰器在原函数调用结束后打印’end’

def print_end(func):
    def test(*args,**kwargs):
        func(*args,**kwargs)
        print('end')
    return test


@print_end
def func10():
    print('*********')


func10()

写一个装饰器在函数执行前打印start

def print_start(func):
    def test(*args, **kwargs):
        print('start',end='')
        func(*args,**kwargs)
    return test

@print_start
def func11():
    print('***********')


func11()

练习:写一个装饰器,可以返回是数字的函数添加新的功能,让原函数的结果保留2位小数

def func12(func):
    def test(*args, **kwargs):
        result = func(*args,**kwargs)
        if type(result) in (int,float):
            return float(f'{result:.2f}')
        else:
            return result
    return test


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


print(sum3(1.453,2.9343))
print(sum3('a','bbbb'))
print(sum3(1.1111,2))
二、有参数的装饰器

1.有参的装饰器
如果在实现装饰器新增的功能的时候需要额外的数据,那么就可以使用有参装饰器
def 装饰器名称(装饰器的参数列表):
   def test1(func):
    def test2(*args, **kwargs):
     func(*args, **kwargs):
     添加新功能
    return test2
   return test1
练习:写一个装饰器让函数的返回值保留N位小数(返回值是数字的时候)

def constraint_decimals(n):
    def test1(func):
        def test2(*args, **kwargs):
            result = func(*args, **kwargs)
            if type(result) in (float,):
                nums = str(result).split('.')
                return float(f'{nums[0]}.{nums[-1][0:2]}')
        return test2
    return test1


@constraint_decimals(5)
def sum11(a,b):
    return a+b


print(sum11(1.2345646,1))

练习:写一个装饰器在函数结束后打印n个*

def print_x(n):
    def test1(func):
        def test2(*args, **kwargs):
            result = func(*args,**kwargs)
            for x in range(n):
                print('*',end='')
            return result
        return test2
    return test1


@print_x(2)
def sum1(a,b):
    return a*b


print(sum1(1,2))

练习:写一个装饰器可以在原函数的返回值的基础上加上或者减去一个数(针对返回值是数字的函数)

def add_reduce(a,n):
    def test1(func):
        def test2(*args, **kwargs):
            if type(func(*args, **kwargs)) in (int, float):
                if a == '-':
                    return func(*args, **kwargs) - n
                else:
                    return func(*args, **kwargs) + n
            else:
                return func(*args, **kwargs)
        return test2
    return test1


@add_reduce('-', 12)
def sum2(a,b):
    return a+b


print(sum2(1,2))


@add_reduce('+', 10)
def sum3(a,b):
    return a+b


print(sum3('a', 'ccasdsa'))
三、迭代器
1.什么是迭代器

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

2)怎么创建迭代器
创建迭代器的方式有两种:
a.将其他序列转换成迭代器:iter(序列)
b.创建生成器(生成器的本质就是迭代器)

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

iter2 = iter('hello')
print(iter2)    #<str_iterator object at 0x000001F1ECF7A490>
2.获取迭代器的元素(查)

注意:不管以任何方式获取到了迭代器的元素,这个元素在迭代器都不存在了
1)获取单个元素
next(迭代器)

print(next(iter1))
print(next(iter1))
print(next(iter1))
print(next(iter1))

print(list(iter2))
# print(next(iter2))  #StopIteration

2)遍历

iter3 = iter(['name', 'age', 'hello', 'world'])
for x in iter3:
    print(x)

print(next(iter3)) #遍历迭代器的时候已经将元素全部取出,所有没有办法再获取元素了
iter4 = iter(range(5))
next(iter4)
next(iter4)
for x in iter4:
    print(f'x:{x}')
四、生成器
1.生成器

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

2.怎么创建生成器

调用带有yield关键字的函数就能得到一个生成器
调用普通函数:a.执行函数体 b.获取函数返回值

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


print(func1())

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

def func2():
    yield
    print('abc')
    return 100


result = func2()
print(result)
3.生成器怎么生产数据

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

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


gen1 = func3()
print(gen1)     # <generator object func3 at 0x10e8931d0>
print(len(list(gen1)))   # 2


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


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

gen3 = func4(5)
print(len(list(gen3)))    # 2

2)生产器生产的数据是哪些?

def func5():
    yield 10
    yield 100


gen4 = func5()
print(list(gen4))    # [10, 100]
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))
for x in range(10):
    print(x)

print(next(gen5))


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


gen6 = func7(10)
print(next(gen6))
print(next(gen6))
for x in gen6:
    print(f'x:{x}')

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

def create_study_num():
    for x in range(1, 10000):
        yield f'stu{str(x).zfill(4)}'


create_num_gen = create_study_num()
print(next(create_num_gen))
for _ in range(10):
    print(next(create_num_gen))
print(next(create_num_gen))

注意: 每次调用函数的时候都是在创建新的生成器对象

print(next(create_study_num()))     # stu0001
print(next(create_study_num()))     # stu0001

create_num_gen2 = create_study_num()
print(create_num_gen, create_num_gen2)


def study_id(pre: str, width: int):
    for x in range(1, 10**width):
        yield pre+str(x).zfill(width)


create_num1 = study_id('python', 2)
create_num2 = study_id('h5', 3)
print(next(create_num1))
print(next(create_num1))
print(next(create_num2))
print(next(create_num2))