Как мне выполнить несколько операций в понимании списка - PullRequest
0 голосов
/ 05 октября 2018
L = [random.randint(0,50) for i in range(5) random.randint(0,12) for i in range(2)]

Как получить 5 случайных чисел между (0,50), а затем 2 случайных числа между (0,12)?

Ответы [ 8 ]

0 голосов
/ 05 октября 2018

Вот еще один вариант, позволяющий избежать выполнения теста 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;кроме того, код Numpy jpp в любом случае дает разные результаты, поскольку использует другой алгоритм заполнения.Пожалуйста, смотрите 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]

КакВы можете видеть, код Numpy jpp является самым быстрым.Я ожидаю, что разница в скорости была бы еще более очевидной, если бы мы генерировали более длинный список чисел.

Это время было выполнено на древней 32-битной одноядерной машине с частотой 2 ГГц, работающей на Python 3.6.0 на производной от Debian.дистрибутив.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]
0 голосов
/ 05 октября 2018
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]
0 голосов
/ 05 октября 2018

Вы можете сделать это, используя списки и только встроенные модули, например, чудовище:

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

Оба эти подхода трудно читаемы и просты.Вместо этого я лично выбрал бы два цикла, наивный подход.Это было бы эффективно, просто и читабельно.Помимо демонстрации на лодках, я не вижу преимущества вышеупомянутых подходов в рабочем коде.

0 голосов
/ 05 октября 2018

Если вы довольны использованием сторонней библиотеки, вы можете через 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.])

Преимущества этого метода, предварительного выделения памяти, будут очевидны при использовании больших массивов.

0 голосов
/ 05 октября 2018

Примерно так, объединяя 2 списка:

from random import randint
my_list = [randint(0,50) for i in range(5)] + [randint(0,12) for i in range(2)]
0 голосов
/ 05 октября 2018
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)
0 голосов
/ 05 октября 2018

Вы можете изменить второй аргумент на randint() на основе значения i:

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

Выражение 50 if i < 5 else 12 изменит то, что передано в random.randint() для последних двух итераций.

Есть еще много вариантов, в которых вы можете записать это. Постижения списков - это набор циклов и if фильтров, которые многократно выполняют выражение спереди.Существует множество способов написания изменения аргументов для вызова функции на основе значений итераций в выражениях.

Например, вы можете записать эти аргументы в 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]

Возможности безграничны.Lambdas, randint(0, upperbound), randint(*args), функция, которая будет изменять результаты в зависимости от того, как часто она вызывается и т. Д. Но я бы не стал утверждать, что любой из них на самом деле более читабелен или понятен.

Для этого случая, всего с 7 значениями, я бы просто объединил два списка:

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

, так как он более чистый и более читаемый.Небольшие затраты производительности на создание третьего списка, который содержит результаты двух пониманий списка, здесь незначительны.

0 голосов
/ 05 октября 2018

Не используйте повторно имя list.Одним из способов было бы перебрать итерируемые границы и отправить их на randint

from random import randint

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

. Вы также можете использовать itertools.chain и itertools.repeat, чтобы избежать построения этого списка границ

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