为什么 python 随机模块中的某些方法在没有类实例的情况下工作?



在过去的几个月里,我一直在学习python作为我的第一门编程语言,到目前为止,我觉得我对函数、类和方法已经有了很好的理解。然而,最近我正在查看python的随机模块,并遇到了一些我不能完全弄清楚的东西。

def randint(self, a, b):
#Return random integer in range [a, b], including both end points.
return self.randrange(a, b+1)

上面的方法来自random模块,是random()类的一部分,它用于生成a和b之间的随机数。因为它是一个方法,它需要在类实例上调用以满足对'self'参数的要求。

import random

x = random.Random()
print(x.randint(1, 6))

在上面的第二个代码块中,我使用此方法生成1到6之间的随机数。这就像我所期望的那样工作,因为变量x被制作成Random()类的实例,然后在该实例上调用randint()方法,并将1和6作为参数a和b传入。

import random
print(random.randint(1, 6))

我不太明白的是为什么这第三块代码也可以工作并达到相同的结果。在第三个示例中,没有创建Random()类的实例,因此没有任何东西可以满足所要求的"self"论点。我本以为这会给我一个错误,说需要3个参数(self, a和b),但只提供了2个(a和b)。这几乎就像这个方法现在被视为一个常规函数,不再需要在类实例上调用了。为什么会这样?我是不是漏掉了什么?

random.py的末尾,定义了一堆常规函数来调用泛型实例上的方法:

_inst = Random()
seed = _inst.seed
random = _inst.random
uniform = _inst.uniform
triangular = _inst.triangular
randint = _inst.randint
choice = _inst.choice
randrange = _inst.randrange
sample = _inst.sample
shuffle = _inst.shuffle
choices = _inst.choices
normalvariate = _inst.normalvariate
lognormvariate = _inst.lognormvariate
expovariate = _inst.expovariate
vonmisesvariate = _inst.vonmisesvariate
gammavariate = _inst.gammavariate
gauss = _inst.gauss
betavariate = _inst.betavariate
paretovariate = _inst.paretovariate
weibullvariate = _inst.weibullvariate
getstate = _inst.getstate
setstate = _inst.setstate
getrandbits = _inst.getrandbits
randbytes = _inst.randbytes

所以random.randint(...)等于random._inst.randint(...)。但是您不能这样写,因为以_开头的名称不被导出。

在第三个示例中,没有创建Random()类的实例,因此没有任何东西可以满足所需的"self"论点。

这是一个不正确的断言。Random是一个有点特殊的情况,因为模块本身在加载模块时(除了将它们暴露为实例方法之外)通过直接创建通用实例并暴露实例的方法以供使用而无需在顶级脚本中显式实例化,从而将这些方法作为准静态方法提供。

您可以在Random.py:788-793的源代码中找到这一点(以及解释这种设计选择的有用注释):

# ----------------------------------------------------------------------
# Create one instance, seeded from current time, and export its methods
# as module-level functions.  The functions share state across all uses
# (both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.

使用这些方法确实限制了您使用当前时间作为随机性的种子,但对于大多数用例来说,这通常是足够的。

最新更新