Python — 函数进阶(2)

函数进阶(2)

这是函数进阶2.0版本,
将会介绍的就是关于可迭代对象、迭代器、生成器三个概念以及使用方式,
只有将这三者弄明白我们才能够加深对序列的了解。

可迭代对象

在前文之中我们讲到了很多的可迭代对象,
比如说使用zipfiltermap这三个函数,我们将会得到的分别是压缩对象、过滤器对象以及映射对象,但是这三者都是可迭器,

Tip:刚兴趣的小伙伴可以看看我上一篇文章的内容

下方链接直达
函数进阶(1)

这所有的一切都指向迭代器,为了弄清楚什么是迭代器
我们需要先解锁前置知识,什么是可迭代对象?

什么是可迭代对象

先上教科书定义:

可迭代对象就是一群拥有 __iter__()方法的类实例对象

以上就是可迭代对象的定义,over。

没错定义就是这么言简意赅,但是类实例又是什么鬼呢?
果然,要解释一个问题,就会引出另外一个问题,可真麻烦呢。
那么就让我娓娓道来:

Python 是一个面向对象的编程语言,
而面向对象的语言最大的特点就是 —— 类,
类中被封装了大量的属性以及方法,但是却无法直接使用,
需要创建一个实例化的类对象,这就被我们称作为类实例啦

OK,继续回到原本的话题,既然知道了拥有__iter__()方法的就是可迭代对象,
那么究竟有哪些类实例拥有这个方法呢?
我们众所周知的序列都是可迭代对象,但可迭代对象远远不止于此,这一点我将再后续深入浅出地说明,
但首先我们要知道该如何查看这个类拥有__iter__方法

# 我们使用__dict__就可以查看类对象是否拥有这个方法,
# 如果类对象本身就拥有__iter__方法的话,那么其实例化后的类实例也是拥有的
list.__dict__

{'__new__': <built-in method __new__ of type object at 0x00007FFA921F7610>, '__repr__': <slot wrapper '__repr__' of 'list' objects>, '__hash__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'list' objects>, '__lt__': <slot wrapper '__lt__' of 'list' objects>, '__le__': <slot wrapper '__le__' of 'list' objects>, '__eq__': <slot wrapper '__eq__' of 'list' objects>, '__ne__': <slot wrapper '__ne__' of 'list' objects>
'__iter__': <slot wrapper '__iter__' of 'list' objects>.....(省略N多属性、方法)

可迭代对象的意义

在阐述意义之前,我们必须要提出一个问题,那就是for循环的原理是怎样的?
让我们抛弃for循环,使用while实现跟他一样的效果;

list_1 = [1,3,5,7,9]
for element in list_1:
	print(element)
# 这将会依次得到 1 , 3 , 5 , 7 , 9

count = 0						# 定义一个计数器
length = len(list_1)
while count <= length:
	print(list_1[count])
	count += 1
# 这将会依次得到 1 , 3 , 5 , 7 , 9

我们使用while循环达到了跟for循环同样的功能,
但是代码却多了很多行,
而且最为重要的一点是,我们是通过索引来访问列表中的元素,

(集合小声逼逼:我是无序序列,我不配有索引)

那么如果我们需要依次遍历出字典的键该怎么办呢?
for循环能够轻易实现,可是对于用while循环却十分困难。

因此,Python提供了一个统一的方法 —— 可迭代对象,

这提供了巨大的便捷性,让我们不再受到索引的限制,
可以随心所欲地对可迭代对象进行取值。

可迭代对象的使用

接下来就要介绍一下可迭代对象的使用了,

dict_1 = {'Jonasen': 'Jostar', 'Joseph': 'Jostar', 'Kujo': 'Jotaro'}
# 这是一个可迭代对象,接下来我们要让其变成迭代器
iter_dict = dict_1.__iter__()

print(iter_dict)			
<dict_keyiterator object at 0x00000204EC58E840>

OK,这样就完成了迭代器的转换,
是不是异常简单,但是如果仅仅只是这样的话我们只完成了迭代器而已,

dict_1 = {'Jonasen': 'Jostar', 'Joseph': 'Jostar', 'Kujo': 'Jotaro'}
iter_dict = dict_1.__iter__()

# 更新我们的代码
print(iter_dict.__next__())				# 这将会取出第一个键, Jonasen
print(iter_dict.__next__())				# Joseph
print(iter_dict.__next__())				# Kujo
print(iter_dict.__next__())				# 报错,StopIteration

'先将拥有__iter__()方法的可迭代对象转换成迭代器,然后再使用__next__()不断迭代'
'如果超出超过原本元素的数量,则会报错 StopIteration '

迭代器

什么是迭代器

迭代器这个概念在上文之中其实就已经完成了介绍了,

迭代器就是可迭代对象使用__iter__()转换而来的对象,
并且拥有__next__()的方法,能够让我们不断进行迭代

迭代器的优势

1)不需要使用索引就能够不断进行重复的取值操作;
2)因为迭代器采用的惰性计算的原理,因此占用内存极小;

