python - 为什么包装random.Random的random方法好像对RNG有影响?

标签 python python-3.x random

我试图记录对 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 wrappednot_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) 更改调用,所以让我们看看它在引擎盖下调用了什么。

  • Hererandom.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.randrangeright above ,并使用 random.Random._randbelow 生成随机数...
  • ...已实现 right below 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/

    相关文章:

    python - 如何在 Python/Django 中将字典列表转换为 JSON?

    python - 抓取网页的 "preview"- Python

    python - 排序时遇到问题(元组列表)

    python - Pandas:为具有多列的数据框实现groupby +聚合的优雅方法?

    python - 将 linux sha512 影子转换为十六进制

    Python 不可排序类型 : NoneType() > int() when finding max of a list

    python - 隐藏散点图中绘制线上方的所有点

    algorithm - 水库取样

    java - 随机选择的字符串

    android - 如何避免从 image_view 中的数组中随机化图像?