我试图记录对 random()
的调用通过在它周围放一个 wrapper 来打印东西。令人惊讶的是,我注意到我开始获得不同的随机值。我创建了一个小示例来演示该行为:
脚本main.py:
import random
def not_wrapped(seed):
""" not wrapped """
gen = random.Random()
gen.seed(seed)
# def wrappy(func):
# return lambda: func()
# gen.random = wrappy(gen.random)
gen.randint(1, 1)
return gen.getstate()
def wrapped(seed):
""" wrapped """
gen = random.Random()
gen.seed(seed)
def wrappy(func):
return lambda: func()
gen.random = wrappy(gen.random)
gen.randint(1, 1)
return gen.getstate()
for s in range(20):
print(s, not_wrapped(s) == wrapped(s))
python3.7.5 main.py
的输出(与python 3.6.9相同)0 True
1 False
2 False
3 False
4 False
5 True
6 False
7 False
8 False
9 False
10 True
11 False
12 False
13 False
14 False
15 True
16 False
17 True
18 False
19 True
所以当你看到 Random 实例的状态 gen
是不同的,这取决于我是否将 gen.random 包裹在 wrappy
中或不。如果我打电话
randint()
对于所有种子 0-19,两次测试将失败。对于 Python 2.7.17 wrapped
和 not_wrapped
函数每次都返回相同的随机值(为每个种子打印 True)。有谁知道发生了什么?
Python 3.6 在线示例:http://tpcg.io/inbKc8hK
在此在线 repl 上的 Python 3.8.2 上,问题是 不是 显示:https://repl.it/@DavidMoberg/ExemplaryTeemingDos
(此问题已交叉发布于:https://www.reddit.com/r/learnpython/comments/i597at/is_there_a_bug_in_randomrandom/)
最佳答案
这个答案是关于 Python 3.6 的。
状态仅由 gen.randint(1, 1)
更改调用,所以让我们看看它在引擎盖下调用了什么。
random.Random.randint
的实现:def randint(self, a, b):
"""Return random integer in range [a, b], including both end points.
"""
return self.randrange(a, b+1)
random.Random.randrange
是 right above ,并使用 random.Random._randbelow
生成随机数...randint
, 并检查 random
是否方法已被覆盖!看起来像
_randbelow
问题是:...
from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethodType
...
def _randbelow(self, n, int=int, maxsize=1<<BPF, type=type,
Method=_MethodType, BuiltinMethod=_BuiltinMethodType):
"Return a random int in the range [0,n). Raises ValueError if n==0."
random = self.random
getrandbits = self.getrandbits
# CHECKS IF random HAS BEEN OVERRIDDEN!
# If not, this is the default behaviour:
# Only call self.getrandbits if the original random() builtin method
# has not been overridden or if a new getrandbits() was supplied.
if type(random) is BuiltinMethod or type(getrandbits) is Method:
k = n.bit_length() # don't use (n-1) here because n can be 1
r = getrandbits(k) # 0 <= r < 2**k
while r >= n:
r = getrandbits(k)
return r
# OTHERWISE, it does something different!
# There's an overridden random() method but no new getrandbits() method,
# so we can only use random() from here.
# And then it goes on to use `random` only, no `getrandbits`!
(作为引用, getrandbits
在 C here 中实现, random
在 C here 中也实现。)所以有区别:
randint
方法最终的行为取决于是否 random
是否已被覆盖。
关于python - 为什么包装random.Random的random方法好像对RNG有影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63271699/