不忘初心,
牢记使命。

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

2021-04-01 大聪明 0评论 161 0喜欢

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

迭代器

  1. 可迭代对象
    • 可以更新迭代的 实实在在的值。
    • 内部含有'_iter()'方法的。
    • str list tuple sict set range
    • 优点:操作方法多,操作灵活,直观。
    • 缺点:占用内存。
  2. 迭代器
    • 可以更新迭代的一个工具(数据结构)。
    • 内部含有iter() 且含有 __next(0)_方法的。
    • 文件句柄。
    • 优点:节省内存。惰性机制。
    • 缺点:不直观,速度相对慢,操作方法单一,不走回头路。
# 获取一个对象的所以方法:dir()
s1 = 'sdsdasasdas'
print(dir(s1))
l1 = [1,2,3]
print(dir(l1))
print('__iter__' in dir(l1))  # True
  • 判断一个对象是否是可迭代对象
  • 小结
    字面意思:对象?python中一切皆对象。一个实实在在存在的值,对象。
    专业角度:可迭代对象?内部含有”iter“方法的对象,可迭代对象。
    优点:1.存储的数据直接能显示,比较直观。2.拥有的方法比较多,操作很方便。
    缺点:1.占用内存。2.不能直接通过for循环,不能直接取值。
  • 迭代器
  • 迭代器的定义
    字面意思:更新迭代,器:工具:可更新迭代的工具。
    专业角度:内部含有内部含有__iter__方法并且含有__next__方法的对象就是迭代器。
    可以判断是否是迭代器:有__iter__ and __next__方法在不在dir(对象)
    文件句柄with open('wenjian', encoding='utf-8', mode='w') as f1: print('__iter__' in dir(f1) and '__next__' in dir(f1)) 输出True。

  • 判断一个对象是否是迭代器

  • 迭代器的取值
s1 = 'dasdasdas'
obj = iter(s1)  # s1.__iter__()
# print(obj)  # <str_iterator object at 0x0000022770E5EB70>
print(next(obj))  # d
print(obj.__next__())  # a

l1 = [11,22,33,44,55]
obj = l1.__iter__()
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
- 可迭代对象如何转化成迭代器
- while循环模拟for循环机制
# while循环模拟for循环机制
l1 = [1,2,3,4,5,6,7,8]
# for i in l1:  # 先转换成迭代器
#     print(i)
# 将可迭代对象转换成迭代器 
# StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
obj = l1.__iter__()
while True:
    try:
        print(next(obj))
    except StopIteration:
        break

生成器

  • 生成器

    • 什么是生成器?:python社区,生成器与迭代器看成是一种。生成器的本质就是迭代器。唯一的区别:生成器是我们自己用python代码构建的数据结构。迭代器都是提供的,或者转化得来的。
      • 获取生成器的三种方式:
        • 生成器函数。
        • 生成器表达式。
        • python内部提供的一些。
    • 生成器函数获得的生成器。
    • yield
    • yeild return
    • yeild from
  • 生成器表达式,列表推导式

    • 用一行代码去构建一个比较复杂有规律的列表。
    • 列表推导式:
      • 循环模式:[变量(加工后的变量) for 变量 in iterable]
      • 筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
      • 循环模式
# 函数
def func():
    print(11)
    print(22)
    return 3


ret = func()
print(ret)
# 生成器函数
def func():
    print(11)
    print(22)
    yield 3  # 后边写一个next函数就执行到这里,再写一个就接着执行到下一个yeild
    a = 1
    b = 2
    c = a + b
    print(c)
    yield 4
func()  # 不执行
print(func)
ret = func()
print(ret)  # <generator object func at 0x000001E6D1DB9780>
print(next(ret))  # 一次返回一个yield值
print(next(ret))
# 一个next对应一个yield

return 和 yeild 有什么区别
return:函数中只存在一个return结束函数,并且给函数的执行者饭hi之。
yeild:只要函数中有yield那么它就是生成器函数而不是函数了。
生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next(),

# 吃包子
# 这种一下就把2000个包子存进内存
def func():
    l1 = []
    for i in range(1, 5001):
        l1.append(f'{i}号包子')
    return l1
ret = func()
print(ret)

# 生成器方式
def gen_func():
    for i in range(1, 5001):
        yield f'{i}号包子'

ret = gen_func()
# next一次产生一个 极大节省内存
# 只产生一个数据集里边只有一个包子,这个被拿走下一个放进去。
# 需要200个包子
for i in range(200):
    print(next(ret))
