1. Unnamed

    24.12.2009

    0 ↑
    0 ↓
    Как вы думаете, имеет ли смысл использовать генератор в качестве обычной функции?
    ——————
    Пример:
    ——————
    from random import choice

    data = 'I have a lot of money.'

    def gen_as_func(choice=choice):
    global data
    while 1:
    yield choice((data.upper, data.lower, data.strip, data.swapcase, data.title))()
    gen = gen_as_func().next

    def foo(choice=choice):
    global data
    return choice((data.upper, data.lower, data.strip, data.swapcase, data.title))()

    for i in xrange(5):
    print foo()
    print gen()
    ——————
    Недостаток такого подхода заключен, как это часто водится, в его достоинстве:
    аргументы генератор может принять только один раз, при инициализации, но ведь и не всякая функция обязательно должна принимать аргументы.
    Зато такой подход просто обязан быть быстрее.
    Правда я не заметил особого ускорения. :-(
  2. Иван Сагалаев

    24.12.2009

    1 ↑
    0 ↓

    Я бы сказал, что ответ как раз зависит от того, нужна ли эта самая инициализация состояния с сохранением. Если нужна, то генератор часто удобен. Но тогда непонятно, зачем его ".next()" скрывать за вызовом функции. А если не нужно хранить состояние, то и генератор писать незачем. По скорости разницы не будет, и это выливается просто в очень странный способ делать простую вещь.

    P.S. Оборачивайте код в [code]..[/code], чтобы он читался.

  3. Unnamed

    24.12.2009

    0 ↑
    0 ↓
    Спасибо, Иван.
    Моё предположение о том, что вариант с генератором будет быстрее, основывалось на следующих выкладках(довольно туманных, честно говоря, и в основном позаимствованных из прочитанной документации):
    - На каждый отдельный "чих" в Python создается объект кадра(frame), вдобавок к этому вызов функции довольно дорог, в-частности необходимо анализировать позиционные параметры и параметры по умолчанию.
    - В случае с генератором ничего этого нет: всё аргументы анализируются один раз, а объект кадра, как я понимаю, просто ждет возобновления.
    Должно быть эта экономия весьма крохотная :-).
  4. К слову говоря, функцию можно ускорить с помощью psyco, генератор - нет.

  5. Иван Сагалаев

    24.12.2009

    1 ↑
    0 ↓

    В случае генератора вы зато получаете потерю на lookup метода ".next" в словаре объекта генератора. Тут нельзя так прикидочно оценить, что быстрее. Надо делать изолированные тесты и мерять. А вернее, не надо :-) Питон, всё таки, не про это язык.

  6. Unnamed

    24.12.2009

    1 ↑
    0 ↓
    Против лишних движений как раз и направлена строка
    gen = gen_as_func().next
    .
    Создается локальная(в данном примере она одновременно и глобальная) ссылка на метод "next" генератора для прямого доступа к нему, без поиска по словарю.
    Чего же боле? :-)
    P.S.: мерять - не хочется, Python действительно не про это язык.
    Хотя, с другой стороны, если что-то можно сделать быстрее и это "бесплатно", то... so-so-la-la, как говаривал Фридрих Ницше (шутка).
  7. Иван Сагалаев

    24.12.2009

    0 ↑
    0 ↓
    gen = gen_as_func().next
    

    А, не углядел.

  8. Нуб

    25.12.2009

    0 ↑
    0 ↓
    Проясните для меня данный пример
    def foo(choice=choice):
    global data
    Зачем тут явно определять глобальную переменную, ведь мы не изменяем ее в функции,
    а если так, то должено использоваться имя "вызывающего" пространства имен?
    И второе, зачем явно передавать choice в функцию, ведь тут аналогичная ситуация.
  9. Не хочется никого обижать, но, на мой взгляд, разумное объяснение одно: bad coding style.
  10. Unnamed

    25.12.2009

    1 ↑
    0 ↓

    Простите, мне не понятно чему дается это самое разумное объяснение. По поводу "global data": когда начинал писать пример, задумывал его по другому и это имело смысл, а в итоге написал по-другому, и в таком варианте эта строка действительно не нужна.

    зачем явно передавать choice в функцию

    Кажется, Вы не совсем поняли: это не передача, а так называемая "заморозка" переменной в функции. В данном случае при определении функции создается локальная переменная choice со ссылкой на функцию choice модуля math, импортированную в глобальном пространстве имен. Доступ к локальным переменным всегда быстрее по сравнению с доступом к глобальным переменным. Это стандартный питоний подход. Но, признаю, здесь это лишняя мера. Просто такие вещи со временем делаются уже на автомате, и перестаешь обращать на них внимание :-)

  11. elephantum

    29.12.2009

    0 ↑
    0 ↓
    • На каждый отдельный "чих" в Python создается объект кадра(frame), вдобавок к этому вызов функции довольно дорог, в-частности необходимо анализировать позиционные параметры и параметры по умолчанию.
    • В случае с генератором ничего этого нет: всё аргументы анализируются один раз, а объект кадра, как я понимаю, просто ждет возобновления.

    автор тут замечает "чих" как вызов функции foo, но отчего-то не замечает "чих" как вызов функции .next. при этом у .next как раз есть неявный позиционный параметр self.

    короче, не экономьте на спичках, всеравно ваш код 90% времени будет проводить в сети или в базе, если это, конечно, прикладной код.

  12. Unnamed

    29.12.2009

    0 ↑
    0 ↓

    @elephantum

    автор тут замечает "чих" как вызов функции foo, но отчего-то не замечает "чих" как вызов функции .next.

    Код не прикладной, иначе эта экономия на спичках действительно была бы смешной. Я хотел выяснить именно идейную чистоту. А основной упор я делал на том, что создается только один объект кадра. Это крохи, да, я согласен, но если уж раскрывать суть, то максимально (насколько это в моих силах). Спасибо за поправку-уточнение.

  13. Deepwalker

    16.01.2010

    0 ↑
    0 ↓
    А почему генератор только раз может принять данные? А как же send? : )

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.