惰性计算
迭代器本身是一个数据流,同一时间只会保存一个数值,正因为这种储存方式,即使原本的
列表拥有的元素多达成百上千个,在转换成迭代器对象后其本身只会保存一个数值,并且在
使用__next__()就会丢弃原来的,使用下一个(迭代)。

迭代器的劣势

1)因为惰性计算同一时间只会保存一个值的原因,迭代器只能够依次取值,并且同时无法
回溯到上一个值;
2)无法测量迭代器的长度,除非将所有元素遍历一遍,否则我们无法知悉长度(因为迭代器不支持使用len()

怎么使用迭代器

接下来继续介绍迭代器的使用方式,
什么?
你认为就介绍完了?
哦,那我只能说你以后都必须在使用迭代器和可迭代对象的时候总会比别人多敲8个下划线。

老规矩,让Python小姐自己告诉我们,__iter__的原理是怎样的。

help(list.__iter__)

# 你会得到如下的信息
'''
__iter__(self, /)
    Implement iter(self).     ===>  中文: 对自己使用iter()函数
'''

原来所谓的__iter__()只不过是对自己使用函数iter()
等于说,每当我们使用 list.__iter__()的时候,其实跟iter(list)效果是一样的!

(如果有兴趣的话还可以使用help(iter),让Python自己告诉你iter函数)

那么接下里的__next__()其实也是一样的,
有一个名为next()的函数……

生成器

OK,接下来就是全新的东西 —— 生成器。

什么是生成器

生成器:
由我们自己定义的迭代器

没错,就如此简洁,
生成器其实就是有我们自己定义的迭代器,

前文中我们介绍了使用iter()或者__iter__()将可迭代对象转化成迭代器,
而现在我们要自己编写一个迭代器(我们称为生成器)

怎么使用生成器

以函数的形式使用生成器
def new_range(start:int,end:int,step=3):
	while end >= start:
		yield end
		end -= step
	yield '运行结束'
generator = new_range(1,9)				# 初始化生成器
print(next(generator))					# 9
print(next(generator))					# 6
print(next(generator))					# 3
print(next(generator))					# 运行结束

当函数与yield结合使用的时候,就达到了现在这种效果,
关于yiled需要注意的是以下几点:

1)使用了yield的函数,必须要进行初始化,即使这个函数不需要任何参数;
2)yield在函数中的作用效果与return类似,当执行到其的时候会给出一个返回值,但后续的代码会暂时挂起(return是直接终止后续代码的执行);
3)yield可以用于接收外部输入;

接下来就介绍一下yield该如何接收外部输入,

def power(num):
    while True:
        x = yield
        print(x**num)

power_2 = power(2)
power_2.send(None)					# 初始化函数,相当于next(power_2),让其执行到yiled等待接收参数
power_2.send(2)						# 将 2 赋值给 x , 相当于 x = 2
power_2.send(3)						# 打印输出后,重新执行到yield,接收参数3

以上就实现了如何接收外部的输入,
但是为了完成这个,我们必须要注意以下几点:

1)必须使用循环,否则的话接收一个参数后就会报错
2)不可在循环中使用return,否则的话会终止整个循环

让我来用代码示范一遍即可,

def power(num):
    x = yield
    print(x ** num)

pow_2 = power(2)
next(pow_2)
pow_2.send(3)

'''
9
StopIteration
'''

先是输出了一个9,然后就给出StopIteration的错误,
因为使用了pow_2.send()这个方法后,函数会自动去寻找到下一个yiel
而不使用循环,则会给出一个停止迭代的报错。

def power(num):
    while True:
        x = yield
        return x**num

pow_2 = power(2)
next(pow_2)
pow_2.send(3)

'''
StopIteration: 9
'''

有了返回值也是会破坏整个while循环,
当使用pow_2.send()后执行到了return便给出了一个停止循环的错误。

生成器表达式

生成器表达式其实就是列表解析,
感兴趣的朋友可以直接戳下方链接跳转到我以前的文章,详细了解。
Python — 列表解析

generator = ( i for i in range(5))
print(generator)
# <generator object <genexpr> at 0x0000026D4FD75E70>

generator = [i for i in range(5)]
print(generator)
# [0, 1, 2, 3, 4]

当使用列表解析的时候需要格外注意的就是该使用小括号还是中括号,
因为这两者是截然不同的。

for循环的原理介绍

在完成了可迭代对象、迭代器、生成器的介绍之后,
就十分有必要介绍一下for循环的原理,
因为for循环其实就是iter()next()while以及try……except的组合使用

dict_1 = {'Jonasen': 'Jostar', 'Joseph': 'Jostar', 'Kujo': 'Jotaro'}

iter_dict = iter(dict_1)
while True:
	try:
		print(next(iter_dict))
	except StopIteration:
		break

步骤拆解一下

1)将可迭代对象转换成为一个迭代器
2)放入while循环进行循环打印,并且不断调用next()
3)使用异常捕获,当检测到StopIteration的时候就停止整个循环

以上就是本篇文章的所有内容,
而最后一篇函数进阶将会介绍以下递归这个重要的编程方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值