请选择 进入手机版|继续访问电脑版
发新帖

Python 生成一段随机字符串的两种写法

[复制链接]
8780 17

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
在看微信 JS 接口 demo 的 Python 版本时,发现中间的随机字符串是这样生成的:
# 方法 1''.join(random.choice(string.ascii_letters + string.digits) for _ in range(15)其实,也可以换一种方式:
# 方法 2s=''for _ in range(15):    s += random.choice(string.ascii_letters + string.digits)很显然,方法 1 更优雅而且更 Python,方法 2 更加像是 C++/Java 的写法。
不知道这两者在性能上有没有差异?

举报 使用道具

回复

精彩评论17

ltux  注册会员  发表于 2017-10-4 18:21:48 | 显示全部楼层
不知道这两者在性能上有没有差异?
请自己测试,不要让别人帮你测试。

举报 使用道具

回复
jingniao  注册会员  发表于 2017-10-4 21:50:37 | 显示全部楼层
1,第一种方式更推荐,速度快一点
2,第一段你少了个括号

举报 使用道具

回复
jingniao  注册会员  发表于 2017-10-5 05:17:14 | 显示全部楼层
补充:理论上……

举报 使用道具

回复
crab  新手上路  发表于 2017-10-5 08:34:22 | 显示全部楼层
100W 次
timeit
第一个 18s
第二个 17.3s

举报 使用道具

回复
daniel65536  新手上路  发表于 2017-10-5 15:55:39 | 显示全部楼层
Python 3.6 开始可以使用:
''.join(random.choices(string.ascii_letters + string.digits, k=15)

举报 使用道具

回复
chace  新手上路  发表于 2017-10-5 16:24:13 | 显示全部楼层
@jingniao 没注意,谢谢提醒

举报 使用道具

回复
chace  新手上路  发表于 2017-10-5 16:30:04 | 显示全部楼层
@Daniel65536 谢谢。目测跟我一样少打了一个括号^_^

举报 使用道具

回复
chace  新手上路  发表于 2017-10-5 17:12:54 | 显示全部楼层
@Daniel65536 这个内置函数的速度是最快的,100W 个字符 3.15765118598938s

举报 使用道具

回复
workwonder  新手上路  发表于 2017-10-5 17:55:08 | 显示全部楼层
https://gist.github.com/wonderbeyond/1806c7b43d3e642e5ad0aee7052b8e8f  显示 Gist 代码


这是我记的笔记,搬 Django 的实现,为什么写这么复杂,大家可以发表下看法。

举报 使用道具

回复
kilerd  注册会员  发表于 2017-10-6 05:26:04 | 显示全部楼层
@workwonder ide 没跟你说 for i 里面的 i 没用到吗?

举报 使用道具

回复
ryd994  新手上路  发表于 2017-10-6 14:38:54 | 显示全部楼层
可以对比一下 choice 和 choices 的源码
https://hg.python.org/cpython/file/tip/Lib/random.py#l252
https://hg.python.org/cpython/file/tip/Lib/random.py#l340
choice 是生成一个随机的整数索引
choices 是把分布比重(默认等比重)转换成 0-1 的数轴,然后 random()生成 0-1 小数,对应到数轴上
大家底层都是用的 random(),choices 更复杂,理应更慢才对

使用 cProfile 测试
>>> cProfile.run('"".join(random.choice(string.ascii_letters + string.digits) for _ in range(10**7))')
         60321941 function calls in 21.869 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
10000001    5.516    0.000   20.772    0.000 <string>:1(<genexpr>)
        1    0.000    0.000   21.869   21.869 <string>:1(<module>)
10000000    6.283    0.000    8.918    0.000 random.py:222(_randbelow)
10000000    5.381    0.000   15.256    0.000 random.py:252(choice)
        1    0.000    0.000   21.869   21.869 {built-in method builtins.exec}
10000000    0.956    0.000    0.956    0.000 {built-in method builtins.len}
10000000    0.785    0.000    0.785    0.000 {method 'bit_length' of 'int' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
10321936    1.851    0.000    1.851    0.000 {method 'getrandbits' of '_random.Random' objects}
        1    1.097    1.097   21.869   21.869 {method 'join' of 'str' objects}


>>> cProfile.run('"".join(random.choices(string.ascii_letters + string.digits, k=10**7))')
         10000007 function calls in 3.463 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.014    0.014    3.463    3.463 <string>:1(<module>)
        1    0.000    0.000    3.374    3.374 random.py:340(choices)
        1    2.780    2.780    3.374    3.374 random.py:352(<listcomp>)
        1    0.000    0.000    3.463    3.463 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.075    0.075    0.075    0.075 {method 'join' of 'str' objects}
10000000    0.594    0.000    0.594    0.000 {method 'random' of '_random.Random' objects}

可以看到:
1. choice 法到底层用的是 getrandbits
# Only call self.getrandbits if the original random() builtin method
# has not been overridden or if a new getrandbits() was supplied.
说明 getrandbits 应该是比 random 更快的,否则官方不会这么用

2. choice 法的 function calls 是 choices 法的 6 倍,而正好时间也是将近 6 倍,很可能这两者是有关联的

3.看 tottime,choices 的时间主要是在 random.py:352
return [population[_int(random() * total)] for i in range(k)]
这里构建 list 消耗大可以理解

choice 的时间主要是在<string>:1,random.py:222,random.py:252 上
choice 一个 5 行的函数,吃这么多时间,很难理解

举报 使用道具

回复
happlebao  新手上路  发表于 2017-10-6 16:53:19 | 显示全部楼层
``` python
λ  python -m timeit -n 1000 -r 10 -s "import random, string" "''.join(r andom.choices(string.ascii_letters + string.digits, k=10000))"
1000 loops, best of 10: 1.53 msec per loop

λ  python -m timeit -n 1000 -r 10 -s "import os" "os.urandom(10000)"
1000 loops, best of 10: 2.91 usec per loop
```

note:  1 msec (milliseconds) = 1000 usec (microseconds)

举报 使用道具

回复
chace  新手上路  发表于 2017-10-7 09:05:05 | 显示全部楼层
@happlebao
厉害,还有这个方法
>>> cProfile.run('binascii.hexlify(os.urandom(10**7)).decode()')
         6 function calls in 0.789 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.005    0.005    0.789    0.789 <string>:1(<module>)
        1    0.032    0.032    0.032    0.032 {built-in method binascii.hexlify}
        1    0.000    0.000    0.789    0.789 {built-in method builtins.exec}
        1    0.747    0.747    0.747    0.747 {built-in method posix.urandom}
        1    0.005    0.005    0.005    0.005 {method 'decode' of 'bytes' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

举报 使用道具

回复
chace  新手上路  发表于 2017-10-7 12:49:18 | 显示全部楼层
@workwonder 中间一段代码都是在生成 random.seed()。可能是为了随机效果更好吧。

举报 使用道具

回复
chace  新手上路  发表于 2017-10-7 20:23:14 | 显示全部楼层
@ryd994
应该是因为<string>:1,random.py:222,random.py:252 都是 py 实现的,而且调用次数都是最多的,所以耗时吧。
choice:
10000001 5.516 0.000 20.772 0.000 <string>:1(<genexpr>)
10000000 6.283 0.000 8.918 0.000 random.py:222(_randbelow)
10000000 5.381 0.000 15.256 0.000 random.py:252(choice)

而在 choices 中,调用最多的是 C 语言实现的,所以不耗时。
choices:
10000000 0.594 0.000 0.594 0.000 {method 'random' of '_random.Random' objects}

楼上有 XD 提到 os.urandom()调用 syscall(such as /dev/urandom on Unix or CryptGenRandom on Windows)生成一段随机的 bytes 速度更快。

举报 使用道具

回复
mayne95  新手上路  发表于 2017-10-8 18:17:07 | 显示全部楼层
3.6 有个 secrets 模块,不用自己写了

举报 使用道具

回复
nannanziyu  新手上路  发表于 2017-10-8 19:00:19 | 显示全部楼层
试了下 C#  
var r = new Random(Environment.TickCount);  
var randomstring = new string(Enumerable.Range(0,1000000).Select(i=>(char)(r.Next(33,127))).ToArray());  
00:00:00.0247641

举报 使用道具

回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表