一.可迭代对象
可迭代对象是指实现了 __iter__()
方法的对象。__iter__()
方法返回一个迭代器对象。常见的可迭代对象包括列表、元组、字符串、字典和集合等。
s = "world"
s1 = [1, 2, 3, 4, 5]
s2 = (1, 2, 3, 4, 5)
s3 = {"name": "猫"}
s4 = {1, 2, 3, 4, 5}
print(isinstance(s, Iterable), isinstance(s1, Iterable), isinstance(s2, Iterable), isinstance(s3, Iterable), isinstance(s4, Iterable))
# 结果:True True True True True
分析:在这段代码中,我们使用了 isinstance()
函数来检查不同的对象是否是可迭代对象,返回结果均为True,说明列表、元组、字符串、字典和集合都是可迭代对象
二.迭代器
迭代器是指实现了 __iter__()
和 __next__()
方法的对象。__next__()
方法返回下一个元素,如果没有更多元素则抛出 StopIteration
异常。
类 MyDatas
的迭代器实现
class MyDatas:
def __init__(self, n):
"""
初始化方法,创建一个包含从1到n的整数列表,并初始化当前索引为0
:param n: 整数,表示列表的最大值
"""
self.datas = [i for i in range(1, n + 1)] # 创建包含从1到n的整数列表
self.current_index = 0 # 初始化当前索引为0
def __iter__(self):
"""
返回迭代器对象自身
:return: 返回self,即当前对象
"""
return self
def __next__(self):
"""
返回下一个元素,如果当前索引超出列表范围,抛出IndexError异常
:return: 返回当前元素
"""
if self.current_index >= len(self.datas): # 检查当前索引是否超出列表范围
raise IndexError # 抛出IndexError异常
else:
current = self.datas[self.current_index] # 获取当前元素
self.current_index += 1 # 将当前索引加1
return current # 返回当前元素
# 假设md是一个MyDatas对象,例如 md = MyDatas(5)
while True:
try:
for s in md: # 尝试遍历md对象
print(s) # 打印当前元素
except IndexError as e: # 捕获IndexError异常
print("超出范围", e) # 打印错误信息
break # 退出循环
分析:
__init__ 方法
初始化一个包含从1到n的整数列表 self.datas。
初始化当前索引 self.current_index 为0。
__iter__ 方法
返回迭代器对象自身,即 self。这使得 MyDatas 实例可以被用作迭代器。
__next__ 方法
检查当前索引是否超出列表范围,如果超出则抛出 IndexError 异常。
否则,返回当前元素并将当前索引加1。
while True:
try:
for s in md: # 尝试遍历md对象
print(s) # 打印当前元素
except IndexError as e: # 捕获IndexError异常
print("超出范围", e) # 打印错误信息
break # 退出循环
分析:
while True 循环不断尝试遍历 md 对象。
for s in md 使用 for 循环遍历 md 对象,这会调用 md 的 __iter__() 方法获取迭代器,并调用 __next__() 方法获取下一个元素。
如果 __next__() 方法抛出 IndexError 异常,捕获该异常并打印错误信息,然后退出循环。
总结:
迭代器:实现了 __iter__() 和 __next__() 方法的对象。
自定义迭代器:通过定义类并实现 __iter__() 和 __next__() 方法来创建自定义迭代器。
迭代器的使用:通过 for 循环或其他迭代方式使用迭代器,自动调用 __iter__() 和 __next__() 方法。
异常处理:在迭代过程中处理可能抛出的异常(如 IndexError)。
三.生成器
生成器是Python中一种特殊的迭代器,它使用 yield
关键字来产生值。生成器函数在调用时返回一个生成器对象,每次调用 next()
时,生成器函数会从上次 yield
的位置继续执行,直到遇到下一个 yield
或函数结束。
生成器的基本概念
生成器函数使用 yield
关键字来产生值,而不是 return
。每次调用 next()
时,生成器函数会从上次 yield
的位置继续执行,直到遇到下一个 yield
或函数结束。
生成器函数
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
for item in gen:
print(item)
分析:在这个例子中,my_generator
是一个生成器函数,它使用 yield
关键字来产生值。调用 my_generator()
返回一个生成器对象 gen
,我们可以使用 for
循环来遍历它。
生成器表达式
gen = (x for x in range(1, 4))
for item in gen:
print(item)
分析:(x for x in range(1, 4))
是一个生成器表达式,它返回一个生成器对象 gen
,我们可以使用 for
循环来遍历它。
优势
结合 sys.getsizeof()
函数,我们可以更直观地理解生成器在内存使用上的优势。sys.getsizeof()
函数用于获取对象的内存占用大小(以字节为单位)。
# 创建一个包含100万个整数的列表
list_data = [i for i in range(1000000)]
# 创建一个生成器表达式,生成100万个整数
gen_data = (i for i in range(1000000))
# 打印列表和生成器的内存占用大小
print(list_data.__sizeof__())
print(gen_data.__sizeof__())
# 结果:8448712 176
分析:通过比较列表和生成器的内存占用大小,我们可以直观地看到生成器在处理大数据集时的内存优势。生成器非常适合处理大数据集,因为它不会一次性将所有数据加载到内存中,从而节省内存资源。
四.装饰器
装饰器是一种用于修改函数或方法行为的高阶函数。装饰器本质上是一个返回函数的函数,它可以在不修改原函数代码的情况下,增加额外的功能。
# 装饰器
def cost_time(f):
def calc():
start = time.time()
f()
print(f"{f.__name__}的时间开销是{time.time() - start}")
return calc
data = [random.randint(1, 10000) for i in range(10000)]
datas = data.copy()
@cost_time
def my_sort1():
data.sort()
print(data)
# my_sort1 = cost_time(my_sort1)
my_sort1()
@cost_time
def my_sort2():
new_datas = sorted(datas, reverse=False)
print(new_datas)
# my_sort2 = cost_time(my_sort2)
my_sort2()
装饰器 cost_time
def cost_time(f):
def calc():
start = time.time()
f()
print(f"{f.__name__}的时间开销是{time.time() - start}")
return calc
cost_time 是一个装饰器函数,它接受一个函数 f 作为参数。
在 cost_time 内部定义了一个嵌套函数 calc,这个函数用于测量 f 的执行时间。
start = time.time():记录函数开始执行的时间。
f():调用原函数 f。
print(f"{f.__name__}的时间开销是{time.time() - start}"):打印函数 f 的名称和执行时间。
cost_time 返回嵌套函数 calc,这样当装饰器应用到一个函数时,实际上是返回了一个新的函数 calc,这个函数包含了原函数 f 的调用和时间测量逻辑
函数 my_sort1
和 my_sort2
@cost_time
def my_sort1():
data.sort()
print(data)
# my_sort1 = cost_time(my_sort1)
my_sort1()
@cost_time
def my_sort2():
new_datas = sorted(datas, reverse=False)
print(new_datas)
# my_sort2 = cost_time(my_sort2)
my_sort2()
my_sort1 和 my_sort2 是两个用于排序的函数。
@cost_time 语法是装饰器的应用,相当于 my_sort1 = cost_time(my_sort1) 和 my_sort2 = cost_time(my_sort2)。
这表示在调用 my_sort1 和 my_sort2 时,实际上是调用了 cost_time 返回的 calc 函数。
my_sort1 使用 list.sort() 方法对 data 进行原地排序。
my_sort2 使用 sorted() 函数对 datas 进行排序,并返回一个新的排序后的列表 new_datas。
总结
装饰器 cost_time 用于测量函数执行的时间开销。
my_sort1 和 my_sort2 分别使用不同的排序方法对数据进行排序。
通过装饰器,我们可以在不修改原函数代码的情况下,增加时间测量的功能。