Python基础专题 - 超级详细的 Random(随机)原理解析与编程实践

简介: Python基础专题 - 超级详细的 Random(随机)原理解析与编程实践

Python基础专题 - Random(随机)详解


1、什么是随机

1.1 随机可能是造物者的杰作

随机其实并不是一个像其表面那样容易理解的问题,但我们可以这样来描述随机:

对于一个取值有限的事件

  • 从上帝视角看,在事件真是发生前没有任何理由认为其中任何一个事件更可能会真实发生,那么认为这个事件是随机事件;
  • 从实践视角看,不论实践主体做任意多次独立重复实验,事件中任意一个取值(样本点)实际发生的频率随着重复实验的次数的无限增多,各个取值实际发生的次数无限接近于完全相等

然而不得不说,世界纷繁复杂,取值有限的事件无非是我们对这个无限广袤世界从某一角度高度抽象和离散化的结果。

如果将人类认知规律分为必然规律和统计规律,必然规律指事物本质的规律,它毫无例外地适用于事物所有个体。而统计规律是指通过对随机现象的大量观察后所呈现出来的事物的集体性规律

这里所谓上帝指得是拥有一个假想中的超能力以至于能够在事件发生前可以实践出无限种结果的主体。人类不是上帝,不能真实拥有上帝视角,因此所谓随机正是一种通过统计认知的规律。

1.2 伪随机、随机种子与计算机编程中的随机

随机不是人为制造的,而是根生于物理世界。人们不可以通过一种算法凭空创造不确定性

所谓【伪随机】是用确定性的算法计算出的一种在[ 0 , 1 ] [0, 1][0,1]上产生均匀分布的随机数序列的算法,它并不真正的随机,它需要一个随机种子为它提供随机性的动力之源。

随机种子本质就是一个作为初始条件的真随机数,一般通过其查询随机数表后再经过一定计算获得计算机编程中的随机数。

系统是如何获得随机种子呢?

生成伪随机数需要随机种子很多时候计算机系统维护了一个熵池,不断收集非确定性的硬件事件,如机器运行环境中产生的硬件噪音,在运行时刻某一瞬间的系统时间尾数等等,以此生成随机种子。

计算机编程里生成的随机数都是伪随机数,但在一般统计意义上看我们仍然可以视为真随机数。


2、使用Python内建random伪随机数模块

2.1 随机性设定

Python中有内建模块random可以用来生成[0.0,1.0)上的精度为53位的伪随机数,该模块使用马特赛特旋转演算法(MersenneTwister)实现了各种分布的伪随机数生成器。

MersenneTwister算法中,一个n位的线性反馈移位寄存器(LFSR)能够在重复之前产生 2 n − 1 2^n-12n1 位长的伪随机序列,称之为m序列,将产生的伪随机序列除以序列的周期,就可以得到(0,1)上均匀分布的伪随机序列了。

  • random.getstate() - 返回捕获生成器当前内部状态的对象。
import random
state = random.getstate()
  • random.setstate(state) - 将生成器的内部状态恢复到state的状态,一般由getstate()先获取state。
import random
random.setstate(state)

该模块中,可以使用random.seed(a=None, version=2)方法指定a的指为一个确定数在编程时固定随机种子,这样在多次运行生成随机数的代码时,你会发现"随机"出来的结果是同一个。如果 a 被省略或为 None ,则使用当前系统时间。在上一节 (1.2节) 中对于内部机制有详细介绍,这里不在论述。

2.2 实用方法

(1)生成[0,1)上的随机浮点数
import random
random.random()
(2)生成[a,b][a,b)上一个随机浮点数(区间可以不是整数)
  • a < = b a <= ba<=b 时, a < = N < = b a <= N <= ba<=N<=b
  • b < a b < ab<a 时, b < = N < = a b <= N <= ab<=N<=a.
  • 取决于等式 a + ( b − a ) ∗ r a n d o m ( ) a + (b-a) * random()a+(ba)random() 中的浮点舍入,终点 b 可以包括或不包括在该范围内。
