如何在列表理解中执行多个操作

How do I perform multiple operations in a list comprehension

L = [random.randint(0,50) for i in range(5) random.randint(0,12) for i in range(2)]

如何让它在 (0,50) 之间选择 5 个随机数,然后在 (0,12) 之间选择 2 个随机数?

不要重复使用名称 list。一种方法是遍历边界的可迭代对象,并将它们发送到 randint

from random import randint

lst = [randint(*bounds) for bounds in [(0, 50)]*5 + [(0, 12)]*2]

您还可以使用 itertools.chainitertools.repeat 来避免构建边界列表

lst = [randint(*bounds) for bounds in chain(repeat((0, 50), 5), repeat((0, 12), 2))]

您可以根据 i 的值将第二个参数更改为 randint()

[randint(0, 50 if i < 5 else 12) for i in range(7)]

50 if i < 5 else 12 表达式将更改最后两次迭代中传递给 random.randint() 的内容。

您可以拼出更多变体。列表理解是一堆循环和 if 过滤器,它们重复执行前面的表达式。有很多拼写方法 根据表达式中的迭代值 改变函数调用的参数。

例如,您可以将这些参数记录在 functools.partial() objects:

from functools import partial
from random import randint

rint50 = partial(randint, 0, 50)
rint12 = partial(randint, 0, 12)
[rint() for rint in [rint50] * 5 + [rint12] * 2]

可能性是无限的。 Lambdas、randint(0, upperbound)randint(*args),一个根据调用频率等而改变其结果的函数。但我不认为其中任何一个实际上更具可读性或可理解性。

对于这种情况,只有 7 个值,我只是连接两个列表:

[randint(0, 50) for _ in range(5)] + [randint(0, 12) for _ in range(2)]

因为它更简洁、更易读。创建包含两个列表理解结果的第三个列表的小性能成本在这里可以忽略不计。

import random
l = [random.randint(0,50) for i in range(5)]
l.extend([random.randint(0,12) for i in range(2)])

print(l)

可能是这样的,连接 2 个列表:

from random import randint
my_list = [randint(0,50) for i in range(5)] + [randint(0,12) for i in range(2)]

如果您喜欢使用第 3 方库,可以通过 NumPy:

import numpy as np

np.random.seed(0) # for consistency / testing

A = np.zeros(7)
A[:5] = np.random.randint(0, 20, 5)
A[5:] = np.random.randint(0, 12, 2)

array([ 12.,  15.,   0.,   3.,   3.,   7.,   9.])

这种方法(内存预分配)的好处对于较大的数组会很明显。

since you want to pick 5 random values from 0 - 50( exclusive)
i = 0...4
and then you want to pick 2 random values from 0 - 12( exclusive)
i = 5 6


lst = [random.randint(0,50) if i < 5 else random.randint(0,12) for i in range(7)]

print(lst) # [7, 10, 40, 4, 38, 1, 5]

可以使用列表理解来做到这一点,并且只使用内置函数,一些怪异的东西比如:

>>> result = [
...     random.randint(a,b)
...     for args in (((0,50) for _ in range(5)), ((0, 12) for _ in range(2)))
...     for a, b in args
... ]
>>> result
[33, 38, 19, 9, 47, 0, 8]

也许,如果你想使用 itertools,你可以这样做:

>>> from itertools import chain, repeat, starmap
>>> list(starmap(random.randint, chain(repeat((0,50),5), repeat((0,12),2))))
[16, 47, 40, 21, 15, 12, 4]

这两种方法都难以阅读且简单。相反,我个人会使用两个 for 循环,这是一种天真的方法。它将高效、简单且可读。除了炫耀之外,我认为上述方法在生产代码中没有任何优势。

这是另一种变体,它避免在每次迭代时都进行 if 测试。它还使用randrange,比randint.

效率稍高
from random import randrange
lst = [randrange(hi) for num, hi in ((5, 51), (2, 13)) for _ in range(num)]
print(lst)

典型输出

[10, 31, 46, 25, 23, 6, 5]

这相当于

lst = []
for num, hi in ((5, 51), (2, 13)):
    for _ in range(num):
        lst.append(randrange(hi))

外循环选择num子列表中的项目数,以及hi该子列表的随机范围大小;内部循环在所需范围内生成所需数量的随机数。


FWIW,这里有一些 timeit 代码比较了已提交的各种算法。它还验证了在给定相同的随机种子时它们会产生相同的结果。我的简单验证码使用的是eval,所以只能测试表达式,不能测试语句,所以不能测试jpp的或者abhishek的代码;此外,jpp 的 Numpy 代码无论如何都会给出不同的结果,因为它使用了不同的播种算法。请参阅 timeit 文档以了解有关 timeit 的作用以及如何解释结果的信息。

from timeit import Timer
import random
from random import randint, randrange, seed
from itertools import chain, repeat, starmap
from functools import partial
import numpy as np

imports = 'random, randint, randrange, seed, chain, repeat, starmap, partial, np'

