Python中Generators教程

转载至:https://www.bytelang.com/article/content/NQbmUaRIXyA=



要想创建一个iterator,必须实现一个有__iter__()和__next__()方法的类,类要能够跟踪内部状态并且在没有元素返回的时候引发StopIteration异常.

这个过程很繁琐而且违反直觉.Generator能够解决这个问题.

python generator是一个简单的创建iterator的途径.前面讲的那些繁琐的步骤都可以被generator自动完成.

简单来说,generator是一个能够返回迭代器对象的函数.

怎样创建一个python generator?

就像创建一个函数一样简单,只不过不使用return 声明,而是使用yield声明.

如果一个函数至少包含一个yield声明(当然它也可以包含其他yield或return),那么它就是一个generator.

yield和return都会让函数返回一些东西,区别在于,return声明彻底结束一个函数,而yield声明是暂停函数,保存它的所有状态,并且后续被调用后会继续执行.

generator函数和普通函数的区别

  • generator函数包含一个以上的yield声明
  • generator函数被调用的时候,会返回一个iterator对象,但是函数并不会立即开始执行
  • __iter__()和__next__()方法被自动实现,所以可以使用next()函数对返回的此iterator对象进行迭代
  • 一旦一个generator 执行到yield语句,generator函数暂停,程序控制流被转移到调用方
  • 在对generator的连续调用之间,generator的本地变量和状态会被保存
  • 最终,generator函数终止,再调用generator会引发StopIteration异常

下面这个例子说明上述全部要点,我们有一个名为my_gen()的函数,它带有一些yield声明.

# A simple generator function
def my_gen():
    n = 1
    print(‘This is printed first‘)
    # Generator function contains yield statements
    yield n  

    n += 1
    print(‘This is printed second‘)
    yield n  

    n += 1
    print(‘This is printed at last‘)
    yield n  

在线实例:https://www.bytelang.com/o/s/c/nDeJ2dm7FUo=

有趣的是,在这个例子里变量n在每次调用之间都被记住了。和一般函数不同的是,在函数yield之后本地变量没有被销毁,而且,generator对象只能被这样迭代一次。

要想重复上面的过程,需要类似 a = my_gen() 这样创建另一个generator对象,并对其使用next方法迭代。

注意

:我们可以对generator对象直接使用for循环。

这是因为一个for循环接收一个iterator对象,且使用next()函数迭代它,当遇到StopIteration异常的时候自动停止。

# A simple generator function
def my_gen():
    n = 1
    print(‘This is printed first‘)
    # Generator function contains yield statements
    yield n  

    n += 1
    print(‘This is printed second‘)
    yield n  

    n += 1
    print(‘This is printed at last‘)
    yield n  

# Using for loop  

# Output:
# This is printed first
# 1
# This is printed second
# 2
# This is printed at last
# 3  

for item in my_gen():
    print(item) 

在线示例:https://www.bytelang.com/o/s/c/3py5nUg_WVI=

有循环的python generator

上面的例子没有实际的应用意义,我们只是为了探究背后原理。

通常来说,generator都是和循环结合实现的,且这个循环带有一个终止条件。

我们来看一个reverse一个字符串的例子

def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1,-1,-1):
        yield my_str[i]  

# For loop to reverse the string
# Output:
# o
# l
# l
# e
# h
for char in rev_str("hello"):
     print(char)  

在线示例:https://www.bytelang.com/o/s/c/_rs3yQEbIhE=

我们在for循环里面使用range()函数来获取反向顺序的index。

generator除了可以应用于string,还可以应用于其它类型的iterator,例如list,tuple等。

python generator 表达式

使用generator表达式可以很容易地创建简单的generator。

就像lambda函数可以创建匿名函数一样,generator函数创建一个匿名generator函数。

generator表达式的语法类似于python的list comprehension,只是方括号被替换为了圆括号而已。

list comprehension和generator表达式的主要区别在于,前者产生全部的list,后者每次仅产生一项。

它们有些懒惰,仅在接到请求的时候才会产生输出。因此,generator表达式比list comprehension更加节省内存。

# Initialize the list
my_list = [1, 3, 6, 10]  

# square each term using list comprehension
# Output: [1, 9, 36, 100]
[x**2 for x in my_list]  

# same thing can be done using generator expression
# Output: <generator object <genexpr> at 0x0000000002EBDAF8>
(x**2 for x in my_list)  

在线示例:https://www.bytelang.com/o/s/c/BgIb7R1NCls=

上面的例子中,generator表达式没有立即产生需要的结果,而是在需要产生item的时候返回一个generator对象。

# Intialize the list
my_list = [1, 3, 6, 10]  

a = (x**2 for x in my_list)
# Output: 1
print(next(a))  

# Output: 9
print(next(a))  

# Output: 36
print(next(a))  

# Output: 100
print(next(a))  

# Output: StopIteration
next(a)  

在线示例:https://www.bytelang.com/o/s/c/p1^6fITXP5A=

generator表达式可以在函数内部使用。当这样使用的时候,圆括号可以丢弃。

python里为什么要使用generator?

1.容易实现

相对于iterator类来说,generator的实现清晰、简洁。下面是用iterator实现一个2的指数函数

class PowTwo:
    def __init__(self, max = 0):
        self.max = max  

    def __iter__(self):
        self.n = 0
        return self  

    def __next__(self):
        if self.n > self.max:
            raise StopIteration  

        result = 2 ** self.n
        self.n += 1
        return result  

generator这样实现

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1  

因为generator自动跟踪实现细节,因此更加清晰、简洁。

2.节省内存

