Python迭代器、生成器和装饰器

news2024/11/15 12:58:30

一、迭代器

1、迭代器简介

迭代操作是访问集合元素的一种方式,是 Python最强大的功能之一。

迭代器是用来迭代取值的工具,是一个可以记住遍历的位置的对象。

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

1.1 可迭代对象(Iterable)

通过索引的方式进行迭代取值,实现简单,但仅适用于序列类型:字符串,列表,元组。

对于没有索引的字典、集合等非序列类型,必须找到一种不依赖索引来进行迭代取值的方式。

迭代器提供了一种通用的且不依赖于索引的迭代取值方式的功能。

字符串、列表、元组、字典、集合、打开的文件都是可迭代对象。

1.2 迭代器对象(Iterator)

迭代器对象是内置有 iter和 next方法:

  • iter():返回迭代器对象,即得到的仍然是迭代器本身。
  • next():返回迭代器中的下一个元素值。

2、创建迭代器

2.1 创建迭代器

list = [1, 2, 3, 4]
# 创建迭代器对象
# it = iter(list)
it = list.__iter__()

print(type(it)) # <class 'list_iterator'>

# 输出迭代器的下一个元素
print(next(it))  # 1
print(next(it))  # 2
print(it.__next__())  # 3

了解了迭代器,就可以不依赖索引迭代取值了。

使用while循环的实现遍历:

import sys  # 引入 sys 模块

list = [1, 2, 3, 4]
it = iter(list)  # 创建迭代器对象

while True:
    try:
        print(next(it))
    except StopIteration:
        # sys.exit()
        break

上述 while循环可以使用 for语句遍历简写:in后可以跟任意可迭代对象

list = [1, 2, 3, 4]
it = iter(list)  # 创建迭代器对象

for x in it:
    print(x, end=" ")

2.2 创建类迭代器

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

  • __iter__() 方法:返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()方法并通过 StopIteration 异常标识迭代的完成。
  • __next__() 方法:返回下一个迭代器对象。
    Python 的构造函数为 __init__(),它会在对象初始化的时候执行。

**示例代码如下:**创建一个返回数字的迭代器,初始值为 1,逐步递增 1。

class MyClass:
    def __iter__(self):
        self.a = 1 # 初始化一个变量
        return self

    def __next__(self):
        x = self.a
        self.a += 1  # 每次迭代加1
        return x


obj = MyClass()
myIter = iter(obj)

print(next(myIter))
print(next(myIter))
print(next(myIter))
print(next(myIter))

**StopIteration:**

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况。

__next__()方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

示例代码如下:在第 4次迭代后停止执行:

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

    def __next__(self):
        if self.a <= 4:
            x = self.a
            self.a += 1
            return x
        else:
            raise StopIteration # 结束迭代


obj = MyClass2()
myIter = iter(obj)

for x in myIter:
    print(x)

在这里插入图片描述

二、生成器

1、生成器简介

在 Python 中,使用了 yield 关键字的函数被称为生成器(generator)。

生成器是一个返回迭代器的函数,只能用于迭代操作,可理解为生成器就是一个自定义迭代器。

yield 能够临时挂起当前函数,记下其上下文(包括局部变量、待决的 try catch 等),将控制权返回给函数调用者。当下一次再调用其所在生成器时,会恢复保存的上下文,继续执行剩下的语句,直到再遇到 yield 或者退出为止。

生成器有一个基本的方法:

  • next() :返回迭代器中的下一个元素值。

生成器调用过程:

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值,并在下一次执行 next() 方法时从当前位置继续运行。

1.2 yield 和 return 区别

  • 函数一旦遇到 return就结束了,销毁上下文(弹出栈帧),将控制权返回给调用者。
  • yield只能在函数内使用
  • yield可以保存函数的运行状态,挂起函数,用来返回多次值。值的类型没有限制。

2、创建生成器

示例代码如下:使用 yield 实现斐波那契数列。

# 创建生成器函数实现 - 斐波那契
def fibonacci(n):
    # n 代表数列的个数
    a, b, counter = 0, 1, 0
    while counter <= n:
        yield a # 返回
        a, b = b, a + b
        counter += 1 # 个数加1

