【Python】三器一闭(迭代器、生成器、闭包、装饰器)

关于迭代器:

什么是迭代?

迭代是访问集合元素的一种方式。

对list、tuple、str等类型的数据,能够使用for…in…的循环语法从其中依次拿到数据进行使用,这样的过程则称为遍历,也叫迭代。

什么是可迭代对象?

只要是可以通过for…in…的形式进行遍历的,那么这个数据类型就是可以迭代的。

如何判断数据是否可迭代?

内置函数 isinstance() ,可用来判断一个对象是否为指定数据类型。

若是则返回True,反之则返回False。

但是在python中是没有这个可迭代对象这个数据类型的,不过可以通过导入模块中的类来实现。

from collections.abc import Iterable, Iterator  # 从集合.abc 导入可迭代

print(isinstance([], Iterable))      # 列表,True
print(isinstance({}, Iterable))      # 字典,True
print(isinstance((1,), Iterable))    # 元组,True
print(isinstance(set(), Iterable))   # 集合,True
print(isinstance('asaf', Iterable))  # 字符串,True
print(isinstance(123, Iterable))     # False,整型:非可迭代对象
print(isinstance(range(1, 10), Iterable))  # range类型,True

什么是迭代器:

迭代是python中访问集合元素的一种非常强大的一种方式。

迭代器是一个可以记住遍历位置的对象,迭代器对象从第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源。

迭代器有两个方法:iter() 和 next()方法。

迭代器的本质:

我们可以通过 iter() 函数获取这些可迭代对象的迭代器。

然后我们可以对获取到的迭代器不断使用 next() 函数来获取下一条数据。

使用迭代器取数据:
"""使用迭代器取数据"""
nums = [1, 2, 3]   # 可迭代对象
nums = iter(nums)  # 迭代器

print('nums是迭代器么?答案是:', isinstance(nums, Iterator))  # True

# 取出迭代器的数据
num1 = next(nums)
print(num1)

num2 = next(nums)
print(num2)

"""注意: 若迭代的次数超过了可迭代对象的长度, 就会报 StopIteration 异常"""

自定义迭代器:

使用 iternext 方法自定义迭代器。

只要在类中,定义 iter 方法,那么这个类创建出来的对象一定是可迭代对象。如果类中实现了 iter 方法和 next 方法的对象,就是迭代器。

当我们调用 iter() 函数提取一个可迭代对象的迭代器时,实际上会自动调用这个对象的iter方法,并且这个方法返回迭代器。

代码实例:
from collections.abc import Iterator, Iterable

# 类中实现了 iter() 和 next() 方法
class myNum:
    def __iter__(self):  # 返回的是迭代器对象

        # 实例属性
        self.a = 1
        print(self, 'self')  # <__main__.myNum object at 0x100390c50> self
        return self  # 表示实例对象本身是自己的迭代器对象

    def __next__(self):
        # 举例:自增1,为了体现 next() 方法
        self.a += 1
        return self.a


mydata = myNum()  # 创建对象
print(isinstance(mydata, Iterable))   # True

myIter = iter(mydata)  # 把对象生成迭代器
print(myIter, 'myIter')   # <__main__.myNum object at 0x100390c50> myIter

print(isinstance(mydata, Iterator))  # 实现上面2个方法时,才是迭代器:True,这句是自行判断,可以不要

"""
print(next(myIter))
print(next(myIter))  
使用 for 循环代替
"""

for i in range(1, 20):
    print(next(myIter))

小结:
  • 凡是可作用于 for 循环的对象都是 Iterable 类型;
  • 凡是可作用于 next() 函数的对象都是 Iterator 类型;
  • 集合数据类型如 list 、dict、str 等是 Iterable 但不是Iterator,不过可以通过 iter() 函数获得一个 Iterator 对象。

生成器:

在 python 中一边循环一边计算的这种机制,叫做生成器。也叫生成数据的机器代码

以 list 容器为例,在使用该容器迭代一组数据时,必须事先将所有数据存储到容器中,才能开始迭代;而生成器却不同,它可以实现在迭代的同时生成元素。

即:对于可以用某种算法推算得到的多个数据,生成器并不会一次性生成它们,而是什么时候需要,才什么时候生成。

生成器是一种用时间换空间的做法。比如,利用 list 列表储存全体正整数,无穷个正整数再大的内存也无法装得下,这个时候就可以使用生成器,实现用一段代码来储存全部正整数的作用。

