关于迭代器:
什么是迭代?
迭代是访问集合元素的一种方式。
对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 异常"""
自定义迭代器:
使用 iter 和 next 方法自定义迭代器。
只要在类中,定义 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 列表储存全体正整数,无穷个正整数再大的内存也无法装得下,这个时候就可以使用生成器,实现用一段代码来储存全部正整数的作用。
生成器的创建:
- 定义一个以 yield 关键字标识返回值的函数;
- 调用刚刚创建的函数,即可创建一个生成器。
代码实例:
# 生成器的创建
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()函数里的。
如果你对Python感兴趣,想要学习python,这里给大家分享一份Python全套学习资料,都是我自己学习时整理的,希望可以帮到你,一起加油!
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
Python全套学习资料
1️⃣零基础入门
① 学习路线
对于从来没有接触过Python的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
② 路线对应学习视频
还有很多适合0基础入门的学习视频,有了这些视频,轻轻松松上手Python~
③练习题
每节视频课后,都有对应的练习题哦,可以检验学习成果哈哈!
2️⃣国内外Python书籍、文档
① 文档和书籍资料
3️⃣Python工具包+项目源码合集
①Python工具包
学习Python常用的开发软件都在这里了!每个都有详细的安装教程,保证你可以安装成功哦!
②Python实战案例
光学理论是没用的,要学会跟着一起敲代码,动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。100+实战案例源码等你来拿!
③Python小游戏源码
如果觉得上面的实战案例有点枯燥,可以试试自己用Python编写小游戏,让你的学习过程中增添一点趣味!
4️⃣Python面试题
我们学会了Python之后,有了技能就可以出去找工作啦!下面这些面试题是都来自阿里、腾讯、字节等一线互联网大厂,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
5️⃣Python兼职渠道
而且学会Python以后,还可以在各大兼职平台接单赚钱,各种兼职渠道+兼职注意事项+如何和客户沟通,我都整理成文档了。
上述所有资料 ⚡️ ,朋友们如果有需要的,可以扫描下方👇👇👇二维码免费领取🆓