我想在我的网站上为用户生成一个大小为5的字符串(密钥),类似于BBM PIN。
该密钥将包含数字和大写英文字母:
- AU1B7
- Y56AX
- M0K7A
即使我生成数百万个字符串,我也希望能够放心地保证其唯一性。
请问最Pythonic的方法是什么?
我的最爱是
import uuid
uuid.uuid4().hex[:6].upper()
如果您正在使用Django,可以在该字段上设置唯一约束以确保其唯一性。https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique
从3.6开始,您可以使用secrets模块生成漂亮的随机字符串。 https://docs.python.org/3/library/secrets.html#module-secrets
import secrets
print(secrets.token_hex(5))
更安全且更简短的方法是使用Django的加密模块。
from django.utils.crypto import get_random_string
code = get_random_string(5)
get_random_string()
函数返回一个安全生成的随机字符串,底层使用secrets
模块。
您还可以传递allowed_chars
:
from django.utils.crypto import get_random_string
import string
code = get_random_string(5, allowed_chars=string.ascii_uppercase + string.digits)
我不确定是否存在任何简洁的加密方式,但是可以使用一个简单直接的函数来实现,假设你将所有生成的字符串保存在一个集合中:
import random
def generate(unique):
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
while True:
value = "".join(random.choice(chars) for _ in range(5))
if value not in unique:
unique.add(value)
break
unique = set()
for _ in range(10):
generate(unique)
secret
包页面上已经说明:“特别是,应该优先使用secrets而不是随机模块中默认的伪随机数生成器,因为后者是为建模和仿真而设计的,而不是为安全或加密而设计的。” - Shadiimport os
import base64
base64.b32encode(os.urandom(3))[:5].decode('utf-8')
如果你想要独一无二的结果,那么你会遇到问题。因为 36 * 36 * 36 * 36 * 36 = 60'466'176
,如果有几百万个数据,肯定会产生冲突。由于集合比字典快,我们可以使用集合...
some_set = set()
def generate():
return base64.b32encode(os.urandom(3))[:5].decode('utf-8')
def generate_unique():
string = generate()
while string in some_set:
string = generate()
some_set.add(string)
return string
但是由于唯一性通常更加重要,我建议为数字从0到36^5 - 1
中的每个数字生成一个唯一的代码,如下所示。我们可以使用大质数和模数来生成伪随机数。
import base64
import math
num = 1
prime_number = 60466181
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'
def num_to_code(n: int):
string = ''
hashed = hash_number(n)
for x in range(5):
charnumber = hashed % 36
hashed = math.floor(hashed / 36)
string += characters[charnumber]
return string
def hash_number(n: int, rounds = 20):
if rounds <= 0:
return n
hashed = (n * prime_number) % (36 ** 5)
return hash_number(hashed, rounds - 1)
if __name__ == '__main__':
code = num_to_code(1)
print(code)
0 AAAAA (easily fixable ofc)
1 ZGQR9
2 ON797
3 DUMQ6
4 31384
5 R8IP3
Primary Key
),则可以执行以下操作:
注意: 这不会生成固定长度。
我们将向右填充user_id
,以使生成的长度更为静态。
import os
import base64
user_id = 1
#pad the string
number_generate = str(user_id).rjust(5,"0")
base64.b32encode(bytes(number_generate, 'utf-8')).decode('utf-8').replace('=','')
size = 5
''.join(random.choice(string.letters[26:] + string.digits) for in range(size))
def generate(size=5):
code = ''.join(random.choice(string.letters[26:] + string.digits) for in range(size))
if check_if_duplicate(code):
return generate(size=5)
return code
string.letters
和 string.digits
是什么吗? - Dianshengstring.ascii_letters
。 - GLRoman要生成唯一的代码,您可以使用以下命令:
import uuid
str(uuid.uuid1())[:5]
Django provides the function
get_random_string()
which will satisfy the alphanumeric string generation requirement. You don't need any extra package because it's in thedjango.utils.crypto
module.
>>> from django.utils.crypto import get_random_string >>> unique_id = get_random_string(length=32) >>> unique_id u'rRXVe68NO7m3mHoBS488KdHaqQPD6Ofv'
You can also vary the set of characters with
allowed_chars
:
>>> short_genome = get_random_string(length=32, allowed_chars='ACTG') >>> short_genome u'CCCAAAAGTACGTCCGGCATTTGTCCACCCCT'
This is my standard class Model :
class ClassOne(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
systemCode = self.systemCode
if not systemCode:
systemCode = uuid.uuid4().hex[:6].upper()
while ClassOne.objects.filter(systemCode=systemCode).exclude(pk=self.pk).exists():
systemCode = uuid.uuid4().hex[:6].upper()
self.systemCode = systemCode
super(ClassOne, self).save(*args, **kwargs)
但是我所有的模型中都有相同的systemCode字段。因此,我正在使用一个函数来生成值。
因此,这就是如何使用saveSystemCode()函数为所有模型生成唯一值的方法:
import uuid
def saveSystemCode(inClass, inCode, inPK, prefix):
systemCode = inCode
if not systemCode:
systemCode = uuid.uuid4().hex[:6].upper()
while inClass.objects.filter(systemCode=systemCode).exclude(pk=inPK).exists():
systemCode = uuid.uuid4().hex[:6].upper()
return systemCode
class ClassOne(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassOne, self.systemCode, self.pk, 'one_')
super(ClassOne, self).save(*args, **kwargs)
class ClassTwo(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassTwo, self.systemCode, self.pk, 'two_')
super(ClassTwo, self).save(*args, **kwargs)
class ClassThree(models.Model):
name = models.CharField(max_length=100)
systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
....
def save(self, *args, **kwargs):
self.systemCode = saveSystemCode(ClassThree, self.systemCode, self.pk, 'three_')
super(ClassThree, self).save(*args, **kwargs)
while 循环在 'saveSystemCode' 函数中防止再次保存相同的值。
base64.b32encode('12345') == 'GEZDGNBV'
- Paulo Scardinedjango.utils.crypto.get_random_string(5, string.ascii_uppercase+string.digits)
生成它们。您可能希望限制字符集,以避免生成可能在某些字体中难以辨认的混淆字符串,例如l1I1l
。唯一性需要您持久化分配的字符串集合。 - mhawke