一个函数返回一个序列(sequence)的时候,会在内存里面把这个序列构建好再返回。如果这个序列包含很多数据的话,就过犹不及了。

而如果序列是以generator方式实现的,就是内存友好的,因为他每次只产生一个item。

3.代表无限的stream

generator是一个很棒的表示无限数据流的工具。无限数据流不能被保存在内存里面,并且因为generator每次产生一个item,它就可以表示无限数据流。

下面的代码可以产生所有的奇数

def all_even():
    n = 0
    while True:
        yield n
        n += 2  

4.generator流水线(pipeline)

generator可以对一系列操作执行流水线操作。

假设我们有一个快餐连锁店的日志。日志的第四列是每小时售出的披萨数量,我们想对近5年的这一数据进行求和。

假设所有数据都是字符,不可用的数据都以"N/A"表示,使用generator可以这样实现

with open(‘sells.log‘) as file:
    pizza_col = (line[3] for line in file)
    per_hour = (int(x) for x in pizza_col if x != ‘N/A‘)
    print("Total pizzas sold = ",sum(per_hour))  

这个流水线既高效又易读,并且看起来很酷!:)

时间: 2024-10-15 07:06:03

Python中Generators教程的相关文章

python中的教程

1.绘图 1)颜色 plot方法的关键字参数color(或c)用来设置线的颜色.可取值为: 1.颜色名称或简写 b: blue g: green r: red c: cyan m: magenta y: yellow k: black w: white 2.#rrggbb 3.(r, g, b) 或 (r, g, b, a),其中 r g b a 取均为[0, 1]之间 4.[0, 1]之间的浮点数的字符串形式,表示灰度值.0表示黑色,1表示白色 (2)样式 plot方法的关键字参数linest

python操作txt文件中数据教程[1]-使用python读写txt文件

python操作txt文件中数据教程[1]-使用python读写txt文件 觉得有用的话,欢迎一起讨论相互学习~Follow Me 原始txt文件 程序实现后结果 程序实现 filename = './test/test.txt' contents = [] DNA_sequence = [] # 打开文本并将所有内容存入contents中 with open(filename, 'r') as f: for line in f.readlines(): contents.append(line

Python中sqlite学习教程

Python SQLITE数据库是一款非常小巧的嵌入式开源数据库软件,没有独立的维护进程,所有的维护都来自于程序本身.它使用一个文件存储整个数据库,操作十分方便. 以下是重要的 sqlite3 模块程序,可以满足您在 Python 程序中使用 SQLite 数据库的需求.如果您需要了解更多细节,请查看 Python sqlite3 模块的官方文档. 序号 API & 描述 1 sqlite3.connect(database [,timeout ,other optional arguments

[ Python入门教程 ] Python中JSON模块基本使用方法

JSON (JavaScript Object Notation)是一种使用广泛的轻量数据格式,Python标准库中的json模块提供了一种简单的方法来编码和解码JSON格式的数据.用于完成字符串和python数据类型间进行转换. json模块基本函数   json.dumps():把Python数据类型转换成JSON字符串   json.loads():把JSON字符串转换成Python数据类型   json.dump():把Python数据类型转换成JSON字符串并存储在文件中   json

[ Python入门教程 ] Python中日志记录模块logging使用实例

python中的logging模块用于记录日志.用户可以根据程序实现需要自定义日志输出位置.日志级别以及日志格式. 将日志内容输出到屏幕 一个最简单的logging模块使用样例,直接打印显示日志内容到屏幕. import logging logging.critical("critical log") logging.error("error log") logging.warning("warning log") logging.info(&q

python基础教程:Python中垃圾回收和del语句详解

Python语言默认采用的垃圾收集机制是引用计数法,本文详细的介绍了Python中垃圾回收和del语句详解,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧Python中的垃圾回收算法是采用引用计数, 当一个对象的引用计数为0时, Python的垃圾回收机制就会将对象回收 https://tieba.baidu.com/p/6575202861 a = "larry"b = ahttps://www.douban.com/group/topic/16

Python学习入门教程,字符串函数扩充详解

因有用户反映,在基础文章对字符串函数的讲解太过少,故写一篇文章详细讲解一下常用字符串函数.本文章是对:程序员带你十天快速入门Python,玩转电脑软件开发(三)中字符串函数的详解与扩充. 如果您想学习并参与本教程的完善与写作.请在下方讨论区,回复相关问题.一起完善本文章教程的书写. Python字符串常用函数. 声明字符串变量: str = ‘关注做全栈攻城狮,写代码也要读书,爱全栈,更爱生活.’ 下面所有字符串函数函数,是对变量str进行操作: 求字符串长度: 函数使用: 运行结果: 值得注意

他山之石,calling by share——python中既不是传址也不是传值

事情是这样的,Python里是传址还是传值令人疑惑,限于本人没有C基础,所以对大家的各类水平层次不一的解答难以确信. 第一个阶段: 在读<python基础教程第二版>的时候感到疑惑,然后群友解答(略敷衍),接着就是知乎上提问(感谢大家的热心回答,但我很晚才收到推送) 虽然是某天早晨睡不着,翻看公众号的时候看见一篇<不要再问 "Python 函数中,参数是传值,还是传引用?" 这种没有意义的问题了>的文章,初步释疑惑(但后来我觉得他的说法虽然形象,但是不准确) 第

python中@property的使用

python中@property的使用 @property的作用是把一个getter方法变成属性,@xxx.setter把setter方法变成属性赋值. #coding:utf-8 class Screen(object): @property def width(self): return self._width @width.setter def width(self,value): if not isinstance(value,int): raise ValueError('width