Временная переменная в понимании списка

Мне часто случается иметь кусок кода, который выглядит так.

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]