random.choice在Python2和Python3上给出了不同的结果 背景

random.choice在Python2和Python3上给出了不同的结果 背景,python,random,mocking,Python,Random,Mocking,我想测试依赖于random模块的代码 有问题的PR是,代码在这里 问题 由于random模块生成伪随机数,我总是将random.seed(X)设置为已知值X。这适用于连续的测试运行。然而,当使用random.choice([D,C])时,Python3给出的数字似乎与Python2不同。 以下代码段: import random random.seed(1) for i in range(10): print(random.choice(['C', 'D']), end=', ')

我想测试依赖于
random
模块的代码

有问题的PR是,代码在这里

问题 由于
random
模块生成伪随机数,我总是将
random.seed(X)
设置为已知值
X
。这适用于连续的测试运行。然而,当使用
random.choice([D,C])时,Python3给出的数字似乎与Python2不同。

以下代码段:

import random
random.seed(1)

for i in range(10):
    print(random.choice(['C', 'D']), end=', ')
为Python2和Python3提供不同的结果

$ python2 test.py                                                                                                                                                     
C, D, D, C, C, C, D, D, C, C

$ python3 test.py
C, C, D, C, D, D, D, D, C, C
但是,
random.random
方法在2.x和3.x上的工作原理相同:

import random
random.seed(1)

for i in range(10):
    print(random.random())

$ python3 test.py
0.13436424411240122
0.8474337369372327
0.763774618976614
0.2550690257394217
0.49543508709194095
0.4494910647887381
0.651592972722763
0.7887233511355132
0.0938595867742349
0.02834747652200631

$ python2 test.py
0.134364244112
0.847433736937
0.763774618977
0.255069025739
0.495435087092
0.449491064789
0.651592972723
0.788723351136
0.0938595867742
0.028347476522
变通办法 我可以
mock
输出
random.choice
,这对于简单的测试用例非常有效。然而,对于相当复杂的测试用例,我无法模拟输出,因为我根本不知道它应该是什么样子

问题
调用
random.choice
方法时,我是否做错了什么?

每个版本中的
random.choice
都有完全不同的实现

Python 2.7:

def choice(self, seq):
    """Choose a random element from a non-empty sequence."""
    return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty

Python 3.4:

def choice(self, seq):
    """Choose a random element from a non-empty sequence."""
    try:
        i = self._randbelow(len(seq))
    except ValueError:
        raise IndexError('Cannot choose from an empty sequence')
    return seq[i]

该方法可以多次调用random(),也可以调用
getrandbits
,它对
\u urandom

具有不同的底层调用。根据,RNG在Python 2.4中已更改,可能会使用操作系统资源。基于这个和这个问题的另一个答案,期望Random在两个不同版本的Python、两个不同的操作系统甚至两台不同的计算机上给出相同的结果是不合理的。众所周知,Python的下一个版本可以实现一个随机函数,该函数使用系统的麦克风生成随机序列

简短版本:永远不要依赖RNG来给出确定性结果。如果您需要一个已知的序列来满足单元测试,那么您需要重新设计您的方法或单元测试

一种方法是将方法分成两部分:一部分生成随机数。第二部分消耗价值并对其采取行动。然后编写两个单元测试:一个测试生成值的覆盖率,另一个测试基于特定输入的方法输出

另一种方法可能是更改方法,不仅输出结果,还输出创建该结果的随机数。您可以修改单元测试以比较两者,并根据已知对的预期输出通过或失败测试


或者,您的单元测试可以修改为只需运行n次测试,并寻找一个确定某种随机性的排列。

我遇到了完全相同的问题,我对您在以下情况下出现错误的响应数量感到失望:,在Python、机器和操作系统的不同版本中,植入随机函数有望产生可靠一致的结果

令人痛苦的是,使用自己的random类并使用Python2.7中的逻辑重写相关方法似乎是可行的

from random import Random

class MyRandom(Random):
    def sample(self, population, k):
        (code from Python 2.7.6 random module updated for Python 3 syntax)
    
    def choice...

my_random = MyRandom(0)
my_random.sample(['Apples', 'Bananas', 'Carrots'])
随机函数本身是不同的,因此在相同的数字中具有相同的种子结果不会修复许多拒绝返回相同结果的随机函数。虽然新的随机函数是有原因的,但这些原因在已经依赖于旧函数的现有代码基础上是没有意义的


无论如何,我希望这可以帮助其他人解决这个问题。

我猜在代码片段中,对于范围内的I(10):而不是随机中的I(10):为什么您希望随机模块在Python的各个版本中产生完全相同的结果?可能是带有
random.seed()的东西
?如果您提供更多的上下文,可能会有所帮助。使用随机的算法做什么?也许有更好的解决方案。你读过吗?虽然在测试中使用随机很少是一个好主意,但文档中明确指出,
random
使用确定性Mersenne Twister实现。好吧,所以最好的解决方案可能是用已知结果模拟函数,不要依赖
random.seed
,对吗?@UloPe依赖于这是个坏主意,因为算法不能保证保持不变。由于涉及到操作系统资源,我们无法保证在两个不同的环境中有相同的顺序。@elvard如果不知道您要完成什么,就很难给出合理的建议。但是如果您需要一个非随机列表,那么是的,您需要实现自己的序列生成器。随机性应该就是这样——随机性和不可预测性。谢谢,这可能就是问题所在!