Python基础之迭代器、生成器、装饰器

目录

1、迭代器

1.1 什么是迭代

1.2 如何判断可迭代对象

1.3 创建迭代器

2、生成器

2.1 什么是生成器

2.2 创建生成器

2.3 生成器使用案例

3、装饰器

3.1 闭包

3.2 装饰器

3.3 装饰器执行时间

3.4 装饰器传参

3.5 装饰器返回值 

3.6 通用装饰器

3.7 装饰器带参数


1、迭代器

1.1 什么是迭代

迭代是通过重复执行的代码处理相似的数据集的过程,并且本次迭代的处理数据要依赖上一次的产生的结果为下一次产生结果的初识状态,如果中途中有任何停顿,都不能算是迭代。

常见的可迭代对象有:

集合数据类型,如list,dict,set,str等

生成器(generator),包括生成器和带yield的生成器函数

在python中,如果给定一个列表、元组、字符串...,我们可以通过for循环来遍历,这种遍历我们称之为迭代(iteration)

1.2 如何判断可迭代对象

可迭代对象具有__iter__()方法,可迭代对象可以使用for循环遍历,我们导入 form collections import Iterable 模块,使用isinstance(变量,Iterable),判断一个变量是否为可迭代对象返回True表明是可迭代对象,False则不是可迭代对象。

# 3.6之前的版本collections不需要加.abc,3.7之后会提示添加.abc,3.10版本停用不加.abc的情况。
from collections.abc import Iterable  


print(isinstance([1, 2, 3, 4], Iterable))
print(isinstance((1, 2, 3, 4), Iterable))
print(isinstance('python', Iterable))
print(isinstance(12345, Iterable))

1.3 创建迭代器

迭代器是一个可以记住遍历位置的对象

迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。迭代器只能往前不会后退

把一个类作为一个迭代器使用需要在类中是实现两个方法__iter__()和__next__()

__iter__()方法返回对象本身,即:self

__next__()方法返回下一个数据,如果没有数据了,就需要抛出一个StopIterable异常

class MyNumbers:
    '''
    创建一个返回数字的迭代器,初始值为1,逐步递增1,在5次迭代后停止执行
    '''

    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        if self.a <= 5:
            x = self.a
            self.a += 1
            return x
        else:
            raise StopIteration


myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
    print(x)

2、生成器

2.1 什么是生成器

通过列表生成式,我们可以直接创建一个列表,但是受到内存限制,列表容量肯定有限。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那么我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量空间,在python中,这种一边循环一边计算的机制,称为生成器generator。生成器的这个特性,为解决无限个变量和有限内存之间矛盾的问题,提供了解决方案,或者为优化内存使用效率提供了途径。

在函数内使用yield关键字,每次调用函数类似于对迭代器执行next()方法,生成器实际是一种特殊的迭代器。

2.2 创建生成器

方式1

把列表生成式的[]改成()

# 创建一个生成器、
G = (x * 2 for x in range(10))
print(G)
print(next(G))

方式2

当类似列表生成式的for循环无法实现的时候,可以使用函数实现

# 利用函数实现生成器
def CreatNum():
    a, b = 0, 1
    for i in range(5):
        print(b)
        a, b = b, a+b


CreatNum()

函数里面有yield,就变成生成器

# 利用函数实现生成器
def CreatNum():
    print("-----start-----")
    a, b = 0, 1
    for i in range(5):
        print("-----1-----")
        yield b
        print("-----2-----")
        a, b = b, a+b
        print("-----3-----")
    print("-----stop-----")


a = CreatNum()
print(a)
print(next(a))
print("-"*30)
print(next(a))


CreatNum()

输出结果: 

<generator object CreatNum at 0x0000011CE7786030>
-----start-----
-----1-----
1
------------------------------
-----2-----
-----3-----
-----1-----
1

a是一个生成器对象,当第一次调用next(a)的时候,生成器从上往下执行,执行到yeild b的时候停止并返回b的值,当再次调用next(a)的时候,程序根据原来停止的地方接着往下执行,循环执行到yeild b的时候又停止并返回b的值。

方式3

在上面的例子中,我们在循环过程中不停调用yield,就不会中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代,并且不需要关心StopIteration错误

def creatNum():
    print("-----start-----")
    a, b = 0, 1
    for i in range(5):
        yield b
        a, b = b, a+b
    print("-----stop-----")

    
a = creatNum()


for num in a:
    print(num)

但是用for循环调用generator时,发现拿不到generator的return语句的返回值,如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration中

def creatNum():
    print("-----start-----")
    a, b = 0, 1
    for i in range(5):
        yield b
        a, b = b, a+b
    print("-----stop-----")


a = creatNum()


while True:
    try:
        x = next(a)
        print("value:%d" % x)
    except StopIteration as e:
        print("生成器返回值:%s" % e.value)
        break

2.3 生成器使用案例

start = time.time()
print(sum([i for i in range(1000000)]))
end = time.time()
print("使用列表推导式的耗时", end-start)
print("-----这是一个分割线-----")

start1 = time.time()
print(sum(i for i in range(1000000)))
end1 = time.time()
print("使用生成器的耗时", end1-start1)

可以看出使用生成器的耗时会更少,数字继续增大的话,列表推导式大概率会内存爆掉,因为生成器是使用才迭代,只迭代一次不会存在内存中,所以内存会比较稳健的求出结果。

读取大文件