# f 是一个迭代器,由生成器返回生成
f = fibonacci(10)

print()
print(type(f)) # <class 'generator'>
print('=====for遍历====')
for value in f:
    print(value, end=" ")

print()
f = fibonacci(10)
print('=====while遍历====')
while True:
    try:
        # 获取迭代器中的下一个元素值
        value = next(f)
        print(value, end=" ")
    except StopIteration:
        break

在这里插入图片描述

三、装饰器

1、装饰器简介

装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。即装饰器,就是可以让我们拓展一些原有函数没有的功能。

装饰器的使用符合了面向对象编程的开放封闭原则

  • 对扩展开放:意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
  • 对修改封闭:意味着类一旦设计完成,就可以独立工作,而不要对类进行任何修改。

2、创建无参装饰器

2.1 原函数是无参数

假设原函数(被装饰的函数)是无参数的,对其创建一个不带参数的无参装饰器函数。

示例代码如下:

import time

def say1():
    print("我是say1, Hello Python")
    time.sleep(2)

def say2():
    print("我是say2, Hello Python")
    time.sleep(2)

# 计时装饰器
def count_time(func):

    # 装饰函数名:可以自定义
    def wrapper():
        t1 = time.time()
        func()
        print("执行时间为:", time.time() - t1)

    return wrapper


if __name__ == '__main__':
    say1 = count_time(say1)  # 因为装饰器 count_time(say1) 返回的是装饰函数对象 wrapper,
    say1()  # 执行 say1()就相当于执行wrapper()

    print("---------")
    say2 = count_time(say2)
    say2()

注意: 这里的wrapper装饰函数名是可以自定义的,只要你定义的函数名,跟return的函数名是相同即可。

在这里插入图片描述

2.2 原函数有参数

假设原函数(被装饰的函数)是有参数的,对其创建一个带参数的有参装饰器函数。

示例代码如下:

import time


def say1(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)


def say2(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)


# 计时装饰器
def count_time(func):
    # 装饰的函数名:可以自定义
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)

    return wrapper


if __name__ == '__main__':
    say1 = count_time(say1)  # 因为装饰器 count_time(say1) 返回的是装饰函数对象 wrapper,
    say1("say111")  # 执行 say1()就相当于执行wrapper()

    print("---------")
    say2 = count_time(say2)
    say2("say222")

注意:上面装饰器函数wrapper的参数为 *args和 **kwargs,表示可以接受任意参数。

在这里插入图片描述

2.2 装饰器的语法糖@

在 Python项目中,难免会看到@符号的代码,这个 @符号就是装饰器的语法糖。

如果装饰器的语法糖@来实现定义装饰器,我们就可以直接调用这个原函数名。其实底层实现也是创建装饰器函数。本质上没有变化。方便了我们的使用。

(1)原函数是无参数,使用语法糖创建装饰器

import time

# 计时装饰器
def count_time(func):

    # 装饰的函数名:可以自定义
    def wrapper():
        t1 = time.time()
        func()
        print("执行时间为:", time.time() - t1)

    return wrapper

# count_time必须定义在前面
@count_time
def say1():
    print("我是say1, Hello Python")
    time.sleep(2)

@count_time
def say2():
    print("我是say2, Hello Python")
    time.sleep(2)

if __name__ == '__main__':
    say1()  # 使用用语法糖之后,就可以直接调用该函数来执行扩展的装饰功能。
    
    print("---------")
    say2()

(2)原函数是有参数,使用语法糖创建装饰器

import time

# 计时装饰器
def count_time(func):
    # 装饰的函数名:可以自定义
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)

    return wrapper

# count_time必须定义在前面
@count_time
def say1(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)

@count_time
def say2(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)


if __name__ == '__main__':
    say1("say111")  # 使用用语法糖之后,就可以直接调用该函数来执行扩展的装饰功能。

    print("---------")
    say2("say222")

2、创建有参装饰器

装饰器也是函数,既然是函数,那么就可以进行参数传递。

