【python】迭代器、生成器、装饰器

目录

 一, 迭代器

1. 迭代器简介

2. 判断迭代对象

3. 创建迭代器

二, 生成器

1. 生成器简介

2. 创建生成器

3. 生成器使用案例

三, 装饰器

1. 闭包

2. 装饰器

3. 装饰器执行的时间

4. 装饰器传参

5. 装饰器返回值

6. 通用装饰器

7. 装饰器带参数


 一, 迭代器

1. 迭代器简介

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

常见的可迭代对象有:

  • 几何数据类型,如list、tuple、dict、set、str等;
  • 生成器(generator),包括生成器和带yield的生成器函数;

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

for i in 'python':
    print(i,sep=' ',end=' ')  # 输出 p y t h o n

2. 判断迭代对象

可迭代对象并不是指某种具体的数据类型,是指存储了元素的一个容器对象,且容器中的元素可以通过__iter__()方法或__getitem()方法访问。

  1. __iter__方法的作用是让可迭代对象可以使用for...in...循环遍历;__getitem__()方法是让对象可以通过“实例名[index]”的方式访问实例中的元素。
  2. 一个可迭代对象不能独立进行迭代。python中通过for...in来完成迭代,凡是可迭代对象都可以直接用for...in...循环访问,这个语句内部执行:__iter__(0获得一个可迭代器,循环调用__next__()。
  3. 常见的可迭代对象包括:
    1. 集合数据类型,如list、tuple、dict、set、str等;
    2. 生成器,包括生成器和带yield的生成器函数;
  4. 可以通过collections模块的Iterable类型判断

判断一个对象是否是可迭代对象的方法:

from collections import Iterable
isinstance(变量,Iterable)

示例:

from collections import Iterable

print(isinstance([1,2,3],Iterable))  # 输出 True

3. 创建迭代器

迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问呢,直到所有的元素被访问完结束。迭代器只能往前不会后退。

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

  • __iter__()方法返回对象本身,即self。
  • __next__()方法返回下一个数据,如果没有数据了,就需要抛出一个StopIteration异常。

示例,创建一个返回数字的迭代器,初始值为1,逐步递增1,在5此迭代后停止执行。

class MyNumber(object):
    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 = MyNumber()
myitera = iter(myclass)
for x in myitera:
    print(x)

输出:

1
2
3
4
5

二, 生成器

1. 生成器简介

通过列表推导式可以直接创建一个列表,但是会受到内存的限制,列表容量是有限的。并且,创建一个包含100万个元素的列表,占用很大的存储空间,如果只需要访问前面几个元素,后面的绝大部分元素占用的空间就浪费了。如果列表元素可以按照某种算法推算出来,那么可以在循环的过程中不断推算出后续的元素,就不必创建完整的list,从而节省大量的空间。

python提供了这种一边循环一边计算的机制,称为生成器generator。生成器的这个特性,解决了无限个变量和有限内存之间的矛盾问题,提供了解决方案,或者为优化内存使用效率提供了途径。

2. 创建生成器

■ 方式1:把列表生成式的[]改成(),使用next()函数获取生成器的返回值

L = [x*2 for x in range(4)]
print(L)  # 输出 [0, 2, 4, 6]

print('*'*50)

# 生成器
G = (x*2 for x in range(4))
print(G)  # 输出 <generator object <genexpr> at 0x000002570BBEB4F8>
# 遍历生成器G
print(next(G))  # 输出 0
print(next(G))  # 输出 2
print(next(G))  # 输出 4
print(next(G))  # 输出 6

■ 方式2:使用函数创建生成器,如果函数中有yield,那么该函数就变成生成器

如斐波拉契数列,如果使用列表生成式很难实现,使用函数就可以很方便的打印出来。

def createNum():
    a,b = 0,1
    for i in range(5):
        print(b)
        a,b = b,a+b

createNum()

输出显示

1
1
2
3
5

在createNum()函数中加入yield就变成了生成器,如下代码中,a是一个生成器对象,

  • 当第一次调用next(a)的时候,生成器从上往下执行,执行到yield b的时候停止并返回b的值;
  • 再次调用next(a)时,程序从原来停止的地方接着往下执行,循环执行到yield b的时候停止,返回b的值;
def createNum():
    print('开始')
    a,b = 0,1
    for i in range(5):
        print('before')
        yield b
        print('after 1')
        a,b = b,a+b
        print('after 2')
        print('------')
    print('stop')


a = createNum()
print(a)
print(next(a))
print(next(a))
print(next(a))

输出显示:

<generator object createNum at 0x000002C6F970B570>
开始
before
1
after 1
after 2
------
before
1
after 1
after 2
------
before
2

■ 方式3:函数改为生成器后,一般使用for循环进行迭代获取返回值,不需要关心StopIteration异常。如果要获取生成器的返回值,可以捕获StopIteration异常,返回值包含在StopIteration异常的value中。

def createNum():
    print('开始')
    a,b = 0,1
    for i in range(5):
        '''print('before')'''
        yield b
        '''print('after 1')'''
        a,b = b,a+b
        '''print('after 2')
        print('------')'''
    print('stop')


a = createNum()
print(a)
'''print(next(a))
print(next(a))
print(next(a))'''

print('*'*50)
for x in a:
    print(x)

显示输出:

开始
1
1
2
3
5
stop

3. 生成器使用案例

■ 处理大量数据

import time

start = time.time()
print(sum([i for i in range(100000000)]))
end = time.time()
print('使用列表推导式耗时:',end-start)

start = time.time()
print(sum((i for i in range(100000000))))
end = time.time()
print('使用生成器耗时:',end-start)

显示结果,使用生成器的时间要少于使用列表推导式;并且在测试过程中,当使用列表推导式时,内存会飙升,而使用生成器时,内存几乎没有变化。

4999999950000000
使用列表推导式耗时: 8.564022779464722
4999999950000000
使用生成器耗时: 6.0585761070251465

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

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 

三, 装饰器

1. 闭包

闭包是能够读取其它函数内部变量的函数。闭包可以理解成定义在一个函数内部的函数。

本质上来说,闭包是将函数内部和函数外部连接起来的桥梁。

示例,

def test(n1):

    print('before')
    def test_in(n2):
        print('test_in()...')
        print(n1 + n2)

    print('after')
    return test_in

ret = test(10)
print(ret)
ret(1)
ret(20)
ret(30)

输出结果,在test()函数中返回的是test()函数内部的test_in()函数,那么可以把ret看作是一个函数,后面ret(1),ret(20),ret(30)就类似于调用函数test_in(),其中test_in()中的n1就等于创建ret时传进去的参数10

before
after
<function test.<locals>.test_in at 0x00000261FC623400>
test_in()...
11
test_in()...
30
test_in()...
40

2. 装饰器

装饰器,不改变原有程序的功能,不改变函数调用的接口的基础上,增添新的功能。

装饰器可以基于函数实现也可以基于类实现,使用方式基本是固定的,基本步骤是:

  • 定义装饰函数(类);
  • 定义业务函数;
  • 在业务函数上添加@装饰函数/类名;

示例:

def wl(func):
    '''装饰器函数'''
    print('装饰器函数,',func)
    def inner():
        print('before')
        func()
        print('新增加的功能')

    return inner

@wl  # @wl表示调用函数的时候在上方加上 f1 = wl(f1)
def f1():
    '''业务函数1'''
    print('f1()')

@wl
def f2():
    '''业务函数2'''
    print('f2')

f1()
f2()

显示结果:

装饰器函数, <function f1 at 0x00000185E8E03400>
装饰器函数, <function f2 at 0x00000185E8E03510>
before
f1()
新增加的功能
before
f2
新增加的功能

python也支持多个装饰器同时使用


def w1(fn):
    """装饰器函数"""
    print(fn)
    def inner():
        print("---1---")
        return "<b>" + fn() + "</b>"

    return inner


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

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

    return inner


def w3(fn):
    """装饰器函数"""
    print(fn)

    def inner():
        print("---3---")
        return "<i>" + fn() + "</i>"

    return inner

def w4(fn):
    """装饰器函数"""
    print(fn)

    def inner():
        print("---4---")
        return "<i>" + fn() + "</i>"

    return inner

def w5(fn):
    """装饰器函数"""
    print(fn)

    def inner():
        print("---5---")
        return "<i>" + fn() + "</i>"

    return inner

@w1
@w2
@w3
@w4
@w5
def f1():
    """业务函数"""
    print("---0---")
    return "hello python"


ret = f1()
print(ret)

显示结果:

<function f1 at 0x000001D89EA33620>
<function w5.<locals>.inner at 0x000001D89EA336A8>
<function w4.<locals>.inner at 0x000001D89EA33730>
<function w3.<locals>.inner at 0x000001D89EA337B8>
<function w2.<locals>.inner at 0x000001D89EA33840>
---1---
---2---
---3---
---4---
---5---
---0---
<b><i><i><i><i>hello python</i></i></i></i></b>

3. 装饰器执行的时间

python解释器执行到@w1代码时,就会开始自动进行装饰,不是等到调用的时候才装饰。

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

    return inner



@w1 # 只要Python解释器执行到这个代码,就开始自动进行装饰,而不是等到调用的时候才装饰
def f1():
    """业务函数"""
    print("---2---")
    return "hello python"

输出结果,没有调用f1()函数,输出结果打印了 正在装饰

---正在装饰---

多个装饰器执行时间

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

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

    return inner


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

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

    return inner


@w1
@w2
def f1():
    """业务函数"""
    print("---3---")
    return "hello python"
---正在装饰2---
---正在装饰1---

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

4. 装饰器传参

传参示例1:


def w1(fn):
    print('正在装饰')

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

    return inner

@w1
def f1(a,b):
    print(a+b)

f1(10,10)

输出结果:

正在装饰
正在验证
20

不定长参数传参示例:

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):
    print(a+b+c)


