Python - 可迭代,迭代器,生成器,装饰器

一.可迭代对象

可迭代对象是指实现了 __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 分别使用不同的排序方法对数据进行排序。

通过装饰器,我们可以在不修改原函数代码的情况下,增加时间测量的功能。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值