示例代码如下:假设我们在使用装饰器的时候,传入一些备注的msg信息。

import time

# 计时装饰器
# 参数自行定义,这里使用的是默认值参数。
def count_time_args(msg=None):
    def count_time(func):
        def wrapper(*args, **kwargs):
            t1 = time.time()
            func(*args, **kwargs)
            print(f"[{msg}]执行时间为:", time.time() - t1)

        return wrapper

    return count_time


@count_time_args("say1")
def say1(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)


@count_time_args("say2")
def say2(name):
    print("我是%s, Hello Python" % name)
    time.sleep(2)


if __name__ == '__main__':
    say1("say111")

    print("---------")
    say2("say222")

基于原来的 count_time函数外部再包一层用于接收参数的 count_time_args函数,接收回来的参数就可以直接在内部的函数里面调用了。

执行结果如下:

在这里插入图片描述

3、类装饰器

在 Python中,其实也可以用类来实现装饰器的功能,称之为类装饰器。

类装饰器的实现是调用了类里面的 __call__函数。

当我们将类作为一个装饰器,工作流程:

  • 通过__init__()方法初始化类
  • 通过__call__()方法调用真正的装饰方法

类装饰器的写法比我们写装饰器函数更加简单。

3.1 无参类装饰器

示例代码如下:

import time

# 定义无参数的类装饰器
class MyClassCountTimeDecorator:
    def __init__(self, func):
        self.func = func
        print("执行类的__init__方法")

    def __call__(self, *args, **kwargs):
        print('进入__call__函数')
        t1 = time.time()
        self.func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)


@MyClassCountTimeDecorator
def say1():
    print("我是say1, Hello Python")
    time.sleep(2)


def say2():
    print("我是say2, Hello Python")
    time.sleep(2)


if __name__ == '__main__':
    say1()
    print('--------------')
    say2()

在这里插入图片描述

3.2 有参类装饰器

当装饰器有参数的时候,__init__() 函数就不能传入func(func代表要装饰的函数)了,而 func是在__call__函数调用的时候传入的。

import time

# 定义带参数的类装饰器
class MyClassCountTimeDecorator:
    def __init__(self, arg1, arg2):  # init()方法里面的参数都是装饰器的参数
        print('执行类Decorator的__init__()方法')
        self.arg1 = arg1
        self.arg2 = arg2

    def __call__(self, func):  # 因为装饰器带了参数,所以接收传入函数变量的位置是这里
        print('执行类Decorator的__call__()方法')

        def wrapper(*args):  # 装饰器的函数名字可以自定义,只要跟return的函数名相同即可
            print('执行wrap()')
            print('装饰器参数:', self.arg1, self.arg2)
            print('执行' + func.__name__ + '()')
            func(*args)
            print(func.__name__ + '()执行完毕')

        return wrapper


@MyClassCountTimeDecorator('say', '1')
def say1(a1, a2, a3):
    print("我是say1, 传入的参数:", a1, a2, a3)
    time.sleep(2)


@MyClassCountTimeDecorator('say', '2')
def say2(a1, a2):
    print("我是say2, 传入的参数:", a1, a2)
    time.sleep(2)


if __name__ == '__main__':
    say1("鲁班", "后裔", "妲己")
    print('--------------')
    say2("赵云", "赵子龙")

在这里插入图片描述

注意:多理解无参与有参数的装饰器/类装饰器区别,加深理解代码。

四、装饰器的顺序

一个函数可以被多个装饰器进行装饰,那么装饰器的执行顺序是怎么调用的?

答案是:在装饰器修饰完的函数,在执行的时候先执行原函数的功能,然后再由里到外依次执行装饰器的内容。

示例代码如下:

