Pyhton——原生random模块的使用


整体说明

  • 随机种子(seed)是初始化随机数生成器的状态,相同种子产生完全相同的随机序列(可复现性)
    • 不设置种子时,使用系统时间(time.time())作为种子,每次运行结果不同
    • 种子可以是整数、字符串、字节等(内部会转换为整数)
  • 随机状态(state)本质是生成器的内部状态(包含所有必要信息,确保后续随机数可预测)
    • random.getstate():保存当前状态(返回一个元组)
    • random.setstate(state):恢复到之前保存的状态
  • 全局 random 模块在所有地方共享一个状态,不是线程安全的(多线程会竞争状态)
    • 建议多使用 局部 random.Random 实例而不是全局 random 模块

random 模块示例

  • 以下是一个原生的 Python random 模块使用示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    import random

    print("=" * 50)
    print("随机种子的可复现性演示")
    print("=" * 50)

    print("\n【不设置种子(全局随机)】")
    random.seed() # 重置为默认状态(系统时间种子)
    print(f"第一次随机整数序列: {[random.randint(1, 100) for _ in range(3)]}")
    print(f"第二次随机整数序列: {[random.randint(1, 100) for _ in range(3)]}")

    print("\n【设置固定种子】")
    random.seed(42) # 固定种子,注意 seed 是一个函数,不要写成 random.seed = 42,这样会导致 seed 失效
    print(f"种子42-序列: {[random.randint(1, 100) for _ in range(3)]}") # 结果为固定值,跨平台跨进程可复现

    random.seed(42) # 固定种子,重新设置相同种子,会重置随机序列到初始状态
    print(f"种子42-序列: {[random.randint(1, 100) for _ in range(3)]}") # 结果复现上面的结果

    print("\n" + "=" * 50)
    print("随机状态的保存与恢复")
    print("=" * 50)

    random.seed(100) # 先设置种子,确保初始状态一致
    print(f"\n初始状态-随机数序列: {[random.randint(1, 100) for _ in range(3)]}")

    state = random.getstate() # 保存当前随机状态(返回一个不可变对象,包含完整的随机生成器状态)
    print(f"状态改变后-随机数序列: {[random.randint(1, 100) for _ in range(3)]}") # 继续生成几个随机数

    random.setstate(state)# 恢复之前保存的状态,从当时状态开始继续除数,就像是 seed(100) 后已经输出过多个 随机数一样
    print(f"恢复状态后-随机数序列: {[random.randint(1, 100) for _ in range(3)]}") # 继续生成几个随机数,得到的结论是跟上面一致的

    print("\n" + "=" * 50)
    print("全局随机 vs 局部随机实例")
    print("=" * 50)

    # 全局 random:所有模块共享同一个状态
    print("\n【全局随机(共享状态)】")
    random.seed(200)
    print(f"全局-随机数1: {random.uniform(0, 10)}")
    print(f"全局-随机数2: {random.uniform(0, 10)}")

    # 局部 Random 实例:独立状态,不影响全局
    local_rand1 = random.Random(300) # 局部实例1,种子300
    local_rand2 = random.Random(300) # 局部实例2,种子300
    local_rand3 = random.Random(400) # 局部实例2,种子400

    print("\n【局部随机实例(独立状态)】")
    print(f"局部1-随机数(seed=300): {local_rand1.randint(1, 10)}")
    print(f"局部2-随机数(seed=300): {local_rand2.randint(1, 10)}")
    print(f"局部3-随机数(seed=400): {local_rand2.randint(1, 10)}")

    # ==================================================
    # 随机种子的可复现性演示
    # ==================================================
    #
    # 【不设置种子(全局随机)】
    # 第一次随机整数序列: [100, 29, 44]
    # 第二次随机整数序列: [16, 34, 5]
    #
    # 【设置固定种子】
    # 种子42-序列: [82, 15, 4]
    # 种子42-序列: [82, 15, 4]
    #
    # ==================================================
    # 随机状态的保存与恢复
    # ==================================================
    #
    # 初始状态-随机数序列: [19, 59, 59]
    # 状态改变后-随机数序列: [99, 23, 91]
    # 恢复状态后-随机数序列: [99, 23, 91]
    #
    # ==================================================
    # 全局随机 vs 局部随机实例
    # ==================================================
    #
    # 【全局随机(共享状态)】
    # 全局-随机数1: 0.4560930208539393
    # 全局-随机数2: 2.0344697486239927
    #
    # 【局部随机实例(独立状态)】
    # 局部1-随机数(seed=300): 10
    # 局部2-随机数(seed=300): 10
    # 局部3-随机数(seed=400): 6

附录:特别说明

  • random.seed 是一个函数而不是一个数字或字符串,需要使用 random.seed(1) 而不是 random.seed=1 来设置种子
    • 这里容易理解错误
  • 所有设置了固定种子的部分(如 seed=42local_rand1 = random.Random(300)),每次运行结果完全一致
  • 全局随机在未设置种子时,每次运行结果不同;但恢复状态后,会延续之前的序列
  • 局部实例的操作不会影响全局或其他局部实例的状态,实现了完全隔离
  • random.shuffle(input) 是个 in-place 修改方法,会直接修改 input 对象的值

附录:random 隔离实用技巧

  • 临时固定状态:保存原始状态 -> 设置临时种子 -> 执行操作 -> 恢复原始状态(避免污染全局)
  • 独立随机流:为不同任务/模块分配独立种子(比如 task_seed = base_seed + task_id),确保并行任务结果可复现
  • 局部状态持久化:保存局部实例的状态(如 local_rand.getstate()),后续可在其他地方恢复继续使用
  • 禁止全局污染:封装局部实例,强制代码使用独立随机生成器(避免误改全局状态)