Временная переменная в понимании списка
Мне часто случается иметь кусок кода, который выглядит так.
raw_data = [(s.split(',')[0], s.split(',')[1]) for s in all_lines if s.split(',')[1] != '"NaN"']
В принципе, я хотел бы знать, есть ли способ создать временную переменную типа splitted_s
, чтобы избежать необходимости повторять операции над зацикленным объектом (например, в этом случае нужно разделить его три раза).
Ответы
Ответ 1
Если у вас есть два действия для обработки, вы можете внедрить другое понимание списка:
raw_data = [(lhs, rhs)
for lhs, rhs
in [s.split(',')[:2] for s in all_lines]
if rhs != '"NaN"']
Вы можете использовать генератор внутри (он также дает небольшое увеличение производительности):
in (s.split(',')[:2] for s in all_lines)
Это будет даже быстрее, чем ваша реализация:
import timeit
setup = '''import random, string;
all_lines = [','.join((random.choice(string.letters),
str(random.random() if random.random() > 0.3 else '"NaN"')))
for i in range(10000)]'''
oneloop = '''[(s.split(',')[0], s.split(',')[1])
for s in all_lines if s.split(',')[1] != '"NaN"']'''
twoloops = '''raw_data = [(lhs, rhs)
for lhs, rhs
in [s.split(',') for s in all_lines]
if rhs != '"NaN"']'''
timeit.timeit(oneloop, setup, number=1000) # 7.77 secs
timeit.timeit(twoloops, setup, number=1000) # 4.68 secs
Ответ 2
Вы не можете.
Понимание списка состоит из скобок, содержащих выражение, за которым следует предложение for, а затем ноль или более для или если предложения. Результатом будет новый список, полученный в результате оценки выражения в контексте следующих для него предложений for и if.
Отсюда
Назначение в Python не является выражением.
Как пишет Padraic Cunningham - если вам нужно разбить его несколько раз, не делайте этого в понимании списка.
Ответ 3
Начиная с Python 3.8
и введением выражений присваивания (PEP 572) (:=
оператор), можно использовать локальную переменную в пределах понимания списка, чтобы избежать вызова дважды одного и того же выражения:
В нашем случае мы можем назвать оценку line.split(',')
как переменную parts
, используя результат выражения для фильтрации списка, если parts[1]
не равно NaN
; и, таким образом, повторно использовать parts
для получения сопоставленного значения:
# lines = ['1,2,3,4', '5,NaN,7,8']
[(parts[0], parts[1]) for line in lines if (parts := line.split(','))[1] != 'NaN']
# [('1', '2')]
Ответ 4
Позднее редактирование
Теперь, один год мудрее и понимая вопрос..., я бы предложил простой
raw_data = [tuple(ssp[:2]) for s in all_lines for ssp in [s.split(',')] if ssp[1]!='"NaN"']
который работает правильно, потому что [s.split(',')]
- это список, единственным элементом которого является список, возвращаемый s.split(',')
, а righmost/inner loop, for ssp in [s.split(',')]
- грубо, эквивалентный временному назначению, ssp = s.split('',)
Мой первоначальный ответ
<суб >
У меня есть некоторая проблема в понимании именно вопроса, но если вы хотите использовать временную переменную в понимании списка, поместите значение (или выражение), которое вам нужно в список, все в одиночку! и использовать другое понимание списка
суб >
In [1]: [a*b for b in [10] for a in [1,2,3,4,5]]
Out[1]: [10, 20, 30, 40, 50]
In [2]:
<суб >
Самое правое понимание - это то, что находится во внутреннем цикле, поэтому, если у вас есть функция, которая использует много времени для вычисления временного значения, которое должно использоваться в понимании списка, e; g; следующий;-)
суб >
In [2]: def long_computation(x): print 1 ; return x
<суб >
то две следующие конструкции возвращают точно такой же список, но...
суб >
In [3]: [a*b for b in [long_computation(10)] for a in [1,2,3,4,5]]
1
Out[3]: [10, 20, 30, 40, 50]
In [4]: [a*b for a in [1,2,3,4,5] for b in [long_computation(10)]]
1
1
1
1
1
Out[4]: [10, 20, 30, 40, 50]