Itertools.compress Python не работает точно так же, как логическая маска.Зачем? - PullRequest
0 голосов
/ 24 января 2019

У меня есть список строк, которые я хочу отфильтровать по маске логических значений, используя itertools.compress.

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

from itertools import product, starmap, compress

def is_in(string, other_string):
    return string in other_string

to_find = ['hello', 'bye']
some_sentences = ['hello to you', ' hello and bye', 'bye bye']

cartesian = product(to_find, some_sentences)
matched_mask = starmap(is_in, cartesian)
matched = compress(cartesian, matched_mask)
print(list(matched))
actual_result = [('hello', 'hello to you'), ('bye', ' hello and bye')]

expected = [('hello', 'hello to you'), 
           ('hello', 'hello and bye'),
           ('bye', ' hello and bye'), 
           ('bye', 'bye bye')]

1 Ответ

0 голосов
/ 24 января 2019

itertools.product возвращает итератор, и итераторы обычно являются однопроходными (возможны исключения). Как только элемент повторяется, он не повторяется снова.

Однако вы используете результат itertools.product в двух местах: один раз в качестве аргумента для starmap и один раз в качестве аргумента для compress. Таким образом, если starmap «вытолкнет» элемент из product, то в следующий раз compress «вытолкнет» элемент из того же продукта, он получит следующий элемент (не тот же элемент).

В большинстве случаев я бы рекомендовал не назначать такие итераторы в качестве переменных именно из-за их "однопроходного" характера.

Итак, очевидным решением было бы сгенерировать продукт дважды:

matched_mask = starmap(is_in, product(to_find, some_sentences))
matched = compress(product(to_find, some_sentences), matched_mask)
print(list(matched))
# [('hello', 'hello to you'), ('hello', ' hello and bye'), ('bye', ' hello and bye'), ('bye', 'bye bye')]

В этом случае я думаю, что цикл в функции генератора будет более читабельным, чем использование нескольких itertools:

from itertools import product

def func(to_find, some_sentences):
    for sub, sentence in product(to_find, some_sentences):
        if sub in sentence:
            yield sub, sentence

А затем используйте это так:

>>> to_find = ['hello','bye']
>>> some_sentences = ['hello to you', ' hello and bye', 'bye bye']
>>> list(func(to_find, some_sentences))
[('hello', 'hello to you'), 
 ('hello', ' hello and bye'), 
 ('bye', ' hello and bye'), 
 ('bye', 'bye bye')]

Или, если вам нравятся однострочники:

>>> [(sub, sentence) for sub, sentence in product(to_find, some_sentences) if sub in sentence]
[('hello', 'hello to you'),
 ('hello', ' hello and bye'),
 ('bye', ' hello and bye'),
 ('bye', 'bye bye')]
...