# 迭代器 & 生成器

## 迭代器

> 访问集合元素的一种方式，迭代器对象从集合的第一个元素开始访问，直到素有的元素被访问完结束，迭代器只能往前不会后退。不要求事先准备好整个迭代过程中所有的元素，迭代器仅仅在迭代到某个元素时才计算该元素，而在这之前或之后，元素可以不存在或者被销毁。适用于遍历一些巨大的或是无限的集合，比如几个G的文件。

## 迭代器特点

1. 访问者不需要关心迭代器内部的结构，仅需通过 next() 方法不断去取下一个内容
2. 不能随机访问集合中的某个值，只能从头到尾一次访问
3. 访问到一半时不能往回退
4. 便于循环比较大的数据集合，节省内存

## 生成一个迭代器

```python
nums = iter([1,2,3])
print(nums)
print(nums.__next__()) # 1， Python 2.7 nums.next()
print(nums.__next__()) # 2
print(nums.__next__()) # 3
print(nums.__next__()) # StopIteration
```

## 生成器

> 一个函数调用时返回一个迭代器，那个函数就叫做生成器(generator)，如果函数中包含yield 语法，那这个函数就会变成生成器

* 只要含有 `yield` 关键字的函数都是生成器函数
* `yield` 和 `return` 不能共用且需要卸载函数内
* 只要是生成器函数执行之后得到一个生成器作为返回值
* `生成器.__next__()` 返回生成器执行结果，生成器也是迭代器

## yield 作用

yield 可以是函数中断，并保存中断状态，中断后，代码可以继续往下执行，过一段时间还可以在重新调用这个函数，从上次 yield 的下一句开始执行。

```python
def cash_out(amount): # 函数是生成器
  while amount > 0:
    amount -= 100
    yield 100
    print("擦，又来取钱了...败家子")

ATM = cash_out(500) # ATM 是迭代器
print(type(ATM)) # <class 'generator'>
print("取到钱 %s 万" ATM.__next__()) # 100万
print("取到钱 %s 万" ATM.__next__()) # 100万
print("取到钱 %s 万" ATM.__next__()) # 100万
print("取到钱 %s 万" ATM.__next__()) # 100万
print("取到钱 %s 万" ATM.__next__()) # 100万
```

## 总结

* 双下方法：很少直接调用，一般通过其他语法触发此方法
* 可迭代的 - 可迭代协议：含有**iter**的方法('**iter**' in dir(数据))
* 可迭代的一定可以被 for 循环
* 迭代器协议：含有**iter**和**next**方法
* 迭代器一定可迭代，可迭代器的通过调用iter()方法就能得到一个迭代器
* 迭代器特点
  * 方便使用，切只能取所有的数据取一次
  * 节省内存空间
* 生成器
  * 生成器的本质就是迭代器
  * 生成器表现形式
    * 生成器函数
    * 生成器表达式
* 生成器函数
  * 含有 yield 关键字的函数就是生成器函数
  * 调用函数之后函数不执行，返回一个生成器
  * 函数外调用next方法时候渠道一个值
  * 直到取完最后一个，在执行next会报错

```python
def generator():
  for i in range(2000000):
    yield '哈哈哈%s' %i
g = generator() #调用生成器函数得到一个生成器
res = g.__next__() # 每一次执行g.__next__就是从生成器中取值，预示着生成器函数中的代码继续执行
print(res)

num = 0
for i in g:
  num += 1
  if num > 50:
    break
  print(i)
```

* 从生成器中取值的几个方法
  * next 方法
  * for
  * 数据类型的强制转换（占用所有数据的内存）
* send 获取下一个值得效果和 next 基本一样，只是在获取下一个值的时候，给上一个值得位置传递一个数据
  * 第一次使用生成器的时候，用next 获取下一个值
  * 最后一个yield 不能接受外部的值

## 生成器表达式

> 每次只能获取一个数据，不占用太多内存

```python
g = (i for i in range(10))
print(g)
for i in g:
  print(i)
```

## 列表推导式

> 获取所有数据，占用内存

```python
egg_list = ['鸡蛋%s' %i for i in range(10)]
print(egg_list)
```

## 各种推导式

\[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理

\[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能

```python
#30以内所有能被3整除的数
ret = [i for i in range(30) if i%3 == 0]  #完整的列表推导式
g = (i for i in range(30) if i%3 == 0)  #完整的列表推导式
print(ret)

#30以内所有能被3整除的数的平方
ret = [i*i for i in (1,2,3,4) if i%3 == 0]
ret = (i*i for i in range(30) if i%3 == 0)
print(ret)

# 例三:找到嵌套列表中名字含有两个‘e’的所有名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
#['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
ret = [name for lst in names for name in lst if name.count('e') ==2]
ret = (name for lst in names for name in lst if name.count('e') ==2)
print(ret)
```

## 字典推导式

```python
# 例一：将一个字典的key和value对调
mcase = {'a': 10, 'b': 34}
#{10:'a' , 34:'b'}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)

# 例二：合并大小写对应的value值，将k统一成小写
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
#{'a':10+7,'b':34,'z':3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase}
print(mcase_frequency)
```

## 集合推导式，自带结果去重功能

```python
squared = {x**2 for x in [1, -1, 2]}
print(squared)
```

* 各种推导式 ： 生成器 列表 字典 集合
* 遍历操作
* 筛选操作


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hezhiqiang8909.gitbook.io/python-ai/12_generator.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
