Mob*_*erg 5 python random python-3.x
我试图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))
Run Code Online (Sandbox Code Playgroud)
输出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
Run Code Online (Sandbox Code Playgroud)
所以正如你看到的 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_random/)
这个答案是关于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)
Run Code Online (Sandbox Code Playgroud)
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`!
Run Code Online (Sandbox Code Playgroud)
(供参考,这里getrandbits
是用C实现的,这里也是用C实现的。)random
所以存在差异:该randint
方法最终的行为会有所不同,具体取决于random
是否已被重写。