Python(10):Python迭代器与生成器(iterator、for循环、generator、yield)



一、迭代器(foreach)

1、可迭代的对象

内置有__iter__方法的都叫可迭代的对象。

Python内置str、list、tuple、dict、set、file都是可迭代对象

x = 1.__iter__  # SyntaxError: invalid syntax

# 以下都是可迭代的对象
name = 'nick'.__iter__
print(type(name))  # 'method-wrapper'>

2、迭代器对象

执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象

只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的,只能使用迭代器对象。

  1. 内置有__iter__方法,执行该方法会拿到迭代器本身。
  2. 内置__next__方法,执行该方法会拿到迭代器对象中的一个值。
s = 'hello'
iter_s = s.__iter__()
print(type(iter_s))  # 'str_iterator'> iter_s为迭代器对象

while True:
    try:
        print(iter_s.__next__())
    except StopIteration:
        break
#hello

3、迭代器有两个基本的方法:iter()next()

s = 'hello'
iter_s = iter(s) # 创建迭代器对象
print(type(iter_s))  #  iter_s为迭代器对象

while True:
    try:
        print(next(iter_s)) # 输出迭代器的下一个元素

    except StopIteration:
        break
# hello

4、for迭代器循环

可迭代对象可以直接使用常规for语句进行遍历

for循环称为迭代器循环,in后必须是可迭代的对象。

#str
name = 'nick' 
for x in name:
    print(x)

#list
for x in [None, 3, 4.5, "foo", lambda: "moo", object, object()]:
    print("{0}  ({1})".format(x, type(x)))

#dict
d = {
    '1': 'tasty',
    '2': 'the best',
    '3 sprouts': 'evil',
    '4': 'pretty good'
}

for sKey in d:
    print("{0} are {1}".format(sKey, d[sKey]))

#file
f = open('32.txt', 'r', encoding='utf-8')
for x in f:
    print(x)
f.close()

5、实现迭代器(__next__和__iter__)

把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。

  • __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
  • __next__() 方法会返回下一个迭代器对象。
  • StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

创建一个返回数字的迭代器,初始值为 1,逐步递增 1,在 20 次迭代后停止执行:

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
 
myclass = MyNumbers()
myiter = iter(myclass)
 
for x in myiter:
  print(x)

1、模拟range

class Range:
    def __init__(self, n, stop, step):
        self.n = n
        self.stop = stop
        self.step = step

    def __next__(self):
        if self.n >= self.stop:
            raise StopIteration
        x = self.n
        self.n += self.step
        return x

    def __iter__(self):
        return self


for i in Range(1, 7, 3):
    print(i)

#1
#4

2、斐波那契数列

class Fib:
    def __init__(self):
        self._a = 0
        self._b = 1

    def __iter__(self):
        return self

    def __next__(self):
        self._a, self._b = self._b, self._a + self._b
        return self._a


f1 = Fib()
for i in f1:
    if i > 100:
        break
    print('%s ' % i, end='')

# 1 1 2 3 5 8 13 21 34 55 89

二、生成器

1、yield

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象。

yield后面可以加多个数值(可以是任意类型),但返回的值是元组类型的。

  1. 提供一种自定义迭代器的方式
  2. yield可以暂停住函数,并提供当前的返回值
import sys


def fibonacci(n):  # 函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if counter > n:
            return
        yield a
        a, b = b, a + b
        counter += 1


f = fibonacci(10)  #f 是一个生成器
print(type(f))  # 'generator'>

while True:
    try:
        print(next(f), end=" ")
    except StopIteration:
        sys.exit()

    yield和return:

    1. 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
    2. 不同点:return只能返回一次值;yield可以返回多次值

    2、自定义range()方法

    def my_range(start, stop, step=1):
        while start < stop:
            yield start
            start += 1
    
    
    g = my_range(0, 3)
    print(f"list(g): {list(g)}")

    复杂版本:

    def range(*args, **kwargs):
        if not kwargs:
            if len(args) == 1:
                count = 0
                while count < args[0]:
                    yield count
                    count += 1
            if len(args) == 2:
                start, stop = args
                while start < stop:
                    yield start
                    start += 1
            if len(args) == 3:
                start, stop, step = args
                while start < stop:
                    yield start
                    start += step
    
        else:
            step = 1
    
            if len(args) == 1:
                start = args[0]
            if len(args) == 2:
                start, stop = args
    
            for k, v in kwargs.items():
                if k not in ['start', 'step', 'stop']:
                    raise ('参数名错误')
    
                if k == 'start':
                    start = v
                elif k == 'stop':
                    stop = v
                elif k == 'step':
                    step = v
    
            while start < stop:
                yield start
                start += step
    
    
    for i in range(3):
        print(i)  # 0,1,2
    
    for i in range(99, 101):
        print(i)  # 99,100
    
    for i in range(1, 10, 3):
        print(i)  # 1,4,7
    
    for i in range(1, step=2, stop=5):
        print(i)  # 1,3
    
    for i in range(1, 10, step=2):
        print(i)  # 1,3,5,7,9

    3、生成器表达式(i.for .in)

    把列表推导式的[]换成()就是生成器表达式 。

    优点:比起列表推导式,可以省内存,一次只产生一个值在内存中

    t = (i for i in range(10))
    print(t)  # <generator object  at 0x00000000026907B0>
    print(next(t))  # 0
    print(next(t))  # 1

    举例:

    with open('32.txt', 'r', encoding='utf8') as f:
        nums = [len(line) for line in f]  # 列表推导式相当于直接给你一筐蛋
    
    print(max(nums))  # 2
    
    
    with open('32.txt', 'r', encoding='utf8') as f:
        nums = (len(line) for line in f)  # 生成器表达式相当于给你一只老母鸡。
    
    print(max(nums))  # ValueError: I/O operation on closed file.

    posted on 2019-11-18 20:20  springsnow  阅读(503)  评论(0编辑  收藏  举报

    导航