23. 迭代器

一、什么是迭代器

  迭代器 指的是迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次的结果而继续的,单纯的重复并不是重复。迭代器是用来迭代取值的工具,而涉及到把多个值循环取出的数据类型有:列表、字符串、元组、字典、集合、打开的文件对象等。

  我们可以通过 while 循环的方式取值,这种取值方式只适用于有索引的数据类型,如:列表、字符串、元组等。

names = ["Sakura", "Mikoto", "Shana"]

i = 0
while i < len(names):
    print(names[i])
    i += 1

  为了解决基于索引迭代器取值的局限性,Python 需要提供一种能够不依赖索引的取值方式,这就是迭代器。在 Python 中,但凡是内置有 __iter__() 方法的对象都称为 可迭代对象。在 Python 中,列表、字符串、元组、字典、集合、打开的文件对象等都内置了 __iter__() 方法。调用可迭代对象的 __iter__() 方法会将其转换成迭代器对象。转换为迭代器对象后,我们可以通过 __next__() 方法获取迭代器对象的下一个值。

names = ["Sakura", "Mikoto", "Shana"]

iterator = person.__iter__()
print(iterator)
print(type(iterator), end="\n\n")

while True:
    try:
        print(iterator.__next__())
    except StopIteration:
        break
names = ["Sakura", "Mikoto", "Shana"]

iterator = iter(names)
print(iterator)
print(type(iterator), end="\n\n")

while True:
    try:
        print(next(iterator))
    except StopIteration:
        break

如果我们迭代到最后一项,再调用 __next__() 方法或 next() 方法会抛出 StopIteration 异常;

  迭代器对象 是指内置有 __next__() 方法和 __iter__() 方法的对象。调用迭代器对象的 __next__() 方法将得到迭代器对象的下一个值。调用迭代器对象的 __iter__() 方法将得到迭代器的本身,与没调用该方法一样。

person = {"name": "Sakura", "age": 10}

iterator = iter(person)
print(iterator is  iterator.__iter__() is iterator.__iter__().__iter__())

  如果我们想判断某一个数据类型或对象是否可以迭代,我们可以使用 collections 模块来判断;

from collections.abc import Iterable, Iterator

data = "Sakura"
print("可迭代对象-字符串:", isinstance(data, Iterable))
print("迭代器-字符串:", isinstance(data, Iterator))
print()

data = ["Sakukra", "Mikoto", "Shana"]
print("可迭代对象-列表:", isinstance(data, Iterable))
print("迭代器-列表:", isinstance(data, Iterator))
print()

data = ("Sakukra", "Mikoto", "Shana")
print("可迭代对象-元组:", isinstance(data, Iterable))
print("迭代器-元组:", isinstance(data, Iterator))
print()

data = {"Sakukra", "Mikoto", "Shana"}
print("可迭代对象-集合:", isinstance(data, Iterable))
print("迭代器-集合:", isinstance(data, Iterator))
print()

data = {"name": "Sakura", "age": 10}
print("可迭代对象-字典:", isinstance(data, Iterable))
print("迭代器-字典:", isinstance(data, Iterator))
print()
from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []
  
people = People()
print("可迭代对象:", isinstance(people, Iterable))
print("迭代器:", isinstance(people, Iterator))

二、自定义迭代器

  只要在类中定义 __iter__() 方法,那么这个类创建出来的对象一定是可迭代的。通俗的说:一个具备了 __iter__() 方法的对象,就是一个 可迭代对象

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []

    def __iter__(self):
        pass
  
people = People()
print(isinstance(people, Iterable))
print("迭代器:", isinstance(people, Iterator))

  如果想实现自定义迭代器的话,除了在类中定义 __iter__() 方法还需要在类中再定义 __next__() 方法,即一个实现了 __iter__() 方法和 __next__() 方法的对象,就是 迭代器

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.container = []
  
    def add(self, name):
        self.container.append(name)

    def __iter__(self):
        # 这个方法有两个功能:
        # 1.标记用当前类创建出来的对象是一定是可迭代对下个你
        # 2.当调用iter()函数时,这个方法会被自动调用,返回的自己指定的那个迭代器
        return MyIterator(self)

class MyIterator:
    def __init__(self, obj):
        self.__obj = obj
        self.__current = 0

    def __iter__(self):
        pass

    def __next__(self):
        # 这个方法有两个功能:
        # 1.标记当前类创建的对象(一定有__iter__()方法)一定是迭代器
        # 2.当调用next()函数时,这个方法会自动调用,它返回一个数据
        if self.__current >= len(self.__obj.container):
            raise StopIteration
            
        item = self.__obj.container[self.__current]
        self.__current += 1
        return item
            
  
people = People()
print("People-可迭代对象:", isinstance(people, Iterable))
print("People-迭代器:", isinstance(people, Iterator))
print()

iterator = MyIterator(people)
print("MyIterator-可迭代对象:", isinstance(iterator, Iterable))
print("MyIterator-迭代器:", isinstance(iterator, Iterator))
print()

people.add("Sakura")
people.add("Mikoto")
people.add("Shana")

for item in people:
    print(item)
    
for item in people:
    print(item)

 for 循环是一个已经实现的功能,它里面自带了 iter()next() 方法,并且带有 StopIteration 异常判断,通过这个异常判断来决定是否还需要获取迭代器对象。如果自己规定用 None 来表示数据获取完毕,但是 for 循环的代码依然用的是异常处理,所以 for 循环会产生死循环。

from collections.abc import Iterable, Iterator

class People:
    def __init__(self):
        self.__container = []
        self.__current = 0
  
    def add(self, name):
        self.__container.append(name)

    def __iter__(self):
        # 这个方法有两个功能:
        # 1.标记用当前类创建出来的对象是一定是可迭代对下个你
        # 2.当调用iter()函数时,这个方法会被自动调用,返回的自己指定的那个迭代器
        self.__current = 0
        return self
  
    def __next__(self):
        # 这个方法有两个功能:
        # 1.标记当前类创建的对象(一定有__iter__()方法)一定是迭代器
        # 2.当调用next()函数时,这个方法会自动调用,它返回一个数据
        if self.__current >= len(self.__container):
            raise StopIteration
            
        item = self.__container[self.__current]
        self.__current += 1
        return item
            

people = People()

people.add("Sakura")
people.add("Mikoto")
people.add("Shana")

for item in people:
    print(item)

for item in people:
    print(item)

当对一个可迭代对象调用 iter() 方法时,它会自动调用这个可迭代对象的 __iter__() 方法,这个方法返回的对象当做迭代器。

当对一个迭代器对象i调用 next() 方法时,它会自动调用这个迭代器对象的 __next__() 方法,这个方法返回想要的那个数据。

三、迭代器的优势

  迭代器是 惰性计算,它允许逐个访问容器中的元素,而不需要一次性将所有数据加载到内存中,所以能够显著降低内存占用。当数据量很大或者不确定要使用结果个数时,推荐使用迭代器。

from operator import index
import tracemalloc


class Fibo:
    def __init__(self, total):
        self.total = total          # 要生成的数
        self.index = 0              # 当前生成的数的索引
        self.previos = 1            # 前一个数
        self.current = 1            # 当前数

    def __iter__(self):
        return self
    
    def __next__(self):
        # 当生成足够数量后,抛出停止异常
        if self.index >= self.total:
            raise StopIteration
        
        # 前两项都是1
        if self.index == 0 or self.index == 1:
            self.index += 1
            return 1
        
        # 新的结果等于前两项之和
        self.index += 1
        self.previos, self.current = self.current, self.previos + self.current
        return self.current
    
def fibo(total):
    nums = []

    if total <= 0:
        return nums
    elif total == 1:
        nums.append(1)
        return nums
    
    nums = [1, 1]
    for _ in range(2, total):
        nums.append(nums[-1] + nums[-2])
        
    return nums

total = 10000

tracemalloc.start()                 # 开启内存追踪

f1 = Fibo(total)

# 获取当前已追踪的内存,返回的是字节
peak_memory_size = tracemalloc.get_traced_memory()[1] 
print(f"峰值内存占用:{peak_memory_size / 1024 / 1024}MB")

tracemalloc.stop()                  # 停止追踪


tracemalloc.start()                 # 开启内存追踪

f2 = fibo(total)

# 获取当前已追踪的内存,返回的是字节
peak_memory_size = tracemalloc.get_traced_memory()[1] 
print(f"峰值内存占用:{peak_memory_size / 1024 / 1024}MB")

tracemalloc.stop()                  # 停止追踪
posted @ 2024-10-26 18:51  星光映梦  阅读(50)  评论(0)    收藏  举报