# 再要200个包子 从201开始
for i in range(200):
    print(next(ret))


# yield from  将l1这个列表变成了迭代器返回
def func():
    l1 = [1,2,3,4,5]
    yield from l1  # 将l1这个列表变成了迭代器返回
    """
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5
    """
ret = func()
print(next(ret))  # 得到列表的第一个值
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))

def func():
    list1 = ['都是', '打散', '的撒', '离开']
    list2 = ['空', '发帖人', '解耦i', '同意']
    yield from list1
    yield from list2

g = func()
for i in range(8):
    print(next(g))
# for i in g:
#     print(i)

装饰器

闭包

# 封闭的东西:保证数据的安全。

# 方案1
l1 = []  # 全局变量
li = []
def maker_average(new_value):
    l1.append(new_value)
    return sum(l1) / len(l1)
print(maker_average(100000))
print(maker_average(110000))
# 很多代码
l1.append(666)  # 误用l1
print(maker_average(120000))
print(maker_average(90000))

# 方案2:数据安全,l1不能说全局变量。
# 每次执行时l1的列表都会重新赋值为空列表。
li = []
def maker_average(new_value):
    l1 = []
    l1.append(new_value)
    return sum(l1) / len(l1)
print(maker_average(100000))
print(maker_average(110000))
# 很多代码
print(maker_average(120000))
print(maker_average(90000))

# 方案3:闭包。

def maker_average():
    l1 = []
    def average(new_value):
        l1.append(new_value)
        return sum(l1) / len(l1)
    return average


avg = maker_average()  # average
# 查看自由变量,存在即为闭包
print(avg.__code__.co_freevars)  # ('l1',)
print(avg(100000))
print(avg(110000))
print(avg(120000))
print(avg(90000))
# 全局
# maker_average:地址1
# avg: 地址2
# 临时名称空间
# l1 = []
# average: 地址2

# 闭包:  多用于面试题:什么是闭包?闭包有什么作用?
# 1. 闭包只能存在嵌套函数中。
# 2. 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 被引用的非全局变量也称作自由变量,这个自由变量就会与内层函数产生一个绑定关系,
# 自由变量不会在内层中消失。
# 闭包的作用:保证程序的安全性。

# 如何判断一个嵌套函数是不是闭包?
# 1. 闭包只能存在嵌套函数中。
# 2. 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 例一:
def wrapper():
    a = 1
    def inner():
        print(a)
    return inner
ret = wrapper()
# 是
# 例二:
a = 2
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()
# 不是
#
# # 例三:
#
def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)
# 是

# 如何用代码判断闭包
def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)
print(ret.__code__.co_freevars)  # ('a', 'b') 有两个自由变量

装饰器的推导

# 装饰器:装饰,装修,房子就可以住,如果装修,不影响你住,而且体验更加,
# 让你生活中增加; 很多功能,洗澡,看电视
# 器:工具。
# 开放封闭原则:
# 开放:对代码的扩展是开放的。
# 封闭:对源码的修改是封闭的。
# 装饰器:完全遵循开放封闭原则。
# 装饰器:在不改变原函数代码以及调用方式的前提下给其增加新的功能。
# 装饰器就是一个函数。
#
# 版本一:A 写一些代码,测试一下index函数的执行效率。
def index():
    """有很多代码"""
    time.sleep(2)  # 模拟的网络延迟
    print('欢迎登录博客园首页')

def dariy():
    """有很多代码"""
    time.sleep(3)  # 模拟的网络延迟
    print('欢迎登录博客园日记首页')

# # 如果测试别人的代码,要重复复制
import time
# print(time.time())  # 格林尼治时间。 1599562276.1415439
star_time = time.time()

index()

end_time = time.time()
print(end_time - star_time)
# 版本2:利用函数,解决代码重复使用问题
import time
def index():
    """有很多代码"""
    time.sleep(2)  # 模拟的网络延迟
    print('欢迎登录博客园首页')

def dariy():
    """有很多代码"""
    time.sleep(3)  # 模拟的网络延迟
    print('欢迎登录博客园日记首页')

def timmer(f):

    star_time = time.time()

    f()

    end_time = time.time()
    print(end_time - star_time)


timmer(index)
# 版本二还是有问题:原来index函数源码没有改变,给原函数添加了一个新的功能,
# 测试原函数效率的功能。满足开放封闭原则?原函数的调用方式改变了

