我试图通过在它周围放置一个包装器来打印东西来记录对random()
的调用。令人惊讶的是,我注意到我开始获得不同的随机值。我创建了一个小示例来演示该行为:
脚本 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
因此,正如您所看到的,随机实例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
就在上面,它使用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 实现,random
在这里也是用 C 实现的。
所以有区别:randint
方法最终的行为会有所不同,具体取决于random
是否被覆盖。