commands = (
    ('Martijn', '', '[randint(0, 50 if i < 5 else 12) for i in range(7)]'),
    ('Martijn_partial', 
        'rint50 = partial(randint, 0, 50); rint12 = partial(randint, 0, 12)', 
        '[rint() for rint in [rint50] * 5 + [rint12] * 2]'
    ),
    ('Patrick', '', '[randint(*bounds) for bounds in [(0, 50)]*5 + [(0, 12)]*2]'),
    ('Patrick_chain', '', 
        '[randint(*bounds) for bounds in chain(repeat((0, 50), 5), repeat((0, 12), 2))]'
    ),
    ('Ralf', '', '[randint(0,50) for i in range(5)] + [randint(0,12) for i in range(2)]'),
    ('Abhishek', '', 'l = [random.randint(0,50) for i in range(5)];'
        'l.extend([random.randint(0,12) for i in range(2)])'
    ),
    ('PM 2Ring', '', '[randrange(hi) for num, hi in ((5, 51), (2, 13)) for _ in range(num)]'),
    ('jpp', '', 'A = np.zeros(7); ' 
        'A[:5] = np.random.randint(0, 20, 5); A[5:] = np.random.randint(0, 12, 2)'
    ),
    ('Tanmay jain', '', 
        '[random.randint(0,50) if i < 5 else random.randint(0,12) for i in range(7)]'
    ),
    ('juanpa', '', '[random.randint(a,b) for args in (((0,50) for _ in range(5)),' 
        '((0, 12) for _ in range(2))) for a, b in args]'
    ),
    ('juanpa_starmap', '', 'list(starmap(random.randint,'
        'chain(repeat((0,50),5), repeat((0,12),2))))'
    ),
)

def verify():
    for name, setup, cmd in commands:
        if name in ('jpp', 'Abhishek'):
            continue
        seed(17)
        if setup:
            exec(setup)
        print('{:16}: {}'.format(name, eval(cmd)))
    print()

def time_test(loops):
    timings = []
    print('loops =', loops)
    for name, setup, cmd in commands:
        setup = 'from __main__ import ' + imports + ';' + setup
        t = Timer(cmd, setup=setup)
        result = sorted(t.repeat(3, loops))
        timings.append((result, name))
    timings.sort()
    for result, name in timings:
        print('{:16} : {}'.format(name, result))

verify()
time_test(5000)

典型输出

Martijn         : [33, 26, 19, 23, 18, 2, 12]
Martijn_partial : [33, 26, 19, 23, 18, 2, 12]
Patrick         : [33, 26, 19, 23, 18, 2, 12]
Patrick_chain   : [33, 26, 19, 23, 18, 2, 12]
Ralf            : [33, 26, 19, 23, 18, 2, 12]
PM 2Ring        : [33, 26, 19, 23, 18, 2, 12]
Tanmay jain     : [33, 26, 19, 23, 18, 2, 12]
juanpa          : [33, 26, 19, 23, 18, 2, 12]
juanpa_starmap  : [33, 26, 19, 23, 18, 2, 12]

loops = 5000
jpp              : [0.23938178099342622, 0.24184146700281417, 0.3152835669970955]
PM 2Ring         : [0.26918871099769603, 0.27244400099880295, 0.2916741489971173]
Patrick          : [0.34155847399961203, 0.34415175200410886, 0.3531294650019845]
juanpa_starmap   : [0.3417540490045212, 0.34329504700144753, 0.3438059809996048]
Martijn          : [0.3509639670010074, 0.362117896998825, 0.547288200003095]
Martijn_partial  : [0.3511254819968599, 0.35262946599686984, 0.39430355399963446]
Patrick_chain    : [0.3541102219969616, 0.3545923809942906, 0.3555165420038975]
Tanmay jain      : [0.3558294050017139, 0.5510739650053438, 0.7693202439986635]
Ralf             : [0.3678122450000956, 0.44522786799643654, 0.44827762299973983]
juanpa           : [0.4089203829935286, 0.41227930299646687, 0.42410747800022364]
Abhishek         : [0.4811078249986167, 0.4942625819967361, 0.6255962599971099]

如你所见,jpp的Numpy代码是最快的。如果我们生成更长的数字列表,我希望速度差异会更加明显。

这些计时是在一台古老的 32 位单核 2GHz 机器上执行的,运行 Python Debian 衍生发行版上的 3.6.0。 YMMV.


以下是生成相同范围内 50 + 20 = 70 个值的列表(或数组)的时间安排。

loops = 500
jpp              : [0.025625186994147953, 0.025764200996491127, 0.03122780400008196]
PM 2Ring         : [0.21989007600495825, 0.2200367909972556, 0.22065802400175016]
juanpa_starmap   : [0.3094131350007956, 0.3110805670003174, 0.31563361900043674]
Patrick_chain    : [0.3122365829985938, 0.31262181099737063, 0.3137894630053779]
Patrick          : [0.3130071220002719, 0.31769691400404554, 0.3179219129960984]
Ralf             : [0.31566168300196296, 0.3157304769993061, 0.3234770689959987]
Martijn          : [0.3193310350034153, 0.3275600470005884, 0.35491854500287445]
Martijn_partial  : [0.321399387998099, 0.3226969290044508, 0.32442738999816356]
Abhishek         : [0.32655813400197076, 0.3363869300010265, 0.3657162370000151]
Tanmay jain      : [0.32833286200184375, 0.33107244400162017, 0.39565577400207985]
juanpa           : [0.35968791200139094, 0.3754627199959941, 0.3933205349967466]