生成器的创建:

  1. 定义一个以 yield 关键字标识返回值的函数;
  2. 调用刚刚创建的函数,即可创建一个生成器。
代码实例:
# 生成器的创建
def intNum():
    print('开始执行程序:...')
    for i in range(5):
        yield i     # return 回到函数调用处,后面的不会执行,yield 可理解为暂停,同时回到函数调用处,但是此时进到for循环
        print('go on')

# intNum() 函数的返回值用的是 yield 关键字,而不是 return 关键字,此类函数又成为生成器函数。
num = intNum()   # 生成器函数
print(num)       # <generator object intNum at 0x104824110>,生成器函数

# 如果需要继续执行,需要调用 next() 方法,或者用for循环代替
# print(num.__next__())    # 执行一次,就结束
# print(num.__next__())
# print(num.__next__())
# print(num.__next__())
# print(num.__next__())    # 如果执行时超出 范围,会报错

for i in num:
    print(f'for循环:{i}')

"""
和 return 相比,yield 除了可以返回相应的值,还有一个更重要的功能,即每当程序执行完该语句时,程序就会暂停执行。
不仅如此,即便调用生成器函数,Python 解释器也不会执行函数中的代码,它只会返回一个生成器(对象)。
"""

"""要想使生成器函数得以执行,或者想使执行完 yield 语句立即暂停的程序得以继续执行,有以下 2 种方式:
1. 通过生成器(上面程序中的 num)调用 next() 内置函数或者 __next__() 方法;
2. 通过 for 循环遍历生成器。"""

闭包:

什么是闭包?

在函数中再嵌套一个函数,并且引用外部函数的变量。

构成条件:

1、函数嵌套 2、外部函数返回内部函数名 3、内部函数使用外部函数的变量

代码实例:
def outer(x):
    # 嵌套函数inner(),则是一个闭包函数
    def inner(y):
        return x + y   # 引用外部函数的变量

    return inner

print(outer(6)(5))

def myfunc(a, b=1):
    c = 100

    def useC():
        print(f'调用外部函数的变量,并打印:{c}')   # 100
        print(a + b)    # 17

    useC()


myfunc(6, 11)   # 实参值会覆盖形参的值

装饰器:

什么是装饰器:

通过装饰器函数,来修改原函数的一些功能,又不会改变原函数的内部实现,又使得原函数不需要修改。装饰器本身可以增强其他函数的功能。

优点:

避免大量的重复代码;

作用:

1、将这个修饰符下面的函数作为该修饰符函数的参数传入。

2、如果被 @ 的函数是一个闭包函数,那么就把被修饰函数的参数传给闭包的内函数,结合位置参数列表 args 和可变参数字典 kwargs,就可以完成所有参数的接收。

小结:

装饰器的作用就是为了解耦一些通用处理或者不必要功能的,
尽可能让一个函数只负责一个任务,避免后续维护时散弹式修改代码。

代码实例:
import time

# 需求:打印2到20000的所有质数所需时间

# 装饰器
def runTime(func):    # 装饰器,传入一个参数,把我们要运行的函数 printNum() 作为参数传入
    def wrapper():    # 表示的是,要运行这个函数,需要运行哪些内容
        time1 = time.time()  # 运行这个函数前,截取一个时间
        func()  # 然后运行我们的函数
        time2 = time.time()  # 运行结束,再获取一个当前时间
        print(time2 - time1)  # 最后,打印总耗时

    return wrapper

# 判断是否为质数
def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True

@runTime
def printNum():   # 打印质数
    for i in range(2, 20000):
        if isPrime(i):
            print(i)


printNum()

对上述案例进行优化,计算一共有多少个质数?

import time

def runTime(func):  # 装饰器,传入一个参数,把我们要运行的函数 printNum() 作为参数传入
    def wrapper():  # 表示的是,要运行这个函数,需要运行哪些内容
        time1 = time.time()  # 运行这个函数前,截取一个时间
        result = func()  # 然后运行我们的函数,把记录个数的 countNum 记录并保存起来
        time2 = time.time()  # 运行结束,再获取一个当前时间
        print("Toal time:{:.4} s".format(time2 - time1))  # 然后,打印总耗时,保留4位小数
        return result  # 最后,返回记录个数的 countNum

    return wrapper

def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True

@runTime
def countPrintNum():
    countNum = 0  # 如果加上计数器,比如,统计2-20000之间有多少个质数
    for i in range(2, 10000):
        if isPrime(i):
            countNum += 1
    return countNum


countNum = countPrintNum()
print(countNum)

针对上述案例,继续优化,随意截取任意之间两个人的质数个数:

import time

def runTime(func):  # 装饰器,传入一个参数,把我们要运行的函数 printNum() 作为参数传入
    def wrapper(*args):  # 表示的是,要运行这个函数,需要运行哪些内容,*args是接收函数countPrintNum中的不定长参数
        time1 = time.time()  # 运行这个函数前,截取一个时间
        result = func(*args)  # 然后运行我们的函数(fun()等价于countPrintNum()),把记录个数的 countNum 记录并保存起来
        time2 = time.time()  # 运行结束,再获取一个当前时间
        print("Total time: {:.4} s".format(time2 - time1))  # 然后,打印总耗时,保留4位小数
        return result  # 最后,返回记录个数的 countNum

    return wrapper

def isPrime(num):
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True

@runTime
def countPrintNum(maxNum):  # 如果想要统计到任意数之间的质数,附带参数时,我们需要把这个参数 maxNum 带到装饰器中
    countNum = 0
    for i in range(2, maxNum):
        if isPrime(i):
            countNum += 1
            # print(i)
    return countNum


countNum = countPrintNum(100)    # 这里可以传入大于2的任意数
print(countNum)

解析:

代码中的 @runTime,相当于

countPrintNum = runTime(countPrintNum)
countPrintNum()

当运行最后的 countPrintNum() 函数时,调用过程是这样的:

  • 先执行 countPrintNum = runTime(countPrintNum),此时的变量 countPrintNum 指向的是 runTime()
  • runTime(func)中传参的是 countPrintNum,返回的 wrapper,而 wrapper 又会调用到原函数 countPrintNum

所以,先执行 wrapper 函数里面的函数,然后再执行 countPrintNum()函数里的。

---------------------------END---------------------------

题外话

当下这个大数据时代不掌握一门编程语言怎么跟的上脚本呢?当下最火的编程语言Python前景一片光明!如果你也想跟上时代提升自己那么请看一下.

在这里插入图片描述

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。


👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

若有侵权,请联系删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,"三器一闭"指的是迭代器生成器装饰器闭包这四个概念。它们都是Python中非常重要且强大的编程概念。 1. 迭代器(Iterator):迭代器是一个对象,它实现了迭代器协议,即包含`__iter__()`和`__next__()`方法。通过调用`__next__()`方法,迭代器可以逐个返回元素,直到没有元素可返回时抛出`StopIteration`异常。迭代器可以用于遍历可迭代对象,如列表、元组、字典等。 2. 生成器(Generator):生成器是一种特殊的迭代器,它使用函数来创建。生成器函数使用`yield`关键字来暂停函数的执行,并返回一个值。每次调用生成器的`__next__()`方法时,函数会从上次暂停的位置继续执行,直到再次遇到`yield`关键字。生成器可以节省内存,并且可以按需生成数据。 3. 装饰器(Decorator):装饰器是一种用于修改函数或类的行为的函数。它可以在不修改原始函数或类的情况下,通过将其传递给装饰器函数来添加额外的功能。装饰器使用`@`语法来应用于函数或类定义之前。 4. 闭包(Closure):闭包是指一个函数对象,它可以访问并修改其词法作用域之外的变量。闭包在函数内部定义了一个内部函数,并返回该内部函数。内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。闭包可以用于实现函数工厂、实现私有变量和缓存结果等功能。 以下是一个简单的示例,演示了迭代器生成器装饰器闭包的使用: ```python # 迭代器 class MyIterator: def __init__(self, data): self.data = data self.index = 0 def __iter__(self): return self def __next__(self): if self.index >= len(self.data): raise StopIteration value = self.data[self.index] self.index += 1 return value # 生成器 def my_generator(): yield 1 yield 2 yield 3 # 装饰器 def my_decorator(func): def wrapper(): print("Before function execution") func() print("After function execution") return wrapper @my_decorator def my_function(): print("Inside function") # 闭包 def outer_function(x): def inner_function(y): return x + y return inner_function # 使用迭代器 my_list = [1, 2, 3] my_iterator = MyIterator(my_list) for item in my_iterator: print(item) # 使用生成器 my_generator = my_generator() for item in my_generator: print(item) # 使用装饰器 my_function() # 使用闭包 add_5 = outer_function(5) print(add_5(3)) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值