python yield 研究

# example 1.1

def func():
    return 1

def gen():
    yield 1

print(type( func )) # <class 'function'>
print(type( gen )) # <class 'function'>

print(type( func() )) # <class 'int'>
print(type( gen() )) # <class 'generator'>

从例 1.1 可以看到,func和gen都是函数, 但前者返回数字 1,后者返回的是个生成器(generator)。

# example 1.2

print(type( (i for i in range(10)) )) # <class 'generator'>

其实平常接触更多的是像例 1.2 这样的生成器, 由一个生成器表达式(generator expression)生成。

但两者是不同的东西,看下面这个例子。

# example 1.3

gen1 = (i for i in range(10))

def gen():
    for i in range(10):
        yield i
gen2 = gen()

print(gen1) # <generator object <genexpr> at 0x00000000>
print(gen2) # <generator object gen at 0x00000000>

例 1.3 中,两个生成器的作用完全相同,但还是有些区别,不知道怎么描述? 一个是生成器,一个是生成器表达式,但都属于生成器类型……感觉好别扭。

不管怎样,要理解 yield 语句,关键就是了解 python 的生成器。 用官网的说法12, 生成器就是一个返回迭代器(iterator)的函数。 和普通函数唯一的区别就是这个函数包含 yield 语句。

使用生成器,有两种方法。

# example 2.1

def gen():
    yield 1

# 2.1.1
g1 = gen()
for i in g1:
    print(i)

# 2.1.2
g2 = gen()
print( next(g2) )

像例 2.1 里的代码一样, 可以用for语句遍历生成器(注意是遍历生成器gen(),而不是函数gen), 也可以手动使用next函数获取生成器的值。 两者都是使用生成器的next方法来获取生成器的值的(?)。

下面再深入一点点。

# example 3.1

def gen():
    yield 1
    yield 2

generator = gen()

for value in generator:
    print(value)

虽然例 3.1 的生成器实在傻的可以,但是用来解释生成器工作还是可以的。

for 语句在碰到生成器 generator 的时候, 调用generator.next()获取生成器的返回值 (不知道底层是不是这样,总之这么理解吧)。 next()每次调用,可以理解为执行了一次generator() 执行到 yield 的时候,生成器返回了 i 的值(本例中也就是 1)并停止。 这就像普通函数碰到 return 时一样,剩下的代码都被忽略了。 不同的地方在于,python 会记录这个停止的位置。 当再次执行generator()的时候,python 从这个停止位置开始执行而不是开头, 也就是说这次返回了 2。再执行generator(),已经没有 yield 语句了, 就抛出了 StopIteration 。这和其他迭代器是类似的。

要点就一个,python 会记录停止的位置,并且下次从这个位置继续执行。 可以去回顾下例 1.3,就是循环,暂停,再循环的过程。

知道这点,然后可以再深入一点。下面可以算是生成器的高级用法了吧。

# example 4.1

""" output "WHT!" """

def gen():
    while True:
        hello_world = yield
        print(hello_world)

g = gen()

next(g)

g.send("WTF!")
g.send("what the fuck!!")

第一次看到这种代码肯定不知道发生了什么……至少我不知道。 不过我之前就提示了一点,要理解 yield,就必须了解 python 的生成器。

生成器有这么几个方法
next
不用说了,每次 for 还有 next 都是调用这个方法。
send(value)
用 value 对 yield 语句赋值,再执行接下来的代码直到下个 yield。
throw(type[, value[, traceback]])
抛出错误,类似于 raise 吧。
close()
告诉生成器,你已经死了。再调用会抛出 StopIteration。

其实还有这么几个属性没列出来:
gi_code
不知道啥用,生成器的代码?
gi_frame
不知道啥用,环境变量?
gi_running
总算知道了,查看生成器是否再运行。

知道了上面这些,就可以理解例 4.1 的代码了。 其实就是用send
方法对hello_world
进行了赋值……
python 的代码还是很清晰的,但这里还是解释下那个 next 调用和 while。 在调用send
方法前,必须先调用一次next
,让生成器执行到 yield 语句处, 才能进行赋值。外面加上 while 循环是为了避免出现send
之后, 生成器没有 yield 语句了,抛出 StopIteration 的情况。
其实我都没碰到过这么使用生成器的场景,似乎写协程时用的上,这里就先不提了。
另外,每次调用 next 还是挺麻烦的事情,这个工作可以交给修饰器。 代码见例 4.2。

from functools import wraps

def coroutine(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        f = func(*args, **kwargs)
        next(f)
        return f
    return wrapper

目前就了解到这么多,就先写到这里了。研究过协程,再看看能否继续扩展吧。

总结一下:

使用 yield 语句的函数返回的是生成器。
next获取生成器的值。
每次碰到 yield 语句会返回并记录当前位置。
可以对 yield 语句进行赋值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容