01. 迭代器

  • 迭代器中有可迭代类型Iterable与迭代器类型Iterator两种,这两个类型都是在collections.abc模块中的。

    1.1 可迭代类型

    1.1.1 可迭代类型概述

  • 迭代的概念:其实就是之前所描述的遍历操作,即将序列中的元素逐个取出的操作。

  • 可迭代类型的概念:

    • 可使用for-in循环进行遍历的就是可迭代类型。
    • Python中用collections.abc.Iterable描述可迭代类型。

      1.1.2 Python数据类型中的可迭代类型

  • 在Python自带的七大类型中,可迭代类型有:字符串、列表、元组、字典、集合。 ```python from collections.abc import Iterable

print(“abc”, type(“abc”), isinstance(“abc”, Iterable)) # abc True print([12, 34, 56], type([12, 34, 56]), isinstance([12, 34, 56], Iterable)) # [12, 34, 56] True print((12, 34, 56), type((12, 34, 56)), isinstance((12, 34, 56), Iterable)) # (12, 34, 56) True print({‘name’: ‘Bob’, ‘age’: 18}, type({‘name’: ‘Bob’, ‘age’: 18}), isinstance({‘name’: ‘Bob’, ‘age’: 18}, Iterable)) # {‘name’: ‘Bob’, ‘age’: 18} True print({‘name’, ‘age’, ‘gender’}, type({‘name’, ‘age’, ‘gender’}), isinstance({‘name’, ‘age’, ‘gender’}, Iterable)) # {‘gender’, ‘age’, ‘name’} True

  1. - 非可迭代类型有:数值、空值。
  2. ```python
  3. from collections.abc import Iterable
  4. print(10, type(10), isinstance(10, Iterable)) # 10 <class 'int'> False
  5. print(None, type(None), isinstance(None, Iterable)) # None <class 'NoneType'> False

1.1.3 可迭代类型的特点

  • 非迭代器类型的可迭代数据可以重复迭代,因为这类数据是通过迭代器来记录遍历位置的,每当遍历一次,就会生成一个新的迭代器记录位置。
  • 迭代器类型的可迭代数据不可以重复迭代,因为它是通过next来迭代数据的。 ```python nums = [10, 20, 30]

print(“ 30) for i in nums: print(i)

print(“ 30) for i in nums: print(i)

“”” 运行结果:


10 20 30


10 20 30 “””

  1. <a name="V8ohe"></a>
  2. ### 1.2 迭代器类型
  3. <a name="IdIcU"></a>
  4. #### 1.1.1 迭代器类型的概念
  5. - 迭代器的概念:在迭代期间,用于记录序列遍历位置的对象,就称为迭代器。
  6. - 迭代器类型的概念:
  7. - 有的序列对象中有属性在记录迭代的位置,这种序列就可以称之为迭代器类型。
  8. - 但有的序列对象需要依赖于别的数据对象记录迭代的位置,这种序列就不能称之为迭代器类型的序列。
  9. - Python中用`collections.abc.Iterator`描述迭代器类型。
  10. <a name="xxB83"></a>
  11. #### 1.1.2 迭代器类型的特点
  12. - 迭代器有属性在记录迭代的位置。
  13. - 可以使用for-in循环迭代迭代器中的数据对象。
  14. - 可以使用next获取序列中的元素,且用next获取一个元素,迭代器中就会少一个元素。当迭代器中所有元素都被获取后,该迭代器为空。
  15. - 迭代器中的元素不可以被重复获取。
  16. <a name="aZqSJ"></a>
  17. #### 1.1.3 迭代器类型与可迭代类型的关系
  18. - 因为迭代器需要迭代,所以迭代器类型一定是可迭代类型。
  19. - 从Iterator的定义`class Iterator(Iterable):`中也可以看出,Iterator是Iterable的子类,因此Iterator一定是可迭代的。
  20. - 总结:迭代器类型的数据一定是可迭代类型的,但是可迭代类型的数据不一定是迭代器类型的。
  21. <a name="FjbJl"></a>
  22. #### 1.1.4 Python数据类型中的迭代器类型
  23. - Python中自带的七大类型都不是迭代器类型的,但是之前接触过的map()、enumerate()都是迭代器类型的(也是可迭代类型的)。
  24. ```python
  25. from collections.abc import Iterable, Iterator
  26. nums = [10, 20, 30]
  27. map_obj = map(str, nums)
  28. print(isinstance(map_obj, Iterable)) # True
  29. print(isinstance(map_obj, Iterator)) # True
  30. enum_obj = enumerate(nums)
  31. print(isinstance(enum_obj, Iterable)) # True
  32. print(isinstance(enum_obj, Iterator)) # True