f1(10,10)
f2(20,20,20)

输出结果:

正在装饰
正在装饰
正在验证
20
正在验证
60

5. 装饰器返回值

# -*- coding: utf-8 -*-
"""
@File   :   装饰器返回值.py
@Author :   小地瓜重新去华容道工作
@E-Mail :   zoya.zh@qq.com
@Time   :   22/9/18 
"""

def w1(fn):
    print('正在装饰')

    def inner():
        print('正在验证')
        ret = fn()
        return ret

    return inner

@w1
def f1():
    print('test')
    return '原函数返回值'

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

显示结果:

正在装饰
正在验证
test
原函数返回值
正在验证
test
原函数返回值

6. 通用装饰器

# -*- coding: utf-8 -*-
"""
@File   :   通用装饰器.py
@Author :   小地瓜重新去华容道工作
@E-Mail :   zoya.zh@qq.com
@Time   :   22/9/18 
"""

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 'test2原函数返回值'

@w1
def test3(a):
    # 需要参数
    print('test3中数据:',a)


@w1
def test4(a):
    # 需要参数和返回值
    print('test4中数据:',a)
    return 'test4原函数返回值'


ret1 = test1()
print('ret1 = ',ret1)
ret2 = test2()
print('ret2 = ',ret2)
ret3 = test3(4)
print('ret3 = ',ret3)
ret4 = test4(6)
print('ret4 = ',ret4)

