Для чего можно использовать функции генератора Python?

Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int:

int x;
x = 10;

В этом примере переменная x является int, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.

Но когда вы пытаетесь объявить ссылочный тип, произойдет что-то другое. Возьмите следующий код:

Integer num;
num = new Integer(10);

Первая строка объявляет переменную с именем num, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».

Во второй строке ключевое слово new используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования . (точка).

Exception, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num. Перед созданием объекта вы получите NullPointerException. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.

Например, вы можете имеют следующий метод:

public void doSomething(SomeObject obj) {
   //do something to obj
}

В этом случае вы не создаете объект obj, скорее предполагая, что он был создан до вызова метода doSomething. К сожалению, этот метод можно вызвать следующим образом:

doSomething(null);

В этом случае obj имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.

Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething может быть записано как:

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj != null) {
       //do something
    } else {
       //do something else
    }
}

Наконец, Как определить исключение & amp; причина использования Трассировки стека

206
задан Uli Köhler 6 January 2014 в 14:30
поделиться

10 ответов

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

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

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

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

, Если Вы хотите видеть пример последних двух подходов, см. os.path.walk () (старая обходящая файловую систему функция с обратным вызовом) и os.walk () (новый обходящий файловую систему генератор.), Конечно, если Вы действительно хотели собрать все результаты в списке, подход генератора тривиален для преобразования в подход большого списка:

big_list = list(the_generator)
229
ответ дан Thomas Wouters 23 November 2019 в 04:46
поделиться

Я использую генераторы, когда наш веб-сервер действует как прокси:

  1. клиент запрашивает проксированный URL с сервера
  2. , сервер начинает загружать целевой url
  3. , к которому сервер приводит, чтобы возвратить результаты клиенту, как только это получает их
2
ответ дан Brian 23 November 2019 в 04:46
поделиться

Груды материала. Любое время Вы хотите генерировать последовательность объектов, но не хотите должными быть 'осуществлять' их всех в список сразу. Например, у Вас мог быть простой генератор, который возвращает простые числа:

def primes():
    primes_found = set()
    primes_found.add(2)
    yield 2
    for i in itertools.count(1):
        candidate = i * 2 + 1
        if not all(candidate % prime for prime in primes_found):
            primes_found.add(candidate)
            yield candidate

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

def prime_products():
    primeiter = primes()
    prev = primeiter.next()
    for prime in primeiter:
        yield prime * prev
        prev = prime

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

1
ответ дан Nick Johnson 23 November 2019 в 04:46
поделиться

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

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

7
ответ дан MvdD 23 November 2019 в 04:46
поделиться

Мое любимое использование является "фильтром" и "уменьшает" операции.

Скажем, мы читаем файл, и только хотим строки, которые начинаются с "##".

def filter2sharps( aSequence ):
    for l in aSequence:
        if l.startswith("##"):
            yield l

Мы можем тогда использовать функцию генератора в надлежащем цикле

source= file( ... )
for line in filter2sharps( source.readlines() ):
    print line
source.close()

, уменьшать пример подобен. Скажем, у нас есть файл, где мы должны определить местоположение блоков <Location>...</Location> строки. [Не HTML-тэги, но строки, которые, оказывается, выглядят подобными тегу.]

def reduceLocation( aSequence ):
    keep= False
    block= None
    for line in aSequence:
        if line.startswith("</Location"):
            block.append( line )
            yield block
            block= None
            keep= False
        elif line.startsWith("<Location"):
            block= [ line ]
            keep= True
        elif keep:
            block.append( line )
        else:
            pass
    if block is not None:
        yield block # A partial block, icky

Снова, мы можем использовать этот генератор в надлежащем для цикла.

source = file( ... )
for b in reduceLocation( source.readlines() ):
    print b
source.close()

идея состоит в том, что функция генератора позволяет нам фильтровать или уменьшать последовательность, производя другую последовательность одно значение за один раз.

12
ответ дан S.Lott 23 November 2019 в 04:46
поделиться

Простое объяснение: Рассмотрите for оператор

for item in iterable:
   do_stuff()

Много времени, все объекты в iterable не должны быть там от запуска, но могут быть сгенерированы на лету, поскольку они требуются. Это может быть намного более эффективно в оба

  • пространство (Вы никогда не должны хранить все объекты одновременно), и
  • время (повторение может закончиться, прежде чем все объекты необходимы).

Другие времена, Вы даже не знаете всех объектов загодя. Например:

for command in user_input():
   do_stuff_with(command)

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

def user_input():
    while True:
        wait_for_command()
        cmd = get_command()
        yield cmd

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

18
ответ дан dF. 23 November 2019 в 04:46
поделиться

Буферизация. Когда эффективно выбрать данные в больших блоках, но обработать его в маленьких блоках, тогда генератор мог бы помочь:

def bufferedFetch():
  while True:
     buffer = getBigChunkOfData()
     # insert some code to break on 'end of data'
     for i in buffer:    
          yield i

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

26
ответ дан Rafał Dowgird 23 November 2019 в 04:46
поделиться

Посмотрите раздел "Motivation" в PEP 255.

А неочевидное использование генераторов создает прерываемые функции, который позволяет Вам сделать, вещам нравится, обновляют UI или выполняют несколько заданий "одновременно" (чередованный, на самом деле), не используя потоки.

41
ответ дан Nickolay 23 November 2019 в 04:46
поделиться

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

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

, Если у Вас есть функция fibonacci-up-to-n как это:

# function version
def fibon(n):
    a = b = 1
    result = []
    for i in xrange(n):
        result.append(a)
        a, b = b, a + b
    return result

можно более легко записать функцию как это:

# generator version
def fibon(n):
    a = b = 1
    for i in xrange(n):
        yield a
        a, b = b, a + b

функция более ясна. И если Вы используете функцию как это:

for x in fibon(1000000):
    print x,

в этом примере, при использовании версии генератора, целые 1 000 000 списков объекта не будут созданы вообще, всего одно значение за один раз. Это не имело бы место при использовании версии списка, где список будет создан сначала.

87
ответ дан nosklo 23 November 2019 в 04:46
поделиться

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

Абстрактным примером будет генератор чисел Фибоначчи, который не живет в цикле, и когда он вызывается из любого места, всегда будет возвращать следующее число в последовательности:

def fib():
    first = 0
    second = 1
    yield first
    yield second

    while 1:
        next = first + second
        yield next
        first = second
        second = next

fibgen1 = fib()
fibgen2 = fib()

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

>>> fibgen1.next(); fibgen1.next(); fibgen1.next(); fibgen1.next()
0
1
1
2
>>> fibgen2.next(); fibgen2.next()
0
1
>>> fibgen1.next(); fibgen1.next()
3
5

Прекрасная особенность генераторов в том, что они инкапсулируют состояние, не проходя через циклы создания объектов. Один из способов думать о них как о «функциях», которые запоминают их внутреннее состояние.

Я получил пример Фибоначчи от Генераторы Python - что это такое? , и с небольшим воображением вы можете придумать множество других ситуаций, в которых генераторы создают отличную альтернативу для циклов и других традиционных итерационных конструкций.

21
ответ дан Peter Mortensen 23 November 2019 в 04:46
поделиться
Другие вопросы по тегам:

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