1.1.5 从迭代器类型中获取数据的两种方式

  • for-in循环获取数据:(只可以for-in一次,之后再for-in将获取不到任何数据) ```python nums = [10, 20, 30] map_obj = map(str, nums) print(“ 30) for ele in map_obj: print(ele)

print(“ 30) for ele in map_obj: print(ele)

“”” 运行结果:


10 20 30


“””

  1. - 使用`迭代器对象.__next__()`获取迭代器中的元素:(当迭代器中已经没有元素时,再调用`__next__()`会报错)
  2. ```python
  3. nums = [10, 20, 30]
  4. map_obj = map(str, nums)
  5. print(map_obj.__next__()) # 10
  6. print(map_obj.__next__()) # 20
  7. print(map_obj.__next__()) # 30
  8. print(map_obj.__next__()) # 报错:StopIteration,因为迭代器中所有元素都已经被获取了,故迭代器停止迭代。
  • 使用next(迭代器对象)也可以实现同样的功能。(其实next()底层就是调用迭代器对象的__next__()

    1. nums = [10, 20, 30]
    2. map_obj = map(str, nums)
    3. print(next(map_obj)) # 10
    4. print(next(map_obj)) # 20
    5. print(next(map_obj)) # 30
    6. print(next(map_obj)) # StopIteration
  • 注意:只有迭代器类型的对象可以用__next__()next(),非迭代器类型的迭代对象使用这两个方法会报错。 ```python from collections.abc import Iterable, Iterator

nums = [10, 20, 30] print(isinstance(nums, Iterable), isinstance(nums, Iterator)) # True False

print(next(nums)) # TypeError: ‘list’ object is not an iterator print(nums.next()) # AttributeError: ‘list’ object has no attribute ‘next

  1. <a name="xsNmr"></a>
  2. ### 1.3 可迭代类型数据转换为迭代器类型
  3. - 使用`iter(可迭代类型)`可以将一个可迭代类型的数据对象转换成迭代器类型数据。
  4. ```python
  5. from collections.abc import Iterable, Iterator
  6. nums = [10, 20, 30]
  7. print(isinstance(nums, Iterable), isinstance(nums, Iterator)) # True False
  8. iter_nums = iter(nums)
  9. print(isinstance(iter_nums, Iterable), isinstance(iter_nums, Iterator)) # True True

1.4 自定义迭代器

1.4.1 自定义迭代器记录其他序列的位置

  • 迭代器首先是一个类,那么要让自定义类变成一个迭代器,处理定义基本的类结构外,还要重写以下两个方法:
    • __iter__():用于获取迭代器对象,这个方法也是为什么可迭代类型可以重复for-in的核心:
      • 每次for ele in 可迭代类型时,它的__iter__()方法中都会调用iter()函数将当前可迭代类型对象转换成一个迭代器。
      • 因为每进行一次for-in循环,就会生成一个迭代器对象,故可迭代类型可以重复迭代。
    • __next__():根据迭代器中记录位置的属性逐个获取数据元素。
  • 现要求实现一个自定义迭代器对象,接收一个非迭代器类型的序列,将其转换成迭代器类型(实际上就是以类的形式自定义iter()函数)。

    • 基本思路:
      • 首先,这个迭代器在定义时需要有一个属性来接收传入的非迭代器类型的序列,并且还有一个属性来记录遍历的位置。
      • 接着,实现__iter__()__next__()这两个方法。
    • 代码实现: ```python class MyIter(object): def init(self, seq): self.seq = seq # 接收传入的非迭代器类型的序列 self.current = 0 # 记录当前位置

      从写iter()方法,返回迭代器对象。

      def iter(self):

      实际上当前对象就是迭代器对象,因此将self返回即可。

      return self

      获取数据的next方法

      def next(self): if self.current < len(self.seq):

      1. # 如果当前位置的值小于序列的长度。
      2. # 说明当前迭代器中还有可获取的元素,执行元素获取操作。
      3. value = self.seq[self.current] # 获取当前位置上的数据。
      4. self.current += 1 # 为下一次取值做准备,将当前位置+1。
      5. return value # 将获取到的值返回。

      else:

      1. # 否则说明当前迭代器已经为空了。
      2. raise StopIteration # 没有可遍历的了,报错并结束遍历

mi = MyIter(“hello”) for i in mi: print(i) “”” 运行结果: h e l l o “””

mi.next() # StopIteration

  1. - 通过给第24行代码`for i in mi:`打断点并debug发现,在初始化后,只有第一次迭代会调用到`__iter__()`方法,后面都是直接调用`__next__()`方法。
  2. <a name="GcfZw"></a>
  3. #### 1.4.2 自定义迭代器序列(自定义range)
  4. - 自定义迭代器序列是指序列中的元素产生自这个序列,并且序列中还有记录位置的属性。
  5. - 需求:使用迭代器手动实现range
  6. - 需求分析:
  7. - range有以下三种形式:
  8. - `range(10)`0123456789
  9. - `range(1, 10)`123456789
  10. - `range(1, 10, 2)`13579
  11. - 说明序列中需要有起始值、结束值、步长三个属性,并且起始值和步长可以不传值。
  12. - 代码实现:
  13. ```python
  14. class MyRange(object):
  15. def __init__(self, start, stop=None, step=1):
  16. """
  17. :param start: 起始值
  18. :param stop: 结束值
  19. :param step: 步长
  20. """
  21. if stop is None:
  22. # 因为默认参数只能定义在非默认参数之后,因此不能将start设置为None,只能将stop设置为None。
  23. # 此时若只传入一个值(即stop和step都没有传值),则传入的start应该是结束值,而真正的起始值应该为0。
  24. start, stop = 0, start
  25. if step == 0: # 步长值不能为0。
  26. raise ValueError("MyRange() arg 3 must not be zero")
  27. self.start = start
  28. self.stop = stop
  29. self.step = step
  30. self.current = self.start # 当前数据的起始值是范围的起始值
  31. def __iter__(self):
  32. return self # 返回迭代器对象(即当前对象)
  33. def __next__(self):
  34. if self.step > 0: # 若步长值大于0,即range(1, 10, 2) --> 1、3、5、7、9的形式。
  35. if self.current < self.stop: # 若当前位置小于结束值,说明序列中还有数据没有迭代完成,可以获取数据。
  36. value = self.current # 将当前位置作为值返回。
  37. self.current += self.step # 为下一次做准备,按照步长移动当前位置。
  38. return value
  39. else: # 否则说明当前序列已经迭代完成,无法获取数据。
  40. raise StopIteration
  41. else: # 若步长值小于0,即range(10, 1, -1) --> 10、9、8、7、6、5、4、3、2的形式。
  42. if self.current > self.stop:
  43. value = self.current
  44. self.current += self.step
  45. return value
  46. else:
  47. raise StopIteration
  48. seq = MyRange(10)
  49. for ele in seq:
  50. print(ele, end="\t")
  51. print()
  52. # 0 1 2 3 4 5 6 7 8 9
  53. seq = MyRange(1, 10)
  54. for ele in seq:
  55. print(ele, end="\t")
  56. print()
  57. # 1 2 3 4 5 6 7 8 9
  58. seq = MyRange(1, 10, 3)
  59. for ele in seq:
  60. print(ele, end="\t")
  61. print()
  62. # 1 4 7
  63. seq = MyRange(10, 1, -2)
  64. for ele in seq:
  65. print(ele, end="\t")
  66. print()
  67. # 10 8 6 4 2

02. 生成器

2.1 生成器概述

  • 生成器(Generator)是用来批量生成数据的。
  • 生成器本质上也是一种迭代器,只不过是另一种形式的迭代器,与常规的迭代器不一样。 ```python from collections.abc import Iterator

gen = (i for i in range(1, 1000)) # 生成1~999的数据。 print(gen) # at 0x000001C98DEE7580> print(isinstance(gen, Iterator)) # True

  1. - 其他的序列结构会一次性将序列中所有的数据对象全部加载到内存中,而生成器则是序列中的元素获取一个,才加载一个。
  2. <a name="iFiwg"></a>
  3. ### 2.2 生成器的两种形式
  4. <a name="ulRAO"></a>
  5. #### 2.2.1 ()小括号形式
  6. - 将列表推导式的中括号换成小括号就是一种生成器:`[i for i in range(1, 10)]` --> `(i for i in range(1, 10))`
  7. - 示例:生成一批学号,按照6位数格式化。
  8. ```python
  9. sids = (f"{i:0>6}" for i in range(1, 100000))
  10. print(type(sids)) # <class 'generator'>

2.2.2 yield形式

  • 将函数中的return改成yield也可以形成生成器。(yield必须在函数中的循环结构内,否则获取到的生成器只有一个数据)
  • 示例:斐波那契额数列的一种实现形式如下:

    1. last_month = 1 # 前两个数
    2. before_last_month = 1 # 前一个数
    3. current = 1 # 当前数
    4. for i in range(1, 11):
    5. # 若i小于等于2,则current不做计算。
    6. if i > 2:
    7. current = last_month + before_last_month # 若i大于2,则current为前两个数之和。
    8. before_last_month, last_month = last_month, current # 当current计算完成后,before_last_month和last_month这两个指针变量要后移。
    9. print(current)
  • 为了增加程序的复用性和灵活性,可以将其封装成一个函数。但是可以发现一个问题,就是程序在第一次循环后就return截止了,因此只能获取到元素1。 ```python def get_fibonacci(n): last_month = before_last_month = current = 1 for i in range(1, n + 1):

    1. if i > 2:
    2. current = last_month + before_last_month
    3. before_last_month, last_month = last_month, current
    4. return current

print(get_fibonacci(11)) # 1 print(get_fibonacci(22)) # 1

  1. - return改成yield,即可将所有元素返回到一个生成器中。
  2. - yield的中文意思:产生、产出。
  3. - yield的作用:暂停生成器,将数据返回到调用的位置。在下一次获取数据的时候,在yield的基础上再往下继续逻辑操作。
  4. ```python
  5. def get_fibonacci(n):
  6. last_month = before_last_month = current = 1
  7. for i in range(1, n + 1):
  8. if i > 2:
  9. current = last_month + before_last_month
  10. before_last_month, last_month = last_month, current
  11. yield current
  12. fibonacci_11 = get_fibonacci(11)
  13. print(fibonacci_11) # <generator object get_fibonacci at 0x0000019E9D447580>
  14. print(type(fibonacci_11)) # <class 'generator'>
  • 迭代这个生成器即可获取到整个斐波那契数列。
    1. for i in fibonacci_11:
    2. print(i, end=" ")
    3. # 1 1 2 3 5 8 13 21 34 55 89