# 版本三:不能改编原函数的调用方式。
import time
def index():
    """有很多代码"""
    time.sleep(2)  # 模拟的网络延迟
    print('欢迎登录博客园首页')

def dariy():
    """有很多代码"""
    time.sleep(3)  # 模拟的网络延迟
    print('欢迎登录博客园日记首页')

def timmer(f):

    def inner():
        star_time = time.time()
        f()
        end_time = time.time()
        print(end_time - star_time)

    return inner


# timmer(index)  # index()
# ret = timmer(index)   # inner
# ret()  # inner()
"""最基础的装饰器"""
index = timmer(index)  # inner
index()  # inner()

def func():
    print('in func')
def func1():
    print('in func1')

func()
func = 666
func()  # TypeError: 'int' object is not callable

# 版本四:继续研究
import time
def index():
    """有很多代码"""
    time.sleep(2)  # 模拟的网络延迟
    print('欢迎登录博客园首页')

def timmer(f):
    # f = index
    def inner():
        star_time = time.time()
        f()
        end_time = time.time()
        print(end_time - star_time)

    return inner


index = timmer(index)
index()

# 版本五:python做了一个优化:提出了一个语法糖的概念。
# @装饰器名
# 标准版的装饰器
# import time
# # 装饰器写在最上面
# # timmer装饰器
def timmer(f):
    # f = index
    def inner():
        star_time = time.time()
        f()
        end_time = time.time()
        print(end_time - star_time)

    return inner


@timmer  # index = timmer(index)
def index():
    """有很多代码"""
    time.sleep(0.6)  # 模拟的网络延迟
    print('欢迎登录博客园首页')
    return 666


@timmer  # dariy = timmer(dariy)
def dariy():
    """有很多代码"""
    time.sleep(0.3)  # 模拟的网络延迟
    print('欢迎登录博客园日记首页')


# index = timmer(index)
# index()

# dariy = timmer(dariy) @timmer
# dariy()

ret = index()
print(ret)  # None 有返回值 不行啦?

dariy()

# 版本六:被装饰函数有返回值
import time
# 装饰器写在最上面
# timmer装饰器
def timmer(f):
    # f = index
    def inner():
        star_time = time.time()
        r = f()  # 把返回值给他返出去再
        # print(f'真正的index函数返回值{f()}')  # index()
        end_time = time.time()
        print(end_time - star_time)
        return r

    return inner


@timmer  # index = timmer(index)
def index():
    """有很多代码"""
    time.sleep(0.6)  # 模拟的网络延迟
    print('欢迎登录博客园首页')
    return 666


# 加上装饰器不应该改变原函数的返回值,所以666应该返回给我下面的ret
# 但是下面这个ret实际接收的是inner函数的返回值,而666返回给的是装饰器里面的
# f(),也就是r,我们现在要解决的问题就是将r给inner的返回值。
ret = index()
print(ret)

# 版本七: 最标准版的装饰器。
import time
# 装饰器写在最上面
# timmer装饰器
def timmer(f):
    # f = index
    def inner(*args, **kwargs):
        # 函数的定义:* 聚合 args=('李素琴', 18)
        star_time = time.time()
        r = f(*args, **kwargs)  # 把返回值给他返出去再
        # 函数的执行:* 打散:f(*iterable) --> f(*('李素琴', 18)) --> f('李素琴', 18)
        # print(f'真正的index函数返回值{f()}')  # index()
        end_time = time.time()
        print(end_time - star_time)
        return r

    return inner


@timmer  # index = timmer(index)
def index(name):
    """有很多代码"""
    time.sleep(0.6)  # 模拟的网络延迟
    print(f'欢迎{name}登录博客园首页')
    return 666


@timmer  # dariy = timmer(dariy)
def dariy(name, age):
    """有很多代码"""
    time.sleep(0.3)  # 模拟的网络延迟
    print(f'欢迎{age}岁的{name}登录博客园日记首页')


dariy('李素琴', 18)  # inner(name, age)

index('那亲')  # inner('那亲')


# 标准版的装饰器


def wrapper(f):
    def inner(*args, **kwargs):
        """添加额外的功能:执行被装饰函数之前的操作"""
        ret = f(*args, **kwargs)
        """添加额外的功能:执行被装饰函数之后的操作"""
        return ret
    return inner

发表评论 取消回复

电子邮件地址不会被公开。

请输入正确格式的qq邮箱
请输入以http或https开头的URL,格式如:https://libo_sober.top