使用生成器的挂起并可重新在挂起点运行的特点,可以实现按需,每次读取指定大小文件,避免读取文件时,因为一次性读取内容过多,导致内存溢出问题

def read_file(fpath):
    BLOCK_SIZE = 1024
    with open(fpath, 'r', encoding='utf-8') as f:
        while True:
            block = f.read(BLOCK_SIZE)
            if block:
                yield block
            else:
                return 
            
            
a = read_file('C:/readme')
print(next(a))

3、装饰器

3.1 闭包

闭包就是能够读取其他函数内部变量的函数,可以理解成定义在一个函数内部的函数,本质上,闭包是将函数内部和函数外部连接起来的桥梁。

def test(number):

    print("--1--")

    def test_in(number2):
        print("--2--")
        print(number+number2)

    print("--3--")
    return test_in


ret = test(100)
print("-"*30)
ret(1)
ret(10)

先调用外面函数传递一个默认的number值,用ret去指向返回内部函数的引用,接下来调用ret的时候就会在之前调用外部函数的基础上进行计算。

3.2 装饰器

在程序中不会对原来的函数进行改变,还要添增新的功能,调用函数时接口没有变化。

装饰器可以基于函数实现,也可以基于类实现,使用步骤:

定义装饰函数(类)

定义业务函数

在业务函数上添加@装饰函数/类名

def w1(func):
    """装饰器函数"""
    def inner():
        func()
        print("这是添加的新功能")
    return inner

@w1
def f1():
    """业务函数"""
    print("--f1--")


def f2():
    """业务函数"""
    print("--f2--")


f1()
f2()

多个装饰器同时使用

def w1(fn):
    """装饰器函数"""

    def inner():
        print("-----1-----")
        return "<b>" + fn() + "<b>"

    return inner


def w2(fn):
    """装饰器函数"""

    def inner():
        print("-----2-----")
        return "<1>" + fn() + "<i>"
    return inner

@w1
@w2
def f1():
    """业务函数"""
    print("-----3-----")
    return "aaa---bbb---ccc"


ret = f1()
print(ret)

 先调用w1,再调用w2,w2调用结束,w1再调用结束

3.3 装饰器执行时间

python解释器执行到装饰器装饰地方代码,装饰器就开始装饰,不需要等到函数调用时候开始装饰

def w1(fn):
    """装饰器函数"""

    print("---正在装饰---")

    def inner():
        print("---正在验证---")
        fn()

    return inner

@w1
def f1():
    """业务函数"""
    print("---2---")
    return "aaa---bbb---ccc"

代码执行到@w1就开始装饰,我们没有调用f1()都输出了---正在装饰---,我们调用的是装饰完后的结果。

def w1(fn):
    """装饰器函数"""
    print("正在装饰1")

    def inner():
        print("---正在验证1---")
        fn()
    return inner()


def w2(fn):
    """装饰器函数"""
    print("正在装饰2")

    def inner():
        print("---正在验证2---")
        fn()

    return inner

@w1
@w2
def f1():
    """业务函数"""
    print("---3---")
    return "aaa---bbb---ccc"

@w1在最上面,@w1下面需要一个函数,但是@w1下面是@w2,需要等@w2装饰完成再装饰。

3.4 装饰器传参

传递两个参数案例

def w1(fn):
    """装饰器函数"""
    print("---正在装饰---")

    def inner(a, b):
        print("正在验证")
        fn(a, b)

    return inner

@w1
def f1(a, b):
    """业务函数"""
    print(a+b)

不定参案例 

def w1(fn):
    """装饰器函数"""
    print("---正在装饰---")

    def inner(*args, **kwargs):
        print("正在验证")
        fn(*args, **kwargs)

    return inner

@w1
def f1(a, b):
    """业务函数"""
    print(a+b)

@w1
def f2(a, b, c, d):
    """业务函数"""
    print(a+b+c+d)


f1(10, 20)
f2(10, 20, 30, 40)

3.5 装饰器返回值 

def w1(fn):
    """装饰器函数"""
    print("---正在装饰---")

    def inner():
        print("---正在验证---")
        ret = fn()  # 保存返回来的字符串
        return ret  # 把字符串返回到调用的地方

    return inner

@w1
def test():
    """业务函数"""
    print("---test---")
    return "这是原函数返回值"


ret = test()  # 参数接收返回值
print(ret)

3.6 通用装饰器

def w1(fn):
    """装饰器函数"""

    def inner(*args, **kwargs):
        print("---记录日志---")
        ret = fn(*args, **kwargs)
        return ret

    return inner


@w1
def test1():
    """不带返回值"""
    print("---test1---")


@w1
def test2():
    """带返回值"""
    print("---test2---")
    return "这是原函数的值"

@w1
def test3(a):
    """业务函数"""
    print("---test3中的数据:%d" % a)


ret1 = test1()
print(ret1)
ret2 = test2()
print(ret2)
ret3 = test3(10)
print(ret3)

3.7 装饰器带参数

def func_arg(arg):
    def func(funtionName):
        def func_in():
            print("输出给装饰器传入的参数:%s" % arg)
            if arg == "hello":
                funtionName()
                funtionName()
            else:
                funtionName()

        return func_in

    return func


@func_arg("hello")
def test():
    print("---test---")

@func_arg("aaa")
def test1():
    print("---test1---")


test()
test1()

步骤:

先执行func_arg()函数,函数返回结果是func这个函数的引用

@func

使用@func对test进行装饰

猜你喜欢

转载自blog.csdn.net/xiao__dashen/article/details/125243827