12.装饰器,迭代器,生成器

  • 引子
  • 装饰器

  • 装饰器语法糖

  • 无参装饰模板

  • 叠加多个装饰器

  • 迭代器

  • 生成器


  • 装饰器

  • 1、什么是装饰器

    装饰器就是一个用来为被装饰对象添加新功能的工具
    装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
  • 2、为何要用装饰器

    在添加新功能的时候要遵循开放封闭的原则:一旦软件上线运行之后,应该对修改源代码封闭,对扩展功能开放
    原则:
    1、不修改函数内的源代码
    2、不修改函数的调用方式
    装饰器就是在遵循原则1和2的前提下,为被装饰对象添加上新功能
  • 3、如何实现装饰器

    函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,
    都是“函数嵌套+闭包+函数对象”的组合使用的产物
# 需求:在不修改index函数的源代码和调用方式的前提下为其添加统计运行时间的新功能
import time    # 导入时间模块

def index():   # 一个函数
    time.sleep(1)   # 模拟index运行的一点时间   源代码
    print('from index')                    # 源代码

index()     # 调用方式



# 方案一:失败
# 问题:确实添加了新功能,但是修改了源代码
import time

def index():
    start = time.time()   # 统计开始时间
    time.sleep(1)
    print('from index')
    stop = time.time()    # 统计结束时间
    print('run time is %s' %(stop - start))

index()


# 方案二:失败
# 问题:看似装饰器的效果实现了,但是引发了重复代码的问题
import time

def index():
    time.sleep(1)
    print('from index')

start = time.time()
index()
stop = time.time()
print('run time is %s' %(stop - start))


# 方案三:失败
# 问题:把要装饰的代码丢进函数里,为的是重复调用,但是把index写死了,就无法用上装饰器
import time

def index():
    time.sleep(1)
    print('from index')

def wrapper():
    start = time.time()
    index()
    stop = time.time()
    print('run time is %s' %(stop - start))

wrapper()
wrapper()


# 方案四:失败
# 问题: 把index函数名变成变量名func,要为wrapper函数的函数体代码传参数,
#       直接用参数传参,装饰对象虽然写活了,但只能装饰index
import time

def index():
    time.sleep(1)
    print('from index')

def wrapper(func):
    start = time.time()
    func()              # 被装饰函数
    stop = time.time()
    print('run time is %s' %(stop - start))

wrapper(index)


# 方案五:====》闭包函数
import time

def index():       # index = 被装饰对象index函数的内存地址
    time.sleep(1)
    print('from index')
    
def outter(func):    # func = 被装饰对象index函数的内存地址
	def wrapper():
    	start = time.time()
    	func()    # 被装饰对象index函数的内存地址
    	stop = time.time()
    	print('run time is %s' %(stop - start))
    return wrapper    # 千万别加括号
                       # outter=(被装饰对象index函数的内存地址)
index = outter(index)  # outter-》返回wrapper函数的内存地址-》index = wrapper函数的内存地址
print(index)
index()

# 方案六:====》将wrapper伪装成被装饰的函数,name应该装的尽可能像一些
import time

def index():   # index = 被装饰对象index函数的内存地址
    time.sleep(1)
    print('from index')
    
def home(name):
    time.sleep(2)
    print("welcome %s to home page" %name)
    return 123
    
def outter(func):     # func = 被装饰对象index函数的内存地址
	def wrapper(*args,**kwargs):
		start = time.time()
		res = func(*args,**kwargs)  # 被装饰对象index函数的内存地址(x,y,z)
		stop = time.time()
		print('run time is %s' %(stop - strt))
   		return res
	return wrapper

index = outter(index) 
home = outter(home)  # 偷梁换柱:home这个名字指向的wrapper函数的内存地址

res = home("geng")  # wrapper("geng")
print(res)

res = index()   # wrapper()
print(res)
          
  • 装饰器语法糖

    语法糖:让你开心的语法
import time
from functools import wraps

def outter(func):  # func = 被装饰对象index函数的内存地址
    @wraps(func)
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)  # 被装饰对象index函数的内存地址(x,y,z)
        stop = time.time()
        print('run time is %s' %(stop - start))
        return res
    return wrapper  # 千万别加括号

# 在被装饰对象正上方的单独一行写@装饰器名字
@outter  # index = outter(index)  # outter(被装饰对象index函数的内存地址) -> 返回wrapper
                                  # 函数的内存地址---> index = wrapper函数的内存地址
def index():  # index = 被装饰对象index函数的内存地址
    """这是index函数"""
    time.sleep(1)
    print('from index')

@outter  # home = outter(home)
def home(name):
    time.sleep(2)
    print("welcome %s to home page" %name)
    return 123

# res = home("egon")  # wrapper("egon")
# print(res)

# res = index()  # wrapper()
# print(res)

print(index.__name__)
print(index.__doc__)
  • 无参装饰器模板

# 闭包函数 函数 (被装饰函数)
def outter(func):     
    def wrapper(*args,**kwargs):   # wrapper函数,在wrapper内写被装饰的函数
        res = func(*args,**kwargs)  # *args,**keargs让参数保持一致,装的像一点
        return res       # 让返回值保持一致,装的像一点
    return wrapper     
# 以上的装饰器为被装饰对象啥新功能也没添加,就是无参装饰器模板

