random.seed在生成器表达式中失效?三种实现的可复现性差异
生成器表达式与yield生成器的随机序列复现差异问题
我写了一个用random.randint生成随机整数序列的函数,设置了seed参数保证结果可复现,但遇到了奇怪的现象:返回生成器表达式时,相同seed的多次调用结果不一致;返回元组时结果一致;而用yield实现的生成器,相同seed的结果又能保持一致。我猜这和生成器的控制流机制有关,但搞不清具体原因。
1. 返回生成器表达式的代码(结果不一致)
In [2]: import random In [3]: def rand_sequence(n, seed=None): ...: if seed is not None: ...: random.seed(seed) ...: return (random.randint(0, n) for _ in range(n)) ...: In [4]: first = rand_sequence(10, seed=0) In [5]: second = rand_sequence(10, seed=0) In [6]: assert tuple(first) == tuple(second) --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Input In [6], in <cell line: 1>() ----> 1 assert tuple(first) == tuple(second) AssertionError:
2. 返回元组的代码(结果一致)
In [7]: def rand_sequence(n, seed=None): ...: if seed is not None: ...: random.seed(seed) ...: return tuple(random.randint(0, n) for _ in range(n)) ...: In [8]: first = rand_sequence(10, seed=0) In [9]: second = rand_sequence(10, seed=0) In [10]: assert first == second In [11]:
3. 使用yield实现生成器的代码(结果一致)
In [11]: def rand_sequence(n, seed=None): ...: if seed is not None: ...: random.seed(seed) ...: ...: for _ in range(n): ...: yield random.randint(0, n) ...: ...: In [12]: first = rand_sequence(10, seed=0) In [13]: second = rand_sequence(10, seed=0) In [14]: assert tuple(first) == tuple(second)
问题本质:执行时机的差异
1. 生成器表达式版本为什么不一致?
- 调用
rand_sequence时,会立刻执行函数内的random.seed(seed),然后返回一个未执行的生成器表达式。 - 先创建
first和second时,两次调用都把全局的random状态重置为seed=0。 - 但当你把
first转成元组时,才会迭代生成器表达式,消耗10次随机数,此时random的状态已经偏离初始的seed=0。 - 接着迭代
second时,random已经不是初始状态了,生成的自然是另一组序列。
2. 元组版本为什么一致?
- 调用函数时,执行
random.seed(seed)后,立刻迭代生成器表达式并转成元组,整个序列在函数调用过程中就生成完毕。 - 每次调用函数都会重新设置
seed,生成序列时都是从初始状态开始,所以结果一致。
3. yield生成器版本为什么一致?
- 带
yield的生成器函数,调用时不会执行任何函数体代码,直接返回生成器对象。 - 只有当你迭代生成器(比如转成元组)时,才会从头执行函数体:先设置
seed=0,再生成随机数。 - 迭代
first和second时,各自都会重新执行random.seed(0),重置随机状态,所以生成的序列完全一致。
内容的提问来源于stack exchange,提问作者hamnghi
相关产品推荐
相关产品推荐

