python中的random模块包含pseudorandom number generators(PRNGs)的两个接口(类)。您可以将其视为生成随机数的两种方式。
关于模块的注释 secrets.
模块secrets不实现任何类型的PRNG,但提供了基于 SystemRandom 和 os.urandom 的辅助函数(这很棒,因为我们不必自己编写它们) ( SystemRandom 基于)。 cmets是我的:
from random import SystemRandom
_sysrand = SystemRandom() #secrets._sysrand
randbits = _sysrand.getrandbits #secrets.randbits
choice = _sysrand.choice #secrets.choice
def randbelow(exclusive_upper_bound): #secrets.randbelow
...
return _sysrand._randbelow(exclusive_upper_bound) #uses SystemRandom
def token_bytes(nbytes=None): #secrets.token_bytes
...
return os.urandom(nbytes)
def token_hex(nbytes=None): #secrets.token_hex(uses token_bytes())
...
return binascii.hexlify(token_bytes(nbytes)).decode('ascii')
def token_urlsafe(nbytes=None): # secrets.token_urlsafe(uses token_bytes())
...
tok = token_bytes(nbytes)
return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')
Random.random() 是如何工作的?
random.random() 在第 749 行的“random.py”模块中定义(对我来说)
_inst = Random()
...
random = _inst.random
random.Random() 类本身没有定义random() 方法,但继承了_random.Random()(它确实定义了一个名为random() 的方法),这是一个名为Random() 的类,位于模块_random .
_random(它是一个内置模块)模块的C源代码可以在here找到(它实际上叫_randommodule.c。见下文解释)
(从历史上看,如果一个模块被称为垃圾邮件,那么包含它的 C 文件
实现称为 spammodule.c;如果模块名称很
long,和 spammify 一样,模块名可以只是 spammify.c。)
_random.Random.random()(或random.random())方法在_randommodule.c文件中定义为_random_Random_random_impl()。
static PyObject *
_random_Random_random_impl(RandomObject *self)
{
uint32_t a=genrand_int32(self)>>5, b=genrand_int32(self)>>6;
return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0));
}
genrand_int32() 是由Mersenne Twister PRNG 实现定义的函数,它返回一个 4 字节的数字。
SystemRandom().random() 是如何工作的?
(我知道你没有要求 SystemRandom(),但在我写这个的时候我还没有意识到)
我制作了这张图片作为我的答案的概述(但是,我鼓励您阅读全部内容)
SystemRandom().random() 在模块random.py 中定义。
...
def random(self):
"""Get the next random number in the range [0.0, 1.0)."""
return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF**strong text**
该函数使用模块os.py中定义的另一个名为urandom()的函数
from os import urandom as _urandom
os.py 模块本身没有定义函数urandom(),而是从内置模块中导入它。如果您在 POSIX OS 上,os.py 将导入 posix 内置模块,如果您在 Windows NT OS 上,则将导入 nt 内置模块。这些模块包含 urandom() 的定义。
if 'posix' in _names:
name = 'posix'
linesep = '\n'
from posix import *
或
elif 'nt' in _names:
name = 'nt'
linesep = '\r\n'
from nt import *
posix 和 nt 是内置模块,因此它们没有
__file__ 属性。
POSIX
static PyObject *
os_urandom_impl(PyObject *module, Py_ssize_t size)
{
...
bytes = PyBytes_FromStringAndSize(NULL, size);
...
result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
...
return bytes
}
-
_PyOS_URandom() 在 bootstrap_hash.c 文件中定义,然后调用 pyurandom()
int
_PyOS_URandom(void *buffer, Py_ssize_t size)
{
return pyurandom(buffer, size, 1, 1);
}
static int
pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
{
...
return dev_urandom(buffer, size, raise);
...
}
-
dev_urandom 在bootstrap_hash.c 文件中定义,然后使用/dev/urandom 目录获取随机字节。
static int
dev_urandom(char *buffer, Py_ssize_t size, int raise)
{
...
fd = _Py_open("/dev/urandom", O_RDONLY);
...
do {
n = _Py_read(fd, buffer, (size_t)size);
...
} while (0 < size);
...
}
Windows NT
可能看起来有点奇怪(我也这么认为),但posixmodule.c 文件也用于 NT 系统,这是文件开头的引用(注释)
此文件也用于 Windows NT/MS-Win。在那种情况下
模块实际上称自己为“nt”,而不是“posix”,还有一些函数
要么未实现,要么实现方式不同。来源
假定对于 Windows NT,定义了宏“MS_WINDOWS”
独立于使用的编译器。不同的编译器定义它们的
自己的功能测试宏,例如'_MSC_VER'。
对于 Windows NT,函数调用链与 POSIX 相同,直到 pyurandom() 函数
static int
pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
{
...
#ifdef MS_WINDOWS
return win32_urandom((unsigned char *)buffer, size, raise);
#else
...
}
-
win32_urandom() 在bootstrap_hash.c 文件中定义,然后调用CryptGenRandom()。
static int
win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
{
...
if (!CryptGenRandom(hCryptProv, chunk, buffer))
{
...
}
...
return 0;
}
-
CryptGenRandom() 在wincrypt.h 文件中声明并在Advapi32.lib 和Advapi32.dll 库中定义(这些文件由Microsoft 提供)