从给定字符生成固定长度随机字符串的最佳Python方法

4 投票
2 回答
1277 浏览
提问于 2025-04-17 10:34

这是我之前提问的一个延伸,具体可以查看我早期的问题

问题描述:给定一个数字N和一个任意的(但不能为空的)集合/字符串/列表,包含字符E,要求返回一个由E中的字符组成的随机字符串,长度为N

用Python最优雅的方式怎么做呢?我可以用''.join(( random.choice(E) for i in xrange(N) )),但我在寻找更好的方法。有没有random或者itertools中的内置函数可以做到这一点呢?

如果能做到以下几点会更好:

  1. 减少函数调用次数
  2. 能写成一行代码
  3. 对任意的NE都适用
  4. 运行效率更高

PS:我提这个问题其实是因为我对Python有点追求(如果我可以这么说的话),想找一些优雅和艺术感的代码写法。我提到这个是因为看起来有点像作业,我想向Stack Overflow社区保证这并不是作业。

2 个回答

4

你的解决方案看起来已经很不错了,不过为了全面起见,这里有一些其他的选择:

''.join(map(random.choice, [E]*N))

或者可以使用itertools库:

from itertools import repeat
''.join(map(random.choice, repeat(E, N)))

如果你在用Python 2.x,itertools.imap()会比map()更高效,因为它不会在内存中创建完整的列表。

这里有一些有趣的时间测试数据(在Python 2.6上测试的):

>>> import timeit
>>> t1 = timeit.Timer("''.join(random.choice('abcdefghijkl') for i in xrange(3))", "import random")
>>> t2 = timeit.Timer("''.join(map(random.choice, ['abcdefghijkl']*3))", "import random")
>>> t3 = timeit.Timer("''.join(map(random.choice, repeat('abcdefghijkl', 3)))", "import random; from itertools import repeat")
>>> t4 = timeit.Timer("''.join(random.sample('abcdefghijkl'*3, 3))", "import random")
>>> t1.timeit(1000000)   # (random.choice(E) for i in xrange(N))  - OP
7.0744400024414062
>>> t2.timeit(1000000)   # map(random.choice, [E]*N)              - F.J
4.3570120334625244
>>> t3.timeit(1000000)   # map(random.choice, repeat(E, N))       - F.J
5.9411048889160156
>>> t4.timeit(1000000)   # random.sample(E*N, N)                  - DSM
6.9877378940582275

显然,map(random.choice, [E]*N)是赢家,至少在小的EN情况下是这样的。

9
''.join(random.sample(E*N, N))

虽然这样做在集合(sets)上可能不太管用,仔细想想。

''.join(random.choice(E) for i in xrange(N))

其实这已经相当符合Python的风格了——简单、清晰,而且表达得很好。

需要花几个小时去思考的那种“Python风格”其实并不是真正的Python风格。

撰写回答