def Decorator_1(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器1')

    return wrapper


def Decorator_2(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器2')

    return wrapper


def Decorator_3(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器3')

    return wrapper


@Decorator_1
@Decorator_2
@Decorator_3
def say1():
    print("我是say1, Hello Python")


if __name__ == '__main__':
    say1()

在这里插入图片描述

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT项目_RPC(进程间通讯)

QT项目_RPC(进程间通讯) 前言&#xff1a; 两个进程间通信、或是说两个应用程序之间通讯。实际情况是在QT开发的一个项目中&#xff0c;里面包含两个子程序&#xff0c;子程序有单独的界面和应用逻辑&#xff0c;这两个子程序跑起来之后需要一些数据的交互&#xff0c;例如&…

GEE学习笔记 八十四:【GEE之Python版教程十四】矢量数据(ee.feature)

上一节讲了几何图形Geometry&#xff0c;这一节讲矢量数据&#xff08;ee.feature&#xff09;&#xff0c;它的构成也就是几何图形以及属性字典。 1、API定义 首先看一下GEE的python版API&#xff08;Welcome to GEE-Python-API’s documentation! — GEE-Python-API 1.0 do…

【论文】智能隧道检测车的现状及改进策略

本文转载自《智慧城轨》2022年第11期 作者&#xff1a;黄丹樱1,韦强1,朱椰毅2,范骁1,林浩立1 单位&#xff1a;1 浙江师范大学工学院&#xff1b;2 浙江金温铁道开发有限公司 声明&#xff1a;本文仅用于学术分享&#xff0c;不做商业用途&#xff0c;如有侵权&#xff0c;联…

从实现到原理,聊聊Java中的SPI动态扩展

原创&#xff1a;微信公众号 码农参上&#xff0c;欢迎分享&#xff0c;转载请保留出处。 八股文背多了&#xff0c;相信大家都听说过一个词&#xff0c;SPI扩展。 有的面试官就很喜欢问这个问题&#xff0c;SpringBoot的自动装配是如何实现的&#xff1f; 基本上&#xff0c…

Redis第二讲

二、Redis02 2.1 发布和订阅 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff1a;发送者 (pub) 发送消息&#xff0c;订阅者 (sub) 接收消息。 Redis 客户端可以订阅任意数量的频道。 发布订阅的实现 1、打开一个客户端订阅channel1 127.0.0.1:6379> subscribe ch…

红黑树的原理+实现

文章目录红黑树定义性质红黑树的插入动态效果演示代码测试红黑树红黑树 定义 红黑树是一个近似平衡的搜索树&#xff0c;关于近似平衡主要体现在最长路径小于最短路径的两倍&#xff08;我认为这是红黑树核心原则&#xff09;&#xff0c;为了达到这个原则&#xff0c;红黑树所…

LeetCode刷题--- 面试题 01.07. 旋转矩阵(原地旋转+翻转替旋转)

&#x1f48c; 所属专栏&#xff1a;【LeetCode题解&#xff08;持续更新中&#xff09;】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;…

【C++之容器篇】二叉搜索树的理论与使用

目录前言一、二叉搜索树的概念二、二叉搜素树的模拟实现&#xff08;增删查非递归实现&#xff09;1. 二叉搜素树的结点2. 二叉搜索树的实现&#xff08;1&#xff09;. 二叉搜索树的基本结构&#xff08;2&#xff09;构造函数&#xff08;3&#xff09;查找函数&#xff08;4…

1225057-68-0,Alkyne PEG4 TAMRA-5,四甲基罗丹明-四聚乙二醇-炔基TAMRA红色荧光染料连接剂

中英文别名&#xff1a;CAS号&#xff1a;1225057-68-0 | 英文名&#xff1a;5-TAMRA-PEG4-Alkyne |中文名&#xff1a;5-四甲基罗丹明-四聚乙二醇-炔基物理参数&#xff1a;CASNumber&#xff1a;1225057-68-0Molecular formula&#xff1a;C36H41N3O8Molecular weight&#x…

P16 激活函数与Loss 的梯度

参考&#xff1a;https://www.ngui.cc/el/507608.html?actiononClick这里面简单回顾一下PyTorch 里面的两个常用的梯度自动计算的APIautoGrad 和 Backward, 最后结合 softmax 简单介绍一下一下应用场景。目录&#xff1a;1 autoGrad2 Backward3 softmax一 autoGrad输入 x输出损…

buu [UTCTF2020]basic-crypto 1

题目描述&#xff1a; 01010101 01101000 00101101 01101111 01101000 00101100 00100000 01101100 01101111 01101111 01101011 01110011 00100000 01101100 01101001 01101011 01100101 00100000 01110111 01100101 00100000 01101000 01100001 01110110 01100101 00100000 0…

【Kubernetes】【七】命令式对象配置和声明式对象配置

命令式对象配置 命令式对象配置就是使用命令配合配置文件一起来操作kubernetes资源。 1&#xff09; 创建一个nginxpod.yaml&#xff0c;内容如下&#xff1a; apiVersion: v1 kind: Namespace metadata:name: dev---apiVersion: v1 kind: Pod metadata:name: nginxpodnames…

调用Windows安全中心实现登录验证

文章目录运行效果用到的运行库代码实现使用日志Win10 Flat风格XP风格总结运行效果 输入用户名和密码点击确定后获取到的信息&#xff1a; 用到的运行库 NuGet搜索安装即可 Kang.ExtendMethodKang.ExtendMethod.Log https://gitee.com/haozekang/kang Vanara.PInvoke https:…

安全算法 - 国密算法

国密即国家密码局认定的国产密码算法。主要有SM1&#xff0c;SM2&#xff0c;SM3&#xff0c;SM4&#xff0c;SM7, SM9。国密算法分类国家标准官方网站如下&#xff1a;http://openstd.samr.gov.cn/bzgk/gb/SM1 为对称加密。其加密强度与AES相当。该算法不公开&#xff0c;调用…

Nacos——配置管理基础应用

目录 一、快速入门 1.1 发布配置 1.2 nacos client远程获取配置 1.2.1 导入坐标 1.2.2 程序代码 二、Nacos配置管理基础应用 2.1 Nacos配置管理模型 2.1.2 配置集(Data Id) 2.1.3 配置项 2.1.4 配置分组 (Group) 2.1.5 命名空间(Namespace) 2.1.6 最佳实践&#xff0…

Worok:专门针对亚洲实体的网络间谍组织

ESET 的研究人员发现了一个全新的攻击组织 Worok&#xff0c;该组织自动 2020 年就一直处于活跃状态。Worok 使用的工具集包括一个 C 编写的加载程序 CLRLoad、一个 PowerShell 编写的后门 PowHeartBeat 和一个 C# 编写的加载程序 PNGLoad&#xff0c;攻击者使用隐写术来提取隐…

PPT与Inkscape自定义色板

PPT与Inkscape自定义色板简述 本文主要分享了PPT与Inkscape中自定义色板功能&#xff0c;以满足个性化配色需求。此外&#xff0c;文末分享了常见的配色网站和图片网站&#xff0c;前者可以满足配色需求&#xff0c;后者可以满足配图需求。 PPT自定义色板 在常见的办公三件套中…

pycharm远程连接服务器,并单步调试服务器上的代码

每天都有不同的朋友来Push我 那如果比较健忘的话&#xff0c;为啥不问一下chatGPT呢 问题的缘由在我想在本地单步调试代码。。。 我的代码完全在云端服务器的&#xff0c;还有数据集都是&#xff0c;但实际上本地代码可以通过pycharm给他传上去。 但是在后面配置的时候需要两…

高密度部署,基于动态库的尝试,rust动态调库

目录前言faas特点方案思考实践制作动态库调用动态库尾语前言 最近在搞faas平台&#xff0c;也试了各大云厂商的产品&#xff0c;效果都不是很理想。和我心目中的faas想去甚远。  和小伙伴们吹完牛逼&#xff0c;心有所感&#xff0c;写下这篇文章&#xff0c;时间跨度较长&…

「AI人工智能」Node.js如何接入OpenAI开发

文章目录前言一、创建OpenAI账号二、安装axios 库三、导入 axios 库四、调用 OpenAI API五、测试 OpenAI API前言 本文主要介绍如何将 Node.js 应用程序与 OpenAI 集成&#xff0c;可以使用 OpenAI API。 一、创建OpenAI账号 创建一个 OpenAI 帐户并注册 API 密钥。你可以在 …