Выражения генератора и понимание списка

Если вы уверены, что selectedOption конструктора Filter(T selectedOption) не является нулевым. Вы можете использовать отражение.

public class Filter<T> {
    private List<T> availableOptions = new ArrayList<T>();
    private T selectedOption;

    public Filter(T selectedOption) {
        this.selectedOption = selectedOption;
        for (T option : this.selectedOption.getClass().getEnumConstants()) {  // INVALID CODE
            availableOptions.add(option);
        }
    }
}

Надеюсь, это поможет.

377
задан Sнаđошƒаӽ 8 April 2016 в 17:12
поделиться

9 ответов

Хороший ответ Джона (это понимание списка лучше, когда вы хотите повторять что-то несколько раз). Однако также стоит отметить, что вы должны использовать список, если вы хотите использовать любой из методов списка. Например, следующий код не будет работать:

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

В основном, используйте выражение генератора, если все, что вы делаете, это итерация один раз. Если вы хотите сохранить и использовать сгенерированные результаты, то вам, вероятно, лучше понять список.

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

257
ответ дан Eli Courtwright 8 April 2016 в 17:12
поделиться

Итерирование по выражению генератора или пониманию списка сделает то же самое. Однако понимание списка сначала создаст весь список в памяти, а выражение генератора создаст элементы на лету, так что вы можете использовать его для очень больших (а также бесконечные!) последовательности.

163
ответ дан kenorb 8 April 2016 в 17:12
поделиться

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

92
ответ дан John Millikin 8 April 2016 в 17:12
поделиться

Важным моментом является то, что понимание списка создает новый список. Генератор создает повторяемый объект, который будет «фильтровать» исходный материал на лету, когда вы будете использовать биты.

Представьте, что у вас есть файл журнала объемом 2 ТБ, называемый "принц-файлом", и вы хотите, чтобы содержимое и длина были для всех строк, начинающихся со слова "ВХОД".

Таким образом, вы пытаетесь начать с написания списка:

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

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

Таким образом, вместо этого мы можем использовать генератор, чтобы применить «фильтр» к нашему контенту. На самом деле никакие данные не читаются, пока мы не начнем итерацию по результату.

logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

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

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

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

Давайте запишем наши отфильтрованные строки в другой файл:

outfile = open("filtered.txt","a")
for entry,length in long_entries:
    outfile.write(entry)

Теперь мы читаем входной файл. Поскольку наш цикл for продолжает запрашивать дополнительные строки, генератор long_entries запрашивает строки из генератора entry_lines, возвращая только те, длина которых превышает 80 символов. И, в свою очередь, генератор entry_lines запрашивает строки (отфильтрованные как указано) от итератора logfile, который, в свою очередь, читает файл.

Таким образом, вместо того, чтобы «выталкивать» данные в свою функцию вывода в виде полностью заполненного списка, вы даете функции вывода способ «извлекать» данные только тогда, когда это необходимо. В нашем случае это гораздо эффективнее, но не так гибко. Генераторы один путь, один проход; данные из файла журнала, который мы прочитали, немедленно удаляются, поэтому мы не можем вернуться к предыдущей строке. С другой стороны, нам не нужно беспокоиться о сохранении данных, как только мы закончим с ними.

55
ответ дан tylerl 8 April 2016 в 17:12
поделиться

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

Например:

sum(x*2 for x in xrange(256))

dict( ((k, some_func(k) for k in some_list_of_keys) )

Преимущество состоит в том, что список не полностью сформирован, и, следовательно, мало памяти используется (и также должен быть быстрее)

Вы должны, Тем не менее, используйте понимание списка, когда желаемый конечный продукт является списком. Вы не собираетесь сохранять какую-либо память, используя выражения генератора, так как вам нужен сгенерированный список. Вы также получаете возможность использовать любые функции списка, например отсортированные или перевернутые.

Например:

reversed( [x*2 for x in xrange(256)] )
45
ответ дан Fred Foo 8 April 2016 в 17:12
поделиться

При создании генератора из изменяемого объекта (например, списка) следует помнить, что генератор будет оцениваться по состоянию списка во время использования генератора, а не во время создания генератора:

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

Если есть шанс, что ваш список будет изменен (или изменяемый объект внутри этого списка), но вам нужно состояние при создании генератора, вам нужно вместо этого использовать понимание списка.

10
ответ дан freaker 8 April 2016 в 17:12
поделиться

Я использую модуль Hadoop Mincemeat . Я думаю, что это отличный пример, чтобы принять к сведению:

import mincemeat

def mapfn(k,v):
    for w in v:
        yield 'sum',w
        #yield 'count',1


def reducefn(k,v): 
    r1=sum(v)
    r2=len(v)
    print r2
    m=r1/r2
    std=0
    for i in range(r2):
       std+=pow(abs(v[i]-m),2)  
    res=pow((std/r2),0.5)
    return r1,r2,res

Здесь генератор получает числа из текстового файла (размером до 15 ГБ) и применяет простую математику к этим числам, используя map-Reduce от Hadoop. , Если бы я не использовал функцию yield, а использовал бы понимание списка, для вычисления сумм и среднего значения потребовалось бы гораздо больше времени (не говоря уже о сложности пространства).

Hadoop - отличный пример использования всех преимуществ Генераторов.

4
ответ дан SherylHohman 8 April 2016 в 17:12
поделиться

Как насчет использования [(exp для x в iter)], чтобы получить пользу от обоих. Производительность от понимания генератора, а также методов списка

0
ответ дан David 8 April 2016 в 17:12
поделиться

Иногда вы можете обойтись без функции tee из itertools , она возвращает несколько итераторов для одного и того же генератора, которые могут использоваться независимо.

3
ответ дан kenorb 8 April 2016 в 17:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: