简体   繁体   English

如何在列表推导中执行多个操作

[英]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个随机数?

You can vary the second argument to randint() based on the value of i : 您可以根据i的值改变randint()的第二个参数:

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

The 50 if i < 5 else 12 expression will change what is passed to random.randint() for the last two iterations. 50 if i < 5 else 12表达式将改变最后两次迭代传递给random.randint()的内容。

There are many more variations you can spell this in. List comprehensions are a bunch of loops and if filters that repeatedly execute the expression at the front. 你可以拼写更多的变体。列表推导是一堆循环, if过滤器重复执行前面的表达式。 There are lots of ways to spell vary the arguments to a function call based on the iteration values in expressions. 有很多方法可以根据表达式中的迭代值改变函数调用的参数

For example, you could record those arguments in functools.partial() objects : 例如,您可以在functools.partial()对象中记录这些参数:

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]

The possibilities are endless. 可能性是无止境。 Lambdas, randint(0, upperbound) , randint(*args) , a function that'll vary its results depending on how often it has been called, etc. But I wouldn't argue that any of these are actually more readable or understandable. randint(0, upperbound)randint(0, upperbound)randint(*args) ,一个根据调用次数而改变结果的函数,等等。但我不认为其中任何一个实际上更具可读性或可理解性。

For this case, with just 7 values, I'd just concatenate the two lists: 对于这种情况,只有7个值,我只是连接两个列表:

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

as it's just cleaner and more readable. 因为它更干净,更易读。 The small performance cost of creating a 3rd list that contains the results of the two list comprehensions is negligible here. 创建包含两个列表推导结果的第三个列表的小的性能成本在这里可以忽略不计。

Something like this maybe, concatenating 2 lists: 这样的事情可能会连接两个列表:

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

Don't reuse the name list . 不要重复使用名称list One way would be to loop through an iterable of the bounds, and send those to randint 一种方法是循环遍历可迭代的边界,并将它们发送到randint

from random import randint

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

You could also use itertools.chain and itertools.repeat to avoid building that list of bounds 您还可以使用itertools.chainitertools.repeat来避免构建该边界列表

lst = [randint(*bounds) for bounds in chain(repeat((0, 50), 5), repeat((0, 12), 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)

Here's another variation that avoids doing an if test on every iteration. 这是另一种避免在每次迭代中进行if测试的变体。 It also uses randrange , which is slightly more efficient than randint . 它还使用randrange ,它比randint稍微有效。

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

typical output 典型输出

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

This is equivalent to 这相当于

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

The outer loop selects num , the number of items in the sublist, and hi the size of the random range for that sublist; 外环选择num ,在子列表的项目数,和hi随机范围为该子列表的大小; the inner loop generates the required amount of random numbers in the desired range. 内循环在所需范围内生成所需数量的随机数。


FWIW, here's some timeit code comparing the various algorithms that have been submitted. FWIW,这里的一些timeit比较已提交的各种算法的代码。 It also verifies that they produce the same results when given the same random seed. 它还验证了当给出相同的随机种子时它们产生相同的结果。 My simple verification code uses eval , so it can only test expressions, not statements, so it can't test jpp's or Abhishek's code; 我的简单验证代码使用eval ,因此它只能测试表达式而不是语句,所以它不能测试jpp或Abhishek的代码; besides, jpp's Numpy code gives different results anyway, since it uses a different seeding algorithm. 此外,jpp的Numpy代码无论如何都会给出不同的结果,因为它使用了不同的种子算法。 Please see the timeit docs for info on what timeit does, and how to interpret the results. 有关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)

typical output 典型输出

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]

As you can see, jpp's Numpy code is the fastest. 如你所见,jpp的Numpy代码是最快的。 I expect that the speed difference would be even more apparent if we were generating a longer list of numbers. 如果我们生成更长的数字列表,我希望速度差异会更明显。

These timing were performed on an ancient 32 bit single core 2GHz machine, running Python 3.6.0 on a Debian derivative distro. 这些时序是在古老的32位单核2GHz机器上进行的,在Debian衍生发行版上运行Python 3.6.0。 YMMV. 因人而异。


Here are timings for the production of lists (or arrays) of 50 + 20 = 70 values in the same ranges. 以下是在相同范围内生成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]

If you are happy using a 3rd party library, you can via NumPy: 如果您对使用第三方库感到满意,可以通过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.])

The benefit of this method, memory pre-allocation, will be evident with larger arrays. 这种方法的好处,即内存预分配,对于更大的阵列将是明显的。

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]

You can do this using list-comprehensions and only built-ins, some monstrosity like: 可以使用list-comprehensions和只有内置函数来做到这一点,有些像monstr;

>>> 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]

Perhaps, if you want to use itertools, you can do something like: 也许,如果你想使用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]

Both of these approaches are hardly readable and simple. 这两种方法都难以理解和简单。 Instead, I would personally go with two for-loops, the naive approach. 相反,我个人会选择两个for循环,这是一种天真的方法。 It would be efficient, simple, and readable. 它将是高效,简单和可读的。 Other than showboating, I see no advantage to the above approaches in production code. 除了showboating,我认为生产代码中的上述方法没有任何优势。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM