迭代、可迭代对象,迭代器、生成器、装饰器及yield与return的区别

参照了这位大神的博客然后总结了一下:https://blog.csdn.net/rainharder/article/details/73368770

何为迭代

迭代有两个特征:

1.重复进行某一特征
2.下一次操作依赖于上次操作的结果

见如下例子:

for i in range(5):
	print("Hello")   # 非迭代,只是重复打印 Hello
	print(i)         # 迭代,不止打印i的值,并且每次打印i都加1

何为可迭代对象

顾名思义,即可被迭代的对象

见如下例子:
在python中导入collections库中的Iterable类,在这个例子里,下面的isinstance函数是用来判断传入的数据类型对象是否为Iterable,即是否为可迭代对象。

from collections import Iterable

f = open('a.txt')
i = 1
s = "1234"
d = {'abc':1}
t = (1,2,345)

print(isinstance(f,Iterable)) #判断文件对象是否为可迭代对象
print(isinstance(i,Iterable)) #判断整型是否为可迭代对象
print(isinstance(s,Iterable)) #判断字符串对象是否为可迭代对象
print(isinstance(d,Iterable)) #判断字典对象是否为可迭代对象
print(isinstance(t,Iterable)) #判断元组对象是否为可迭代对象

>>>
True
False
True
True
True

可知,除了整型,文件类型、字符串类型、字典型、元组类型都是可迭代对象

那isinstance这个函数内部又是如何判断传入的对象是否为可迭代对象的呢?

因为,每个可迭代对象里都有一个__iter__()方法正是因为这个方法,才使得这个基本数据类型变为可迭代

现在,我们再用一个函数hasattr()来好好看看:

f = open('a.txt')
i = 1
s = "1234"
d = {'abc':1}
t = (1,2,345)

#hasattr(obj,string) 判断对象中是否有string方法
print(hasattr(f,'__iter__')) #判断文件对象是否为可迭代对象
print(hasattr(i,'__iter__')) #判断整型是否为可迭代对象
print(hasattr(s,'__iter__')) #判断字符串对象是否为可迭代对象
print(hasattr(d,'__iter__')) #判断字典对象是否为可迭代对象
print(hasattr(t,'__iter__')) #判断元组对象是否为可迭代对象

>>>True
False
True
True
True

可知,除了整型对象,其余的数据对象都有__iter__()方法

#没有 __iter__()方法
class A:
	def __init__(self):
		pass

B = A()
print(isinstance(B,Iterable))
>>>False
#有 __iter__()方法
class A:
	def __init__(self):
		pass
	def __iter__(self):
		pass
		
B = A()
print(isinstance(B,Iterable))
>>>True

何为迭代器对象(即迭代器)

实现了迭代器协议的对象
(那什么是迭代器协议?我们待会再讲)

首先,我们看看什么是容器:

容器是一个把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取。比如在python中,属于容器类型的有: list、dict、str、tuple等。

但容器并不具有提供数据的功能,那我们是怎么获取数据的呢?是迭代器赋予了容器这种功能。迭代器对象与可迭代对象区别就在于迭代器对象多了一种__next__()方法。

我们来看下面的例子,我们同样用hasattr()函数来判断:

from collections import Iterable

f = open('a.txt')
i = 1
s = "1234"
d = {'abc':1}
t = (1,2,345)

#hasattr(obj,string) 判断对象中是否有string方法
print(hasattr(f,'__next__')) #判断文件对象是否为迭代器对象
print(hasattr(i,'__next__')) #判断整型是否为迭代器对象
print(hasattr(s,'__next__')) #判断字符串对象是否为迭代器对象
print(hasattr(d,'__next__')) #判断字典对象是否为迭代器对象
print(hasattr(t,'__next__')) #判断元组对象是否为迭代器对象

>>>True
False
False
False
False

可知,除了整型对象都是可迭代对象,但是只有文件时迭代器(对象)

迭代器是如何工作的呢

我们先看下面这个例子:

lst = [1, 2, 3, 4, 5]
for i in lst:
	print(i)
>>> 1 2 3 4 5

它内部的运行机制:

1.调用__iter__()方法,将可迭代对象(这里时列表lst)变成一个迭代器
2.这个迭代器再调用其__next__()方法,返回取到的第一个值,这个元素就被赋值给了i
3.打印输出
4.抛出异常,循环结束

下面我们来模拟循环的每一步并输出:

lst = [1,2,3,4,5]
#将lst变为迭代器
item = lst.__iter__()

#迭代器调用next方法,并且返回取出的元素
print(item.__next__())
print(item.__next__())
print(item.__next__())
print(item.__next__())
print(item.__next__())

#报StopIteration错误
print(item.__next__())

>>>1
2
3
4
5

在最后一次调用item.next()的时候,它会报错,
为什么呢?因为此时已经迭代到最后一个值、没有返回值可以得到了,这说明循环该结束了。

上面的例子还可以写成:

lst = [1,2,3,4,5]
#等价于 lst.__iter__() 人为显示调用
for i in iter(lst):
    print(i)


#解释器隐式调用
for i in lst:
    print(i)


#两种输出均为1,2,3,4,5

所以,我们可以这么认为,一般for循环in后面的对象至少必须是一个可迭代对象。

何为迭代器协议

对象需要提供__next__()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

如何自建迭代器

1.具有__iter__()方法,iter()方法会返回一个迭代器(Iterator)
2.返回的迭代器具有__next__()方法,每次迭代都会返回下一个对象,当没有下一个对象元素时,则会引发StopIteration类型错误。

该过程我们可以使用iter()方法调用对象的__iter__()方法获取一个迭代器,使用next()方法来调用迭代器的__next__()方法。我们看下面的例子:

def forIn(iterable,func):
    iterator = iter(iterable)
    try:
        while True:
            p = next(iterator)
            func(p)
    except StopIteration:
        pass


forIn([1,5,7,10],print)


>>>1
5
7
10

何为生成器

生成器是一类特殊的迭代器。区别在于:自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法和__next__方法,但它没有这个两个函数),即通过自己特殊的方式。

  • 作用?只在需要的时候才产生一个生成器结果对象,不是立即产生结果,

Python有两种方式提供生成器
1.通过生成器表达式:通俗讲,就是把一对中括号 [] 变换成一对小括号 ()

见如下例子:

lst = [1,2,3,4,5]
#生成器generator,类似list,但是把[]改成()
gen = (a for a in lst)
for i in gen:
    print(i)

>>>1
2
3
4
5

我们还可以在shell里做如下对比:
L返回的是列表类型,但G返回的是生成器对象,你只有循环调用next()函数才能访问到里面的每个元素,如果你只调用一次,则它只返回一个元素。

L = [x*2 for x in range(5)]
L
>>>[0,2,4,6,8]


G = (x*2 for x in range(5))
G
>>><generator object <genexpr> at 0x00000230C53857D8>

for i in range(5):
    next(G)

>>>0
2
4
6
8

2.通过生成器函数,函数内部有yield关键字。

现在我们来看看yield 与 return的关系

相同点:都是返回程序中执行结果
区别:yield返回执行结果并不中断程序执行,yield语句一次返回一个结果,在每个结果中间,呈现函数挂起状态,以便下次从它离开的地方继续执行。
而return在返回执行结果的同时中断程序执行

我们现在上例子:

def return_test(n):
	for i in range(n):
		return 2*n

for i in range(return_test(3)):
	print(i)
	
>>>0
1
2
3
4
5

当传入3时,由于return返回的是2*n即6,所以从0开始输出直到5。

我们再看yield的例子:

def yield_test(n):
   for i in range(n):
   	yield 2*i

for i in yield_test(3):
   print(i)
   
>>>0
2
4

该函数返回的是2*i,第一次返回0,函数挂起,再依次返回2、4.

细心的同学可以发现,为什么return例子打印循环那里要加一个range,但是yield例子里却不用加?这说明,return 返回的就是它本身的数据类型,所以我们要通过range将他包装成一个可迭代对象,但是yield返回的是一个迭代器,自然它是一个可迭代对象啦。

装饰器

即高阶函数 + 函数嵌套 + 闭包

装饰器原则:不改变源代码和函数的调用方式
高阶函数:函数参数是一个函数名或者返回的是函数名
函数嵌套:在一个函数中又定义了一个新的函数
闭包:在函数嵌套中,内部函数对外部函数作用域变量的引用(除去全局变量),则称内部函数为闭包。

举个例子,假如你有一个已经上线的网站,之前是免费开放的,有一天你不希望免费开放了,你想让大家登陆后才能访问,你又不能修改源码,此时就要用到装饰器了。

总结

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值