# 认证功能装饰器模板
def login(func):
    def wrapper(*args,**kwargs):
        name = input('username>>>: ').strip()
        pwd = input('password>>>: ').strip()
        if name == 'egon' and pwd == "123":
            res = func(*args,**kwargs)
            return res
        else:
            print('账号密码错误')
    return wrapper

@login
def index():
    print('index')

index()

  • 叠加多个装饰器

def deco1(func1):     # func1 = wrapper2的内存地址
    def wrapper1(*args,**kwargs):
        print('==========>wrapper1')
        res1 = func1(*args,**kwargs)
        print('end1')
        return res1
    return wrapper1

def deco2(func2):    # func2 = wrapper3的内存地址
    def wrapper2(*args,**kwargs):
        print('=========>wrapper2')
        res2 = func2(*args,**kwargs)
        print('end2')
        return res2
    return wrapper2

def deco3(func3):    # func3 = 被装饰index的内存地址
    def wrapper3(*args,**kwargs):
        print('=========>wrapper3')
        res3 = func3(*args,**kwargs)
        print('end3')
        return res3
    return wrapper3
#                           index = wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址) -> wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址) -> wrapper2的内存地址
@deco3 # deco3(被装饰index的内存地址)-> wrapper3的内存地址
def index():
	print('index......')
	
index()           

  • 迭代器

  • 1、什么是迭代器

    迭代器指的是迭代取值的工具

    什么是迭代?

    迭代就是一个重复的过程,但是每一次重复都是在上一次的基础之上进行的,单纯的重复不是迭代
nums = [111, 222, 333]  # 遍历列表,索引,迭代取值
nums = "hello"          # 遍历字符串,索引,迭代取值

def get(l):     # 函数就是取值工具
    i = 0
    while i < len(l):
    	print(l[i])
    	i += 1

get(nums)   
  • 2、为何要用迭代器

    1、迭代器提供了一种不依赖于索引的、通用的迭代取值方案
    2、节省内存
  • 3、如何用迭代器

    可迭代的对象
    1、内置有_iter_方法的对象都叫可迭代的对象
    调用可迭代对象的_iter_方法,返回的是它的迭代器
    迭代器对象
    1、内置有_next_方法
    2、内置有_iter_方法
    调用迭代器对象的_iter_方法得到的它自己,就跟没调用一样
    调用迭代器对象的_next_方法返回下一个值,不依赖索引
    可以一直调用_next_直到取干净,则抛出异常StopIteration
# 内置的类型都是可迭代的对象
"abc"        # 字符串
[1,2,3]      # 列表
(1,2,3)      # 元组
{'k1':111}   # 字典
{1,2,3}      # 集合
f = open('a.txt',mode='wt')  # 文件对象本身就是迭代器对象

# 例1:
# 迭代器直接取值方式
nums = [111,222,3333]
nums_iter = nums.__iter__()     # 将可迭代对象转为迭代器版本,源列表没动,产生新的
print(nums_iter)
print(nums_iter.__next__())
print(nums_iter.__next__())
print(nums_iter.__next__())
print(nums_iter.__next__())   # 抛出异常 StopIteration


x = iter(nums)
print(next(x))
print(next(x))
print(next(x))

# 例2:
# 用while循环解决迭代器取值
nums = [111,222,3333]
nums_iter = iter(nums)  # 把可迭代对象先转成迭代器对象
while True:
    try:   # 异常处理,next到第四次检测出异常
        res = next(nums_iter)
        print(res)
    except StopIteration:  # 捕捉异常,结束循环
        break

# 1、先调用in后那个对象的__iter__方法,拿到迭代器对象
# 2、 res = next(迭代器对象),执行一次循环体代码
# 3、循环往复步骤2,直到值取干净抛出异常StopIteration,for会捕捉异常结束循环

nums = [111,222,3333]   # 用for循环两行代码解决例2
for res in nums:
    print(res)
  • 迭代器的优缺点

    优点:

    ​ 1、为序列和非序列类型提供了一种统一的迭代取值方式。

    ​ 2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在 内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内 存大小的限制,可以存放的值的个数是有限的。缺点

    缺点:

    ​ 1、除非取尽,否则无法获取迭代器的长度

    ​ 2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会 停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如 果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。


  • 生成器

  • yield 与 return的异同

    相同点:返回值层面用法一样
    不同点:return只能返回值一次,而yield可以返回多次
def func():
    print('xxx')
    yield 111
    print('yyy')
    yield 222
    print('zzz')
    yield 333
    print('mmmm')  # 报错,抛出异常 StopIteration 取到这里就取干净了

# 当函数内出现yield关键字,再调用函数并不会触发函数体代码的运行,会返回一个生成器
g = func()
# print(g)  # 生成器就是一种自定义的迭代器


res = next(g)
print(res)

res = next(g)
print(res)

res = next(g)
print(res)

next(g)

# 例:
# 一出场就是一只老母鸡,可以存无穷个值的类型,但每次只能拿一个
def func():
    res = 1
    while True:
        yield res
        res += 1

g = func()  # 得到一个迭代器

print(next(g))   # 打印next触发代码运行,可以返回无数个值
print(next(g))
print(next(g))



nums =[111,22,33]

x = iter(nums)
for res in x:  # x.__iter__()
    print(res)
print('='*50)

for res in x:  # x.__iter__()
    print(res)


for res in nums:  # nums.__iter__()
    print(res)
print('='*50)
for res in nums:  # nums.__iter__()
    print(res)
原文地址:https://www.cnblogs.com/gfeng/p/14213135.html