Keupayaan untuk menjana nilai rawak adalah kemahiran pengaturcaraan yang penting. Walaupun anda tidak memerlukannya setiap hari, nilai rawak masih muncul di beberapa tempat yang mengejutkan. Sudah tentu, bagi kebanyakan orang, apabila mereka memikirkan nilai rawak, perkara pertama yang terlintas di fikiran ialah kes penggunaan berkaitan permainan seperti kocok kad, craps dan mesin slot.
Walau bagaimanapun, terdapat banyak kegunaan lain untuk nilai rawak. Ini termasuk:
Jana kata laluan selamat atau URL tetapan semula kata laluan.
Buat data sampel untuk kes ujian atau demonstrasi kod.
Bersihkan data maklumat pengenalan peribadi (PII) untuk menyediakan data untuk analisis.
Seperti banyak tugas pengaturcaraan penting lain, bahasa pengaturcaraan Python mempunyai sokongan yang direka bentuk dengan baik dan konsisten untuk menjana nilai rawak sebagai sebahagian daripada perpustakaan standard Python. Beberapa modul berkaitan termasuk random
modul, secrets
modul dan uuid
modul. Modul secrets
menyediakan nombor rawak yang kuat secara kriptografi, manakala modul random
menyediakan nombor yang kurang selamat, yang boleh digunakan untuk permainan, data ujian, simulasi, dsb. Perbezaan antara kedua-dua jenis nilai rawak ini dibincangkan di bawah.
Perbezaan Utama antara Nombor Rawak "Benar" dan Nombor Rawak Pseudo: Penjana nombor rawak pseudo menggunakan algoritma untuk menjana urutan nombor yang kelihatan rawak tetapi menentukan . Malah, mereka sangat pasti sehingga diberikan nilai benih yang sama (atau nilai permulaan), mereka pasti akan menjana urutan nombor yang sama! Penjana nombor rawak, sebaliknya, bergantung pada proses fizikal untuk mencipta nombor yang benar-benar rawak.
Walaupun penjana nombor rawak pseudo sesuai untuk banyak tujuan, ia tidak sesuai untuk aplikasi yang memerlukan rawak sebenar, seperti kriptografi. Ini kerana seseorang yang mengetahui algoritma boleh meramalkan nombor seterusnya dalam jujukan. Oleh itu, penjana nombor rawak benar adalah penting untuk aplikasi kritikal keselamatan.
Seperti yang kita bincangkan dalam bahagian sebelumnya, modul random
bagus jika kita menulis data permainan atau simulasi. Walau bagaimanapun, jika kami berurusan dengan token kebenaran atau data selamat yang lain, kami memerlukan modul secrets
.
Terdapat perbezaan lain antara kedua-dua modul ini. Modul rawak adalah yang lebih lama daripada dua modul dan telah wujud sejak versi Python 1. Ia mempunyai antara muka berfungsi yang besar yang pada asasnya merupakan pembalut di sekitar contoh kongsi kelas random.Random
. Walau bagaimanapun, anda sentiasa boleh membina kelas rawak anda sendiri dan menggunakannya. random.Random
Kelas Rawak boleh dijadikan instantiat dengan benih yang diketahui untuk memberikan urutan nombor rawak yang boleh dihasilkan semula. Jika tiada, nilai benih berdasarkan pemasa sistem akan digunakan.
Contoh ini menunjukkan kesan memilih benih pada kelas random.Random
:
"""Creating random integers demo""" from random import Random, randint seed = 42 seeded_1 = Random(seed) seeded_2 = Random(seed) randomly_seeded = Random() # Get a random number between 1 and 1000, inclusive print(seeded_1.randint(1, 1000)) print(seeded_2.randint(1, 1000)) print(randomly_seeded.randint(1, 1000)) # Use the functional interface print(randint(1,1000))
655 655 161 956
Dua kejadian kelas rawak yang diwujudkan dengan benih yang sama menghasilkan dua baris input pertama. Setiap kali saya menjalankan program, baris ini mencetak 655. (Cubalah!).
Dua baris terakhir dibuat menggunakan contoh benih rawak dan antara muka fungsi (juga benih rawak). Kod berubah dengan setiap larian, dan dalam kes dua baris ini, sebarang larian susunan berangka adalah bertepatan sepenuhnya.
Pada pandangan pertama, modul rahsia, pertama kali diterangkan dalam PEP 506 dan pertama kali muncul dalam Python 3.6, kelihatan sangat berbeza daripada modul rawak Python. Banyak pembalut antara muka berfungsi hilang, jadi, sebagai contoh, anda tidak boleh mengimport randint
secara langsung. Selain itu, terdapat kelas secrets.SystemRandom
yang akan mengabaikan sebarang nilai benih yang mungkin anda berikan kepadanya.
Walau bagaimanapun, jika kita melihat di bawah perbezaan cetek ini, kedua-dua kelas adalah sangat serupa, sebenarnya, melihat kod sumber:
secrets.SystemRandom
Kelas sebenarnya alias untuk kelas random.SystemRandom
.
random.SystemRandom
ialah subkelas random.Random
. Oleh itu, secara amnya, kefungsian yang terdapat dalam satu sistem juga tersedia dalam sistem yang lain. (Dua pengecualian ialah getstate
dan setstate
, yang tidak dilaksanakan dalam SystemRandom
). Perbezaan paling ketara di dalam
ialah gelagat "rawak" teras dalam SystemRandom dilaksanakan berdasarkan os.urandom
. Seterusnya, fungsi urandom
diluluskan integer dan mengembalikan sejumlah bait rawak menggunakan penjana nilai rawak kuat kriptografi yang bergantung pada platform.
我们现在知道我们可以使用这两个类中的任何一个,它们基本上是可互换的,但secrets.SystemRandom
将更真实地随机,因此在更安全的环境中使用。
考虑到这一点,接下来让我们看一些用例。
我们已经了解了如何使用randint
在特定范围内生成随机数。randrange
函数非常相似,只是它不包括上限,而randint
的上限是包含的。如果你足够频繁地运行这段代码,第一行输出会出现5,但第二行不会。
from secrets import SystemRandom rand = SystemRandom() # Integers betewen 1 and 5, inclusive print(rand.randint(1,5)) # Integers between 1 and 5, but not including 5 print(rand.randrange(1,5))
Random和SystemRandom共享了几种方法,允许你根据各种分布生成随机值。这些包括均匀分布(获得两个端点之间的浮点值,类似于randint
提供的)、高斯(正态)分布等。
例如,我们可以创建一个包含20个虚拟IQ值的列表,这些值沿着与真实人群相同的正态曲线随机分布。根据定义,智商的平均值为100,标准差为15。(顺便说一句,为了本示例的目的,我们希望对这种分布建模,即使我们忽略了对这个想法和我们如何测试它提出的合理批评。)
以下是随机创建 20 个 IQ 的“群体”的代码:
"""IQ distribution""" from secrets import SystemRandom rand = SystemRandom() population = [round(rand.gauss(100, 15)) for _ in range(0,20)] print(population)
当然,输出会有所不同。这是一个有代表性的运行:
[102, 90, 88, 82, 102, 93, 127, 121, 94, 107, 103, 80, 106, 106, 84, 107, 108, 88, 123, 121]
在其他语言中,从列表或其他序列中进行选择通常需要两个步骤。首先,你得到一个从0到列表上界(长度减1)的随机数。然后将该索引应用于列表以选择元素。在Python中,choice
和chchoices
ices这两种方法使你能够同时执行这两个步骤。这使得从任何类型的序列中选择所需大小的随机样本非常容易。
例如,给定上面的代码,假设我们想要获取IQ的总体,并从中选择一个或多个值。下面是我们可以快速完成的方法:
# Select one IQ at random print(rand.choice(population)) # Select four IQs at random print(rand.choices(population, k=4))
输出(示例):
102 [107,102,88,103]
因为在Python中使用随机方法很容易从序列中选择随机选项。通过Random.choice
或者Random.choices
函数,在Python中创建随机字符串也很简单。此外,secrets模块定义了一些特殊的函数,根据你的需要,也可以使用这些函数。
让我们首先看看一种通用方法,你可以使用它生成多种类型的字符串。字符串模块包括几个基本上是硬编码字符序列的字符串,例如ascii_lowercase
(a-z)、ascii_uppercase
(A-Z)、ascii_letters
、punctuation
和digits
。Random.choices
或者SystemRandom.choices
可以调用其中任何一个来创建所需长度的数组,然后可以使用str类的join方法将数组转换为新字符串。
我们在以下示例中结合了这些步骤:
from string import ascii_letters, digits, punctuation, ascii_lowercase, ascii_uppercase from secrets import SystemRandom rand = SystemRandom() four_digits = "".join(rand.choices(digits, k=4)) ten_mixed_case = "".join(rand.choices(ascii_letters, k=10)) assorted = ascii_letters + punctuation twenty_assorted = "".join(rand.choices(assorted, k=20)) print(four_digits) print(ten_mixed_case) print(twenty_assorted)
代码输出:
8782 PLZYOxFLoQ !mNsKsF;([I#F(c<jcg><h3 id="h20" data-id="h590b8bf-SL4CIZsY">使用Secrets模块加密随机字符串</h3> <p data-id="p838747a-IPEgWEdV">除了如上所示轻松创建随机字符串外,secrets模块还提供了几个函数,可用于生成各种格式的随机字节序列。在最低级别,我们可以使用<code>token_bytes</code>函数生成各种长度的原始“字节”数组。</p> <pre class="brush:php;toolbar:false">from secrets import token_bytes b = token_bytes(10) print(type(b)) print(b)
代码输出:
<class> b'!\x05P\xc6a\x87\xf9~(\xa9'</class>
原始字节作为加密算法或类似算法的输入可能很有用,但请记住,它们不会包含有效的UTF-8代码点,因此不应使用此函数生成字符串。要获取字符串,可以使用上一节中的技术或下面两个函数中的一个。
我们可以返回一个字符串,它不是以原始格式获取字节,而是以十六进制格式再次由随机字节组成。这为每个字节提供了两个十六进制输出字符:
from secrets import token_hex token = token_hex(10) print(f"Returned a {type(token)} of length: {len(token)}:") print(token)
代码输出:
Returned a <class> of length: 20:</class>
同一系列中的第三个功能——在某些方面可能是最有用的——是token_urlsafe
。此函数允许我们将随机字节字符串转换为稍微修改的base64编码字符串。在这里,每个字节平均产生1.3个字符,结果可以安全地用作URL-例如,表示缩短的URL或用作密码重置令牌。另一个好处是字符串来自比16位token_hex
更大的潜在随机字符集。
from secrets import token_urlsafe token = token_urlsafe(15) print(token)
代码输出:
gfN2nGjO7izMPyXs5tvU
虽然我们在本文中的重点是随机值,但我们现在想花一些时间讨论对于所有实际目的来说都是随机和唯一的值。解决这个问题的一种非常普遍的方法是通用唯一标识符(UUID)的概念。UUID是一个128位的数字,不能100%保证是唯一的,但在统计上很可能是唯一的,以至于发生冲突的机会非常小。
除了大数字之外,UUID还共享一种通用的表示格式。128位的数字可以表示为32个十六进制数字,而UUID添加四个连字符以形成一个36个字符的字符串,以8-4-4-6-12的模式排列。例如:
'967909e3-7231-4040-aae4-8b6b2fb96a0b'
Python模块uuid有几个不同的函数,对应于许多公认的算法,用于创建此类标识符,但建议使用两种最常见的类型之一,uuid1和uuid4。
uuid1值是通过将网络节点id(通常意味着网卡的mac地址)与有关UUID版本和变体的少量信息以及表示高分辨率时间戳的许多位相结合来创建的。
相反,uuid4值通常包含用于存储版本和变体信息的6位,以及122位纯随机数据。因此,根据维基百科,尽管原则上可能存在两个uuid4值的冲突,但在实践中,“在103万亿个版本4 UUIDs中找到重复的概率是十亿分之一。”。
你可以使用Python uuid模块轻松创建uuid1和uuid4值。正如我们将看到的,字符串表示看起来是相同的,尽管描述uuid1字段中的位更有意义。
from uuid import uuid1, uuid4 print(uuid1()) print(uuid4())
代码输出:
bfc89f3e-e6ab-11ec-abfc-4a9b744d17b8 025586c2-50ed-41a6-ae31-bf96b9d79df2
与本文中的大多数内容一样,当我们说“示例输出”时,实际上只是他代码运行其中一次的结果。当然,至少在uuid4的情况下,如果你运行这段代码103万亿次,你有十亿分之一的机会得到与我相同的结果。
在结束对UUID的讨论之前,我们在这里提到它们,因为它们是一个被广泛接受的标准,但正如我们所看到的,uuid4的实现与系统模块中的许多实用函数之间有很多重叠。例如,我经常看到uuid4
函数用于在数据库中生成主键,特别是在NoSQL上下文中,其中可能不支持自动递增字段。原则上,人们也可以使用secrets.token_hex
用于同样的任务,但uuid4可能会使代码的意图更加清晰。
附上产生随机数分布的图以及代码实现:
import random import matplotlib.pyplot as plt x = [random.randint(1, 100) for n in range(100)] y = [random.randint(1, 100) for n in range(100)] plt.figure(figsize=(8,6), dpi=80) plt.scatter(x, y) plt.show()
结果:
Atas ialah kandungan terperinci Bagaimana untuk melaksanakan nilai rawak dalam Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!