显示结果:

记录日志
test1
ret1 =  None
记录日志
test2
ret2 =  test2原函数返回值
记录日志
test3中数据: 4
ret3 =  None
记录日志
test4中数据: 6
ret4 =  test4原函数返回值

Process finished with exit code 0

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("haha")
def test2():
    print("---test2---")


test()
test2()

输出结果并显示:

输出给装饰器传入的参数:hello
---test---
---test---
输出给装饰器传入的参数:haha
---test2---

执行步骤:

  1. 限制性func_arg('hello')函数,这个函数return的结果是func函数的引用;
  2. @func
  3. 使用@func对test进行装饰

装饰器传参的作用:传递的参数不同,可以用于判断做不同的事情

参考:

100天精通Python(基础篇)——第27天:迭代器、生成器、装饰器_无 羡ღ的博客-CSDN博客

Python中可迭代对象是什么? - 老猿Python - 博客园 (cnblogs.com)

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python中的迭代器生成器装饰器都是非常重要的编程概念,下面分别介绍一下: 1. 迭代器 Python中的迭代器是一种可以遍历集合中元素的对象,可以使用for循环遍历集合中的元素。迭代器实现了两个方法:__iter__()和__next__()。__iter__()方法返回迭代器对象自身,__next__()方法返回集合中的下一个元素。 下面是一个简单的使用迭代器遍历列表的示例: ``` my_list = [1, 2, 3] my_iterator = iter(my_list) for i in my_iterator: print(i) ``` 2. 生成器 生成器是一种特殊的函数,可以在执行过程中多次返回值,而不是只返回一次。生成器使用yield语句返回值,可以暂停函数的执行,并在需要时继续执行。 下面是一个简单的生成器示例: ``` def my_generator(): yield 1 yield 2 yield 3 for i in my_generator(): print(i) ``` 3. 装饰器 装饰器是一种可以修改函数或类的行为的函数,可以在不修改原始代码的情况下添加额外的功能。装饰器本质上是一个可以接受函数或类作为参数的函数,可以在不修改原始函数或类的情况下修改其行为。 下面是一个简单的装饰器示例: ``` def my_decorator(func): def wrapper(): print("Before the function is called.") func() print("After the function is called.") return wrapper @my_decorator def my_function(): print("Inside the function.") my_function() ``` 在上面的代码中,我们定义了一个装饰器函数my_decorator,它接受一个函数作为参数,并返回一个新的函数wrapper。这个新函数在调用原始函数之前和之后打印一些文本。我们使用@符号将装饰器应用到my_function函数上,这样my_function函数的行为就被修改了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值