我想在python中生成一些字母数字密码。一些可能的方法是:
import string
from random import sample, choice
chars = string.letters + string.digits
length = 8
''.join(sample(chars,length)) # way 1
''.join([choice(chars) for i in range(length)]) # way 2
但我不喜欢两者,因为:
i
变量未使用,我找不到如何避免这种情况的好方法那么,还有其他好的选择吗?
P.S。所以我们在这里进行了一些使用timeit
进行100000次迭代的测试:
''.join(sample(chars,length)) # way 1; 2.5 seconds
''.join([choice(chars) for i in range(length)]) # way 2; 1.8 seconds (optimizer helps?)
''.join(choice(chars) for _ in range(length)) # way 3; 1.8 seconds
''.join(choice(chars) for _ in xrange(length)) # way 4; 1.73 seconds
''.join(map(lambda x: random.choice(chars), range(length))) # way 5; 2.27 seconds
因此,获胜者是''.join(choice(chars) for _ in xrange(length))
。
答案 0 :(得分:79)
您应该使用secrets module生成加密安全密码,从Python 3.6开始提供。改编自文档:
import secrets
import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(20)) # for a 20-character password
答案 1 :(得分:32)
对于那里的加密PRNG人员:
def generate_temp_password(length):
if not isinstance(length, int) or length < 8:
raise ValueError("temp password must have positive length")
chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
from os import urandom
return "".join(chars[ord(c) % len(chars)] for c in urandom(length))
注意,对于均匀分布,chars
字符串长度应该是128的整数除数;否则,你需要一种不同的方式从空间中统一选择。
答案 2 :(得分:13)
警告由于严重的安全问题,应忽略此答案!
选项#2似乎很合理,除了你可以添加一些改进:
''.join(choice(chars) for _ in range(length)) # in py2k use xrange
_
是一个传统的“我不在乎那里有什么”变量。而且你不需要列表理解,生成器表达式适用于str.join
。如果它是唯一正确的方式,那么“慢”意味着什么也不清楚。
答案 3 :(得分:5)
我认为这样做会有所帮助。 random.SystemRandom
使用与os.urandom
相同的基础加密随机函数,但它使用熟悉的random
接口。这个函数不会像Ben的答案那样受到奇怪的128字节的影响。
import random
import string
def gen_random_string(char_set, length):
if not hasattr(gen_random_string, "rng"):
gen_random_string.rng = random.SystemRandom() # Create a static variable
return ''.join([ gen_random_string.rng.choice(char_set) for _ in xrange(length) ])
password_charset = string.ascii_letters + string.digits
gen_random_string(password_charset, 32)
答案 4 :(得分:5)
使用内置secrets(Python 3.6及更高版本)的两个食谱
这比接受的答案快了许多。 (请参见下面的时间)
import secrets
password = secrets.token_urlsafe(32)
示例输出:
4EPn9Z7RE3l6jtCxEy7CPhia2EnYDEkE6N1O3-WnntU
token_urlsafe
的参数是字节数。平均一个字节为1.3个字符(base64编码)。
这是秘密文档的修改版本。这样,您就可以更加精细地控制如何生成密码。当然,如果您需要生成很多密码,这不是 fast 选项。
alphabet
中。在此示例中,仅添加了-
和_
。import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
while True:
password = ''.join(secrets.choice(alphabet) for i in range(20))
if (sum(c.islower() for c in password) >=4
and sum(c.isupper() for c in password) >=4
and sum(c.isdigit() for c in password) >=4):
break
示例输出:
HlxTm2fcFE54JA1I_Yp5
如果考虑速度,还可以放下while循环。在这种情况下,它实际上简化了gerrit的答案(但随后您松开了更细粒度的控件):
import string
import secrets
alphabet = string.ascii_letters + string.digits + '-_'
password = ''.join(secrets.choice(alphabet) for i in range(20))
1。 secrets.token_urlsafe
1.62 µs ± 96.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
2。强制位数/大写字符等
107 µs ± 11.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
3。 “我不需要更细粒度的控制”
77.2 µs ± 9.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
速度比较设置:Win10上的python 3.8.5 64位,每个密码43个字符(对于token_urlsafe,= 32字节)。
答案 5 :(得分:4)
对于那些卡在python&lt; 3.6:
上的人,我建议如下import os, math, string, struct
def generate_password(pass_len):
symbols = string.printable.strip()
return ''.join([symbols[x * len(symbols) / 256] for x in struct.unpack('%dB' % (pass_len,), os.urandom(pass_len))])
这比Ben Mosher解决方案的优势在于符号中的每个符号具有相同的发生变化,而使用模数稍微偏向于alpabet中的第一个符号。在这个建议中,符号字母也更大。
答案 6 :(得分:0)
我曾经在使用 the discussion section on the linked git stash
documentation 模块时所做的是:
git stash
您决定密码的长度为“get_random_bytes(lenght)”。
答案 7 :(得分:-2)
在上面的代码的第9行:
val options = SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build()
Nearby.getMessagesClient(
this, MessagesOptions.Builder()
.setPermissions(NearbyPermissions.BLE)
.build())
Nearby.getMessagesClient(this).subscribe(getPendingIntent(), options)
实际上return (choice(options) for _ in xrange(length))
没有定义,因此正确的编码是:
xrange
76行也一样。
答案 8 :(得分:-4)
我用我的偏好写了一个script,主要是关注在抄写和记忆时避免错误。 (例如:删除有些含糊不清且没有重复的字符。)
import optparse
import os
import random
import sys
DEFAULT_CHARS = "234679ADEFGHJKLMNPRTUWabdefghijkmnpqrstuwy"
DEFAULT_LEN = 18
def choices(options, length, choice=random.choice):
return (choice(options) for _ in xrange(length))
def choices_non_repeated(options, length, choice=random.choice):
assert len(options) > 1
last = choice(options)
count = 0
while count < length:
yield last
count += 1
while True:
value = choice(options)
if value != last:
last = value
break
def main(args):
op = optparse.OptionParser(add_help_option=False)
op.add_option("--help", action="help",
help="show help message and exit")
op.add_option("-b", "--bare", action="store_true", default=False,
help="print passwords without trailing newline")
op.add_option("-c", "--chars", metavar="SET", nargs=1, default=DEFAULT_CHARS,
help="character set to use (default: %default)")
op.add_option("--repeat", action="store_true", default=False,
help="allow repetition")
op.add_option("-l", "--len", dest="max", nargs=1, type="int", default=DEFAULT_LEN,
help="max length (default: %default)")
op.add_option("--min", nargs=1, type="int", default=None,
help="min length (defaults to max)")
op.add_option("-n", "--count", nargs=1, type="int", default=None,
help="number of passwords to generate (default: %default)")
op.add_option("--cols", type="int", default=None,
help="number of columns to use")
opts, args = op.parse_args(args)
if args:
op.error("unknown arguments")
if os.isatty(sys.stdin.fileno()) and (
opts.count is None and opts.cols is None
and not opts.bare
):
opts.cols = 80 // (opts.max + 1)
opts.count = opts.cols * 25
else:
if opts.count is None:
opts.count = 1
if opts.cols is None:
opts.cols = 1
if opts.bare and opts.cols != 1:
op.error("bare output requires --cols=1")
if opts.min == None:
opts.min = opts.max
if any(x < 1 for x in [opts.cols, opts.count, opts.min, opts.max]):
op.error("values must be >= 1")
choices_func = choices_non_repeated
if opts.repeat:
choices_func = choices
elif len(set(opts.chars)) < 2:
op.error("must allow repetition or provide a longer character set")
return "op.error shouldn't return"
col = 0
for _ in xrange(opts.count):
length = random.randint(opts.min, opts.max)
password = "".join(choices_func(opts.chars, length))
sys.stdout.write(password)
if not opts.bare:
col += 1
if col == opts.cols:
sys.stdout.write("\n")
col = 0
else:
sys.stdout.write(" ")
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
答案 9 :(得分:-4)
您可能希望使用map
而不是列表推导:
''.join(map(lambda x: random.choice(chars), range(length)))