1、迭代
1、什么是迭代
Python中的迭代指的是按照某种顺序逐个访问容器(如列表、元组、字典等)中的元素的过程。Python中的迭代可以通过for循环、while循环和生成器实现。
2、迭代的方式
三种方式:
for循环迭代
# 循环遍历列表 fruits = ["apple", "banana", "cherry"] for fruit in fruits: print(fruit) # 循环遍历字符串 string = "Hello, World!" for char in string: print(char) # 循环遍历字典 dict = {"name": "Tom", "age": 18, "gender": "male"} for key in dict: print(key, dict[key])
while循环迭代
i = 0 while i < 5: print(i) i += 1
生成器的迭代
# 生成器函数 def my_range(start, end, step): while start < end: yield start start += step # 使用生成器 for i in my_range(0, 10, 2): print(i)
生成器实现的range
2、可迭代对象
1、可迭代对象
凡是实现了__iter__ (或者__getitem__)方法的都是可迭代对象(Iterable),可迭代对象都可以被迭代器(Iterator)进行迭代操作。
python中基础序列基本都是可迭代对象,我们可以用内置函数 isinstance() 来进行判断。
from collections.abc import Iterable, Iterator #python的可迭代对象 ls = [1, 2, 3, 4, 5] tu = ('a', 'b') dic = {'a': 1, 'b': 2} strs = 'abcd' se = {'d', 2, 3} print("列表是不是可迭代对象:", isinstance(ls, Iterable)) print("元组是不是可迭代对象:", isinstance(tu, Iterable)) print("字典是不是可迭代对象:", isinstance(dic, Iterable)) print("字符串是不是可迭代对象:", isinstance(strs, Iterable)) print("集合是不是可迭代对象:", isinstance(se, Iterable)) print('分割线'.center(50, '=')) print(ls.__dir__()) print(tu.__dir__()) print(dic.__dir__()) print(strs.__dir__()) print(se.__dir__()) 列表是不是可迭代对象: True 元组是不是可迭代对象: True 字典是不是可迭代对象: True 字符串是不是可迭代对象: True 集合是不是可迭代对象: True =======================分割线======================== ['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__', '__new__', '__reversed__', '__sizeof__', 'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__'] ['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', '__getnewargs__', 'index', 'count', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__'] ['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__or__', '__ror__', '__ior__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__contains__', '__new__', '__sizeof__', 'get', 'setdefault', 'pop', 'popitem', 'keys', 'items', 'values', 'update', 'fromkeys', 'clear', 'copy', '__reversed__', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__'] ['__repr__', '__hash__', '__str__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__mod__', '__rmod__', '__len__', '__getitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__new__', 'encode', 'replace', 'split', 'rsplit', 'join', 'capitalize', 'casefold', 'title', 'center', 'count', 'expandtabs', 'find', 'partition', 'index', 'ljust', 'lower', 'lstrip', 'rfind', 'rindex', 'rjust', 'rstrip', 'rpartition', 'splitlines', 'strip', 'swapcase', 'translate', 'upper', 'startswith', 'endswith', 'removeprefix', 'removesuffix', 'isascii', 'islower', 'isupper', 'istitle', 'isspace', 'isdecimal', 'isdigit', 'isnumeric', 'isalpha', 'isalnum', 'isidentifier', 'isprintable', 'zfill', 'format', 'format_map', '__format__', 'maketrans', '__sizeof__', '__getnewargs__', '__doc__', '__setattr__', '__delattr__', '__init__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__dir__', '__class__'] ['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__sub__', '__rsub__', '__and__', '__rand__', '__xor__', '__rxor__', '__or__', '__ror__', '__isub__', '__iand__', '__ixor__', '__ior__', '__len__', '__contains__', '__new__', 'add', 'clear', 'copy', 'discard', 'difference', 'difference_update', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', '__reduce__', 'remove', '__sizeof__', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update', '__class_getitem__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
2、可迭代对象如何被迭代
- 当对可迭代对象进行迭代(for)操作时,首先会调用内置函数iter()返回一个迭代器(Iterator)
- 对返回的迭代器调用 next()函数,逐个读取迭代器内容
# 迭代器对象 class ItRange(): def __init__(self,num): self.num = num self.counter = -1 # 注意,counter是从-1开始的,所以第一次调用__next__函数,会返回0 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.num: # 如果迭代次数超过num,则触发异常StopIteration,停止迭代,并返回属性counter raise StopIteration() return self.counter # 可迭代对象 class XRange(): def __init__(self,max_num): self.max_num = max_num def __iter__(self): return ItRange(self.max_num) print(list(XRange(10))) # 你可以看到输出的最终结果就是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] obj1 = XRange(10) for item in obj1: # 在此时,首先是调用了XRange的__iter__方法,返回了一个迭代器, # 然后逐次调用迭代器的__next__方法,形成每一个值 print(item)
3、迭代器
1、迭代器协议
一个对象需要提供next方法,该方法要么返回迭代对象中的下一项,要么就抛StopIteration异常,迭代终止。
2、迭代器的实现原理
在python中,只要一个对象实现了以下两个方法,那么它就是一个迭代器
_ iter _: 返回一个可迭代对象
_ next _: 返回迭代器的下一项,当没有元素可以返回时抛StopIteration异常,迭代终止。class ItRange(): def __init__(self,num): self.num = num self.counter = -1 # 注意,counter是从-1开始的,所以第一次调用__next__函数,会返回0 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.num: # 如果迭代次数超过num,则触发异常StopIteration,停止迭代,并返回属性counter raise StopIteration() return self.counter
3、迭代器的实现(斐波那契)
from collections.abc import Iterable, Iterator #实现一个返回斐契那波数列的迭代器 class fibo_iterator(): def __init__(self): self.a = 0 self.b = 1 def __iter__(self): # 由于自己就是一个迭代器,所以返回自己就行 return self def __next__(self): if self.b > 100: raise StopIteration else: self.a, self.b = self.b, self.a + self.b return self.a
4、生成器
生成器(generator)也是一种迭代器,在每次迭代时返回一个值,直到抛出 StopIteration异常
1、生成器表达式
a = (x ** 2 for x in range(100)) print(type(a)) E:\env\python39\python.exe D:\项目\python\deke\test.py <class 'generator'>
生成器也可以迭代
2、生成器函数含有 yield 关键字的函数,调用该函数时会返回一个生成器
# 基于生成器和可迭代对象构建range函数 class genRange(): def __init__(self,num): self.num = num def __iter__(self): counter = 0 while counter < self.num: yield counter counter += 1 obj1 = genRange(5) for item in obj1: print(item)
3、生成器进阶
生成器的方法:
生成器比生成器多了三个方法:close()send() throw()
关闭生成器 close()
g=(i for i in range(5)) print(g.__next__()) print(g.__next__()) print(g.__next__()) g.close() print(g.__next__())
生成3个元素后,生成器将不在生成对象了
报:StopIterationsend():和next一样可以用来生成数据,可以往生成器内部传递数据(可以和生成器内部进行交互)
使用生成器时的注意:
1、不管使用next方法还是send方法,每次执行到yield时,都会暂停,并且将yield后面的值返回出来
2、send使用时,生成器内部必须处于yield暂停的位置(使用send之前,生成器至少是通过next去生成一次数据)案例一:
def demo(): for i in range(10): yield i g = demo() print('send生成的数据:', g.send(6)) TypeError: can't send non-None value to a just-started generator # 使用send之前没有通过next去生成一次数据
案例二:
def demo(): for i in range(10): res = yield i print('send传入的数据,', res) g = demo() print('next生成的数据:', next(g)) print('send生成的数据:', g.send(6)) E:\env\python39\python.exe D:\项目\python\deke\test.py next生成的数据: 0 send传入的数据, 6 send生成的数据: 1
案例三:
def work(): host = yield # todo 2、代码暂停到这里 for i in range(5): host = yield "https://{}/user/login".format(host) g = work() next(g) # todo 1、启动生成器 print('send方法生成的数据:', g.send("www.baidu.com")) # todo 3、send方法传值,会把值传到host 4、执行for循环把host传到yield中,并且返回 # todo 5、到行代码就停下来了 print('send方法生成的数据:', g.send("www.baidu1.com")) print('send方法生成的数据:', g.send("www.baidu2.com")) print('send方法生成的数据:', g.send("www.baidu3.com")) print('send方法生成的数据:', g.send("www.baidu4.com")) send方法生成的数据: https://www.baidu.com/user/login send方法生成的数据: https://www.baidu1.com/user/login send方法生成的数据: https://www.baidu2.com/user/login send方法生成的数据: https://www.baidu3.com/user/login send方法生成的数据: https://www.baidu4.com/user/login
throw()
在生成器中抛出异常,效果等同于raise
5、生成器和迭代器的区别
1、实现方式不同
迭代器是通过实现__iter__和__next__方法来实现的。__iter__方法返回迭代器对象本身,__next__方法返回下一个值。当没有更多的元素时,__next__方法会引发StopIteration异常。
生成器则是一种特殊的迭代器,它是通过yield关键字来实现的。yield关键字会暂停函数执行,并返回一个值,下次调用时会从上次暂停的位置继续执行,直到函数执行结束或遇到return语句。
2、调用方式不同
迭代器可以使用for循环或者next方法进行迭代操作。
生成器可以使用for循环或者next方法进行迭代操作,也可以使用yield from关键字来迭代嵌套的生成器。
3、内存占用不同
迭代器需要将所有的元素保存在内存中,如果元素非常多的话,会占用大量的内存空间。而生成器则可以一次生成一个元素,并在下次迭代时再生成下一个元素,因此不需要一次性将所有元素都保存在内存中,可以大大节省内存空间。
4、可复用性不同
迭代器只能遍历一次,遍历结束后就不能再次使用。而生成器则可以被多次遍历,每次遍历时会重新生成一组值。