Структурное программирование и генераторы Python?

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

6
задан Alice Purcell 2 December 2012 в 00:03
поделиться

7 ответов

Однако я бы хотел Критерии повторного использования на ступень сложнее: что, если мне нужна структура управления вокруг моего повторяющегося поколения?

itertools часто помогает даже там - вам нужно привести конкретные примеры, когда, по вашему мнению, это не так.

Для например, я мог бы назвать субгенератор навсегда с разными параметры.

itertools.chain.from_iterable .

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

И chain , и chain_from_iterable делают это - ни один суб-итератор не «запускается» до того момента, пока не потребуется первый элемент из него.

Или (и это настоящий желание) Я могу изменить то, что я делаю далее в зависимости от того, что мой контроллер передает мне с помощью send ().

Будем признательны за конкретный пример. В любом случае, в худшем случае, вы будете кодировать для x в blargh: yield x , где приостановленный Pep3080 позволит вам кодировать yield from blargh - около 4 дополнительных символов (не трагедия ; -).

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

Например, предположим, что мы часто делаем что-то вроде: сначала вывести определенное значение; затем многократно, если мы отправили 'foo', вывести следующий элемент из fooiter, если 'bla', из blaiter, если 'zop', от зопитер, что угодно, от дефитера. Как только мы обнаруживаем второе появление этого композиционного паттерна, мы можем закодировать:

def corou_chaiters(initsend, defiter, val2itermap):
  currentiter = iter([initsend])
  while True:
    val = yield next(currentiter)
    currentiter = val2itermap(val, defiter)

и вызывать эту простую композиционную функцию по мере необходимости. Если нам нужно составить другие сопрограммы, а не общие итераторы, у нас будет немного другой композитор, использующий метод send вместо следующей встроенной функции; и т. д.

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

def corou_chaiters(initsend, defiter, val2itermap):
  currentiter = iter([initsend])
  while True:
    val = yield next(currentiter)
    currentiter = val2itermap(val, defiter)

и вызывать эту простую композиционную функцию по мере необходимости. Если нам нужно составить другие сопрограммы, а не общие итераторы, у нас будет немного другой композитор, использующий метод send вместо следующей встроенной функции; и т. д.

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

def corou_chaiters(initsend, defiter, val2itermap):
  currentiter = iter([initsend])
  while True:
    val = yield next(currentiter)
    currentiter = val2itermap(val, defiter)

и вызывать эту простую композиционную функцию по мере необходимости. Если нам нужно составить другие сопрограммы, а не общие итераторы, у нас будет немного другой композитор, использующий метод send вместо следующей встроенной функции; и т. д.

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

11
ответ дан 8 December 2019 в 02:52
поделиться

Вы хотите связать несколько итераторов вместе:

from itertools import chain

def sillyGenerator(a,b,c):
    return chain(quadraticRange(a),quadraticRange(b),quadraticRange(c))
6
ответ дан 8 December 2019 в 02:52
поделиться

Непрактичный (к сожалению) ответ:

from __future__ import PEP0380

def sillyGenerator():
    yield from quadraticRange(10)
    yield from quadraticRange(12)
    yield from quadraticRange(8)

Потенциально практическая ссылка: Синтаксис для делегирования субгенератору

К сожалению, это непрактично: Мораторий на язык Python

ОБНОВЛЕНИЕ Фев 2011:

Мораторий снят, и PEP 380 включен в список TODO для Python 3.3. Надеюсь, этот ответ скоро станет практическим.

Прочтите замечания Гвидо о comp.python.devel

6
ответ дан 8 December 2019 в 02:52
поделиться
import itertools

def quadraticRange(n):
  for i in xrange(n)
    yield i*i

def sillyGenerator():
  return itertools.chain(
    quadraticRange(10),
    quadraticRange(12),
    quadraticRange(8),
  )

def sillyGenerator2():
  return itertools.chain.from_iterable(
    quadraticRange(n) for n in [10, 12, 8])

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

3
ответ дан 8 December 2019 в 02:52
поделиться

Существует предложение по усовершенствованию Python для предоставления yield from оператора для «делегирования генерации». Ваш пример мог бы быть записан как:

def sillyGenerator():
  sq = lambda i: i * i
  yield from map(sq, xrange(10))
  yield from map(sq, xrange(12))
  yield from map(sq, xrange(8))

Или лучше, в духе DRY:

def sillyGenerator():
  for i in [10, 12, 8]:
    yield from quadraticRange(i)

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

3
ответ дан 8 December 2019 в 02:52
поделиться

Для произвольного количества вызовов quadraticRange :

from itertools import chain

def sillyGenerator(*args):
    return chain(*map(quadraticRange, args))

Этот код использует map и itertools.chain . Он принимает произвольное количество аргументов и передает их в порядке quadraticRange . Затем полученные итераторы связываются в цепочку.

2
ответ дан 8 December 2019 в 02:52
поделиться

Существует паттерн, который я называю "ядро генератора", где генераторы не уступают непосредственно пользователю, а относятся к какому-то циклу "ядра", который рассматривает (некоторые) их выходные данные как "системные вызовы" со специальным значением.

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

import functools, types

def flatten(values_or_generators):
    for x in values_or_generators:
        if isinstance(x, GeneratorType):
            for y in x:
                yield y
        else:
            yield x

# Better name anyone?
def subgenerator(g):
    """Decorator making ``yield <gen>`` mean ``yield from <gen>``."""

    @functools.wraps(g)
    def flat_g(*args, **kw):
        return flatten(g(*args, **kw))
    return flat_g

и тогда вы сможете просто написать:

def quadraticRange(n):
    for i in xrange(n)
        yield i*i

@subgenerator
def sillyGenerator():
    yield quadraticRange(10)
    yield quadraticRange(12)
    yield quadraticRange(8)

Обратите внимание, что subgenerator() разворачивает ровно один уровень иерархии. Вы можете легко сделать его многоуровневым (управляя ручным стеком, или просто заменив внутренний цикл на for y в flatten(x): - но я думаю, что так будет лучше, так что каждый генератор, который хочет использовать этот нестандартный синтаксис, должен быть явно обернут @subgenerator.

Обратите также внимание, что определение генераторов несовершенно! Оно обнаруживает вещи, написанные как генераторы, но это деталь реализации. Как собеседник генератора, все, что вас волнует, это то, что он возвращает итератор. Это может быть функция, возвращающая какой-нибудь объект итератора, и тогда этот декоратор не будет работать.

Проверка наличия в объекте метода .next() слишком широка - вы не сможете выдавать строки без их разбора. Так что самым надежным способом будет проверка на наличие явного маркера, так что вы напишете, например:

@subgenerator
def sillyGenerator():
    yield 'from', quadraticRange(10)
    yield 'from', quadraticRange(12)
    yield 'from', quadraticRange(8)

Эй, это почти как PEP!

[кредиты: этот ответ дает похожую функцию - но он глубокий (что я считаю неправильным) и не является обрамлением декоратора]

.
1
ответ дан 8 December 2019 в 02:52
поделиться
Другие вопросы по тегам:

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