import random
random.uniform(2, 3)
(3)生成[a,b]上的随机整数
random.randint(a, b)
import random
random.randint(0, 9) # 生成[0,9]上的随机整数
(4)在区间[start, stop]上以间隔step生成随机整数
random.randrange(start, stop[, step])
import random
random.randrange(1, 9[, 2])
(5)按高斯分布(正态分布)生成随机数
random.gauss(mu, sigma)

mu 是平均值,sigma 是标准差

import random
random.gauss(mu, sigma)
(6)按β \betaβ分布生成[0,1]上的随机数
random.betavariate(alpha, beta)

参数的条件是 alpha > 0 和 beta > 0。 返回值的范围介于 0 和 1 之间。

import random
random.gauss(1, 3)
(7)用作选择器:从序列中随机选取一个元素
random.choice(seq)
random.choices(population, weights=None, *, cum_weights=None, k=1)
  • random.choice(seq):从非空序列 seq 返回一个随机元素。其中 seq 可以是包括列表、元组、range序列,甚至字符串在内的任意Python序列类型。如果 seq 为空,则引发 IndexError。
import random
random.choice(["我","爱","学","习","Python","编","程"])
random.choice("今天天气不错")
  • random.choices(population, weights=None, *, cum_weights=None, k=1):从population中选择替换,返回大小为 k 的元素列表。 如果 population 为空,则引发 IndexError。
  • 如果指定了 weight 序列,则根据相对权重进行选择;
  • 如果给出 cum_weights 序列,则根据累积权重(可能使用 itertools.accumulate() 计算)进行选择;
  • 如果既未指定 weight 也未指定 cum_weights ,则以相等的概率进行选择;
  • weights 或 cum_weights 可以使用任何与 random() 返回的 float 值互操作的数值类型(包括整数,浮点数和分数但不包括十进制小数)
(8)随机打乱序列元素位置(序列洗牌函数)
random.shuffle(x[, random])

将序列 x 随机打乱位置。

  • 可选参数 random 是一个0参数函数,在 [0.0, 1.0) 中返回随机浮点数;
  • 默认情况下,这是函数 random()
import random
x = ["我","爱","学","习","Python","编","程"]
random.shuffle(x)
x
  • Out[R]:[‘程’, ‘编’, ‘我’, ‘习’, ‘Python’, ‘学’, ‘爱’]
(9)无重复随机抽样
random.sample(population, k, *, counts=None)

通过无重复随机抽样返回包含来自总体的元素的新列表,同时保持原始总体不变。

  • population: 集合或者序列,为样本总体的容器;
  • k: int,抽取的子序列的长度(元素个数);
  • counts:list,元素为重复样本的权重,即该样本的出现次数。counts中的元素与population的元素一一对应。
    【代码1】
import random
population = ['王', '者','荣','耀']
random.sample(population,counts=[4, 3, 2, 1], k=3)
  • 和下面的代码时等效的:
    【代码2】
import random
population = ['王', '王', '王', '王', '者', '者', '者', '荣', '荣', '耀']
random.sample(population,counts=[4, 3, 2, 1], k=3)

这里无重复随机抽样中的无重复不是说返回的元素不能有一样的,而是指用的是五放回抽样。

3. Numpy伪随机数模块numpy.random

3.1 numpy中的随机生成器

3.1.1 [简单了解]Permuted Congruential Generator(PCG)

该算法更详细内容可以参考https://www.pcg-random.org/

Permuted Congruential Generator(PCG)即所谓置换同余生成器,是用于随机数生成的一系列简单、快速、节省空间的,统计上良好的算法。在Python编程中,生成器是一种特殊的函数。生成器与普通函数不同的是,它返回迭代器的函数,只能用于迭代操作。生成器(generator)的应用往往解决了存储空间不足的问题,无需再程序以开始就完整计算整个值。

从RandomState类到Generator类

在之前,Numpy.random模块与Python内建的random等模块类似,直接使用马特赛特旋转演算法(Mersenne Twister)伪随机数生成器的容器,并提供有RandomState类:

numpy.random.RandomState(seed=None)

由于该类不是最好的选择,其用法仅以伪代码简要介绍如下:

from numpy.random import MT19937
from numpy.random import RandomState
rs1 = RandomState(12345)          # 获取seed=12345的RandomState()实例 rs
mt19937 = MT19937()              # 旧版MT19937 BitGenerator
mt19937.state = rs.get_state()   # get_state()方法返回一个表示生成器内部状态的元组。
rs2 = RandomState(mt19937)       # 获取seed为mt19937状态的RandomState()实例 rs2 
# 随机返回数值
rs1.random()
rs2.random()
# 按照标准正态分布随机返回数值
rs1.standard_normal()
rs2.standard_normal()
# 按照指数分布随机返回数值
rs1.standard_exponential()
rs2.standard_exponential()
# 以上是控制可获取随机状态,即rs1和rs2都传入了seed参数。
# 不传参数时才能随机中获取不一样的值(见之前关于计算机随机的介绍)。
# 要使用随机性,可以参考下面的做法
rs = RandomState()
rs.random()                  # 从[0.0,1.0)上随机抽取浮点数样本
rs.rand(d0, d1, …, dn)       # 从标准正态分布中返回一个或多个样本。
rs.normal([loc, scale, size])# 从正态(高斯)分布中抽取随机样本
rs.standard_normal()         # 从标准正态(高斯)分布中抽取随机样本
rs .standard_exponential()   # 从标准指数分布中随机抽取样本
rs.shuffle(X)                # 对X进行洗牌,即随机打乱顺序。X是一个序列,如列表。
rs.beta(a, b[, size])        # 从Beta分布中随机抽取样本
binomial(n, p[, size])       # 从二项分布中随机抽取样本
chisquare(df[, size])        # 从卡方分布中抽取样本
rs.dirichlet(alpha[, size])  # 从Dirichlet分布中抽取样本
rs.f(dfnum, dfden[, size])   # 从F分布中抽取样本
rs.gamma(shape[, scale, size])                  # 从Gamma分布中抽取样本
rs.geometric(p[, size])                         # 从几何分布中抽取样本
rs.gumbel([loc, scale, size])                   # 从Gumbel分布中抽取样本
rs.hypergeometric(ngood, nbad, nsample[, size]) # 从超几何分布中抽取样本

然而根据最新的Numpy 1.20.1文档,numpy.random.Generator(bit_generator)(以下简称Generator类)是上述numpy.random.RandomState(seed=None)(以下简称RandomState类)的替代品。

numpy.random.Generator(bit_generator)
  • Generator类依赖于附加的BitGenerator来管理状态并生成随机位,然后将这些随机位从有用的分布转换为随机值。所使用的默认BitGenerator Generator为PCG64。可以通过将实例化的BitGenerator传递给来更改BitGenerator Generator。numpy.random.default_rng()方法能够使用默认的BitGenerator(PCG64)构造一个新的Generator,用法示例如:
from numpy.random import default_rng
rng = default_rng(12345)     # 构造一个随机数生成器类rng
rng.random()                 # 从[0.0,1.0)上随机抽取浮点数样本
  • Out[]:0.22733602246716966
    或者你不必指定随机种子,以让每次运行时随机获取一个不同的值:
from numpy.random import default_rng
rng = default_rng()          # 构造一个随机数生成器类rng
rng.random()                 # 从[0.0,1.0)上随机抽取浮点数样本
  • Out[R]:0.642785685979066
  • 新的numpy.random.BitGenerator(seed=None) 类 是Numpy中通用BitGenerator的基类,所使用的默认BitGenerator Generator为PCG 64。PCG-64是 O’Neill 置换同余生成器 的128位实现。PCG64状态向量由2个无符号128位值组成,这些值在外部表示为Python ints
  • 可以用numpy.random.PCG64(seed=None)类生成一个新的BitGenerator,用法大致如下:
from numpy.random import (
    Generator, 
    PCG64, 
    SeedSequence
)
sg = SeedSequence(1234)                                  # 获取熵值(熵在信息理论中反应不确定度,实际中有系统收集,见上文)
rg = [Generator(PCG64(s)) for s in sg.spawn(10)]         # 使用这些值生成新的BitGenerator

3.1.2 [重要]Generator(bit_generator)类的进一步讲解

前一节已经讲了,default_rng()函数是推荐使用的Generator生成器,并示例了生成一个随机数的写法。本节中将介绍更多常用的方法。

以下是展示用法的伪代码

from numpy.random import default_rng
rng = default_rng()          # 构造一个随机数生成器类rng
rng.integers(low[, high, size, dtype, endpoint])        # 从返回随机整数low(含)到high(不含),
                                                        # 或者如果endpoint=True,low(含)到 high(含)
rng.random([size, dtype, out])                          # 返回[0.0,1.0)上的一个随机浮点随
rng.choice(a[, size, replace, p, axis, shuffle])        # 随机选择样本
rng.bytes(length)                                       # 返回随机字节,length为字节长度
rng.shuffle(x[, axis])              # 序列洗牌(随机打乱顺序)
rng.permutation(x[, axis])          # 随机排列序列,或返回排列范围
rng.permuted(x[, axis, out])        # 随机置换X沿轴轴线
rng.beta(a, b[, size])              # 从Beta分布中抽取样本
rng.binomial(n, p[, size])          # 从二项分布中抽取样本
rng.chisquare(df[, size])           # 从卡方分布中抽取样本
rng.dirichlet(alpha[, size])        # 从Dirichlet分布中抽取样本
rng.exponential([scale, size])      # 从指数分布中抽取样本
rng.f(dfnum, dfden[, size])         # 从F分布中抽取样本
rng.gamma(shape[, scale, size])     # 从Gamma分布中抽取样本
rng.geometric(p[, size])            # 从几何分布中抽取样本
rng.gumbel([loc, scale, size])      # 从Gumbel分布中抽取样本
rng.hypergeometric(ngood, nbad, nsample[, size])        # 从超几何分布中抽取样本
rng.laplace([loc, scale, size])           # 从具有指定位置(或均值)和比例(衰减)的
                                          # 拉普拉斯或双指数分布中抽取样本
rng.logistic([loc, scale, size])          # 从逻辑分布中抽取样本
rng.lognormal([mean, sigma, size])        # 从对数正态分布中抽取样本
rng.logseries(p[, size])                  # 从对数级数分布中抽取样本
rng.multinomial(n, pvals[, size])         # 从多项分布中抽取样本
rng.multivariate_hypergeometric(colors, nsample)        # 从多元超几何分布生成变量
rng.multivariate_normal(mean, cov[, size, …])           # 从多元正态分布中抽取随机样本
rng.negative_binomial(n, p[, size])                     # 从负二项分布中抽取样本
rng.noncentral_chisquare(df, nonc[, size])              # 从非中心卡方分布中抽取样本
rng.noncentral_f(dfnum, dfden, nonc[, size])            # 从非中心F分布中抽取样本
rng.normal([loc, scale, size])      # 从正态(高斯)分布中抽取随机样本
rng.pareto(a[, size])               # 从具有指定形状的Pareto II或Lomax分布中抽取样本
rng.poisson([lam, size])            # 从泊松分布中抽取样本
rng.power(a[, size])                # 从具有正指数a-1的功率分布中以[0,1]抽取样本。
rng.rayleigh([scale, size])         # 从瑞利(Rayleigh)分布中抽取样本
rng.standard_cauchy([size])         # 从mode = 0的标准柯西分布中抽取样本。
rng.standard_exponential([size, dtype, method, out])   # 从标准指数分布中抽取样本
rng.standard_gamma(shape[, size, dtype, out])          # 从标准Gamma分布中抽取样本
rng.standard_normal([size, dtype, out])                # 从标准正态分布(均值= 0,标准差 = 1)中抽取样本
rng.standard_t(df[, size])                   # 从自由度为df的标准学生氏分布(t分布)中抽取样本
rng.triangular(left, mode, right[, size])    # 从间隔上的三角形分布中抽取样本。[left, right]
rng.uniform([low, high, size])         # 从均匀分布中抽取样本
rng.vonmises(mu, kappa[, size])        # 从冯·米塞斯(von Mises)分布中抽取样本
rng.wald(mean, scale[, size])          # 从Wald或高斯逆分布中抽取样本
rng.weibull(a[, size])          # 从威布尔分布中抽取样本
rng.zipf(a[, size])             # 从Zipf分布中抽取样本

以下精选了上面展示的部分方法进行示例

  • 【注意】后面代码公用此段,不再重复书写:
import numpy as np
from numpy.random import default_rng
rng = default_rng()

随机选择器

a = np.array([1,5,7,9,8,7,3,1,4,6])
rng.choice(a)

随机洗牌

array = np.arange(10)
rng.shuffle(array)
array

Out[R]:array([9, 8, 0, 3, 2, 1, 6, 7, 4, 5])

  • 也可以不是数组而仅仅是一般的Python序列:
sequence = ["你","的","头","发","还","好","吗"]
rng.shuffle(sequence)
sequence
  • Out[R]:[‘你’, ‘发’, ‘的’, ‘好’, ‘头’, ‘还’, ‘吗’]

从二项分布(即伯努利分布)中抽取样本

n, p = 10, .5                # 试验次数,每次试验的概率
rng.binomial(n, p, 100) # 结果抛硬币10次,测试100次。

Out[R]:

array([5, 4, 5, 4, 4, 4, 4, 2, 6, 6, 6, 5, 7, 6, 7, 4, 6, 7, 7, 6, 9, 7,
       6, 6, 5, 6, 6, 5, 5, 5, 2, 3, 6, 3, 6, 5, 7, 4, 5, 5, 5, 8, 2, 6,
       6, 3, 4, 4, 9, 6, 7, 3, 8, 8, 7, 4, 4, 2, 5, 4, 3, 5, 5, 3, 6, 5,
       2, 4, 7, 3, 6, 5, 7, 6, 5, 6, 5, 5, 3, 5, 5, 1, 3, 5, 3, 6, 4, 5,
       6, 6, 4, 5, 5, 6, 2, 3, 7, 6, 5, 6], dtype=int64)

从高斯分布(正态分布)中抽取样本

mu, sigma = 0, 0.1         # 均值和标准差
rng.normal(mu, sigma, 10)  # 随机挑选10个样本

Out[R]:

array([-0.08977574, -0.04510079, -0.1347215 ,  0.055061  , -0.07062946,
        0.06331096,  0.11478359,  0.07054353,  0.29988904, -0.07591274])

从泊松分布中抽取样本

rng.poisson(5, 30)

Out[R]:

array([10,  5,  6,  0,  5,  8,  5,  8,  6,  3, 10,  6,  8,  4,  9,  4,  4,
        4,  1,  4,  6,  4, 11,  4,  4,  2,  2,  4,  4,  4], dtype=int64)

从F分布中抽取样本

dfnum = 1.  # 组间的自由度
dfden = 48. # 组内的自由度
rng.f(dfnum, dfden, 100)

Out[R]:

array([1.93197989e-01, 1.24205423e+00, 4.00232880e-01, 4.85092358e-02,
       3.32931687e+00, 2.05889475e+00, 4.61348458e-01, 3.00949845e+00,
       3.90822633e+00, 2.23728556e+00, 9.34600481e-03, 5.15363963e-01,
       1.43485077e-01, 4.41028093e-01, 1.32146240e-01, 1.11471393e+00,
       1.03337187e+00, 2.30848196e-01, 4.07377166e-01, 2.05472096e-01,
       1.63826036e+00, 4.46181253e-01, 1.86498211e+00, 2.66618563e+00,
       1.08118560e+00, 4.48247556e-01, 9.43349767e-02, 1.76855083e+00,
       2.95544929e-03, 6.73061275e-01, 2.15134324e-01, 1.39935387e-01,
       1.01244134e+00, 5.53635434e-03, 8.99606895e-01, 2.66732309e-01,
       1.18154518e+00, 1.12718292e+00, 2.10690001e-01, 5.68640747e-01,
       9.89775122e-03, 4.65351259e-02, 2.16960435e+00, 1.22627946e+00,
       1.15816353e+00, 2.03245748e+00, 8.48710506e+00, 2.08246000e+00,
       1.93013549e+00, 1.09309756e-01, 8.01513824e-01, 1.65082925e-01,
       3.19968754e-01, 1.31481564e-02, 2.47961229e-01, 4.73627037e-03,
       2.81335155e-02, 9.91148164e-02, 4.86448293e-01, 4.40648125e+00,
       9.06591744e+00, 1.27533676e+00, 4.73046307e-02, 1.87428590e-01,
       4.35645568e-01, 3.03655416e-01, 1.33503179e+00, 4.69885038e-01,
       4.82723958e-01, 8.95961791e-01, 1.65418312e+00, 6.51444154e-04,
       1.15344115e-01, 1.39118341e-01, 1.51368685e-01, 5.23925653e+00,
       2.70342516e+00, 1.36388925e-01, 7.42148964e+00, 4.99751983e-02,
       7.12414489e-01, 1.04688667e+00, 4.33790508e-04, 7.12767650e-02,
       3.57984128e-01, 3.16070339e-02, 3.07002039e-01, 6.77355120e-03,
       1.09919251e-03, 4.41067472e-02, 1.82543358e+00, 2.52812298e-01,
       2.00608200e+00, 3.32439576e+00, 3.43718583e-01, 1.57335325e-01,
       6.16422855e-01, 4.06029886e-01, 1.51402319e-02, 1.17152107e+00])

从卡方分布中抽取样本

rng.chisquare(2,10)

Out[R]:

array([6.46906743, 1.74516249, 1.61624403, 1.95992536, 1.14905782,
       4.13322769, 0.41470648, 0.8892556 , 1.22484048, 3.3340893 ])

附录:常用数学分布介绍


如果喜欢,记得点赞哦!

目录
相关文章
|
21小时前
|
缓存 JavaScript 前端开发
|
1天前
|
SQL 分布式计算 资源调度
一文解析 ODPS SQL 任务优化方法原理
本文重点尝试从ODPS SQL的逻辑执行计划和Logview中的执行计划出发,分析日常数据研发过程中各种优化方法背后的原理,覆盖了部分调优方法的分析,从知道怎么优化,到为什么这样优化,以及还能怎样优化。
|
1天前
|
Java
并发编程之线程池的底层原理的详细解析
并发编程之线程池的底层原理的详细解析
9 0
|
1天前
|
JSON Java Maven
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
5 0
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
|
1天前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
4天前
|
机器学习/深度学习 搜索推荐 Python
Python特征工程面试:从理论到实践
【4月更文挑战第17天】本文探讨了Python在数据科学面试中的特征工程,涵盖基础概念如特征选择和提取,实战技能如缺失值和异常值处理,以及特定场景应用。强调避免过度依赖单一方法,忽视数据分布和相关性,以及保持特征工程的可解释性。提供代码示例展示了处理缺失值、标准化、特征选择和异常值检测的基本操作。建议结合业务理解,灵活运用多种方法并注重模型解释性。
19 9
|
6天前
|
机器学习/深度学习 数据采集 算法
scikit-learn入门指南:从基础到实践
【4月更文挑战第17天】这篇指南介绍了scikit-learn,一个Python数据分析和机器学习的重要库。内容涵盖安装、数据加载与预处理、模型训练(如KNN分类器)、评估、调参优化及高级应用,如降维和聚类。通过实例展示了scikit-learn在分类任务中的使用,强调其在数据科学中的重要性。要深入了解,可参考官方文档和实践案例。
|
7天前
|
Java API 数据库
深入解析:使用JPA进行Java对象关系映射的实践与应用
【4月更文挑战第17天】Java Persistence API (JPA) 是Java EE中的ORM规范,简化数据库操作,让开发者以面向对象方式处理数据,提高效率和代码可读性。它定义了Java对象与数据库表的映射,通过@Entity等注解标记实体类,如User类映射到users表。JPA提供持久化上下文和EntityManager,管理对象生命周期,支持Criteria API和JPQL进行数据库查询。同时,JPA包含事务管理功能,保证数据一致性。使用JPA能降低开发复杂性,但需根据项目需求灵活应用,结合框架如Spring Data JPA,进一步提升开发便捷性。
|
1月前
|
缓存 分布式计算 自然语言处理
Python语言的函数编程模块
Python语言的函数编程模块
|
2月前
|
安全 调度 Python
什么是Python中的事件驱动编程?如何使用`asyncio`模块实现异步事件处理?
【2月更文挑战第4天】【2月更文挑战第9篇】什么是Python中的事件驱动编程?如何使用`asyncio`模块实现异步事件处理?

推荐镜像

更多