[ANN] Введение в итераторы и генераторы в питоне
От: Mamut Швеция http://dmitriid.com
Дата: 23.02.09 15:52
Оценка: 8 (1)
Здесь: http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/

Статья на английском


dmitriid.comGitHubLinkedIn
pyhon iterators iterables generators article питон статья итераторы генераторы
Re: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 24.02.09 21:29
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Здесь: http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/


M>Статья на английском


Как-то оно все замороченно в Python получилось.

Вот например в TIScript объявляем некую коллекцию-класс.
У него есть два метода возвращающих итераторы:

class Fruits
    {
      function this()
      {
        this._data = ["apple","orange","lime","lemon","pear","banan","kiwi","pineapple"];
      } 
      function forward()
      {
        var items = this._data;
        var idx = -1;
        return function() // return function that supply "next" value 
        {
          if( ++idx < items.length )
            return items[idx];
        }
      }
      function backward()
      {
        var items = this._data;
        var idx = items.length;
        return function()
        {
          if( --idx >= 0 )
            return items[idx];
        }
      }
    }


Используем так например:

var src = new Fruits();

for(var item in src.forward())
  stdout << item << "\n";

for(var item in src.backward())
  stdout << item << "\n";


Т.е. непонятно зачем в Python нужны отдельные классы итераторов.

Вот еще пример функции range(from, to) которая генерирует последовательность [from..to]

function range( from, to )
{
  var idx = from - 1;
  return function() { if( ++idx <= to ) return idx; } 
}


Пользуем так:

for( var item in range(12,24) )
  stdout << item << "\n";


По-моему мой вариант как-то гуманнее что-ли.
Re[2]: [ANN] Введение в итераторы и генераторы в питоне
От: Константин Россия  
Дата: 25.02.09 13:01
Оценка: 1 (1)
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, Mamut, Вы писали:


M>>Здесь: http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/


CS>По-моему мой вариант как-то гуманнее что-ли.


А в чём принципиальное отличие от:
class Fruits(object):
  def __init__(self):
    self.__data=["apple","orange","lime","lemon","pear","banan","kiwi","pineapple"]
  def forward(self):
    idx=0
    while idx < len(self.__data):
      yield self.__data[idx]
      idx=idx+1
  def backward(self):
    idx=len(self.__data)-1
    while idx >= 0:
      yield self.__data[idx]
      idx=idx-1

fruits=Fruits()
for f in fruits.forward():
  print f

for f in fruits.backward():
  print f

def range2(start,end):
  i=start
  while i < end:
    yield i
    i=i+1

for i in range2(12,24):
  print i
Re[3]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 25.02.09 18:04
Оценка:
Здравствуйте, Константин, Вы писали:

CS>>По-моему мой вариант как-то гуманнее что-ли.


К>А в чём принципиальное отличие от:


[skipped]

Отличие в том что я говорил не про генераторы, а про итераторы.

Вот этот пример из статьи

class ByteValue(object):

    def __init__(self, data):
        self.data = data
        self.current_item = 0

    def __iter__(self):
        return self

    def next(self):
        if (self.current_item == len(self.data)):
            raise StopIteration
        else:
            byte_value = ord(self.data[self.current_item])
            self.current_item += 1
            return byte_value


во первых некоректен ибо два таких итератора на одной такой коллекции
приведут к непредсказуемым результатам — self.current_item будет использоваться
во всех for f in bytevalue активных для данного instance.
И непонятно как делать rewind такого итератора.

И вообще iterators и generators представляются взаимоисключающими понятиями.
На самом деле generators достаточно дорогое удовольствие — нужно хранить
их call frames в хипе, а не на стеке. У Python не самый быстрый runtime и так.
К слову tiscript на типовых тестах быстрее Питона (сравнивал с v. 2.X), но
медленне (by design) чем Lua.
Re[4]: [ANN] Введение в итераторы и генераторы в питоне
От: Константин Россия  
Дата: 25.02.09 23:43
Оценка: 36 (1)
Здравствуйте, c-smile, Вы писали:

CS>Отличие в том что я говорил не про генераторы, а про итераторы.

Ну и. Я с помощью генератора создал объект поддерживающий iterator protocol. К слову, нет в python классов итераторов. Есть объекты поддерживающие iterator protocol.

CS>Вот этот пример из статьи

CS>во первых некоректен ибо два таких итератора на одной такой коллекции
CS>приведут к непредсказуемым результатам — self.current_item будет использоваться
CS>во всех for f in bytevalue активных для данного instance.
Не понял. Что конкретно не будет работать? Здесь например всё предсказуемо:
str="hello"
a=ByteValue(str) # current_item для a и b разные
b=ByteValue(str) #

CS>И непонятно как делать rewind такого итератора.
Никак. Для Forward Iterator сделать тоже нельзя, обидно. Но если очень хочется, можешь расширить класс, реализующий iterator protocol
class ByteValue(object):
...
  def restart(self):
    self.current_item=0
...

iter=ByteValue("hello")
for f in iter:
  print f
iter.restart()
for f in iter:
  print f


CS>И вообще iterators и generators представляются взаимоисключающими понятиями.

В python почему-то generators это удобный способ создать объект, реализующий iterator протокол.

CS>На самом деле generators достаточно дорогое удовольствие — нужно хранить

CS>их call frames в хипе, а не на стеке.
Не нашёл дорогих операций:
        case YIELD_VALUE:
            retval = POP();
            f->f_stacktop = stack_pointer;
            why = WHY_YIELD;
            goto fast_yield;
fast_yield:
...
    if (tstate->frame->f_exc_type != NULL)
        reset_exc_info(tstate);
    }
...
    exit_eval_frame:
    Py_LeaveRecursiveCall();
    tstate->frame = f->f_back;

    return retval;


CS>У Python не самый быстрый runtime и так.

CS>К слову tiscript на типовых тестах быстрее Питона (сравнивал с v. 2.X), но
CS>медленне (by design) чем Lua.
Полагаю, что это тема для другого обсуждения.
Re[5]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 26.02.09 08:03
Оценка:
Здравствуйте, Константин, Вы писали:

К>Здравствуйте, c-smile, Вы писали:


CS>>Отличие в том что я говорил не про генераторы, а про итераторы.

К>Ну и. Я с помощью генератора создал объект поддерживающий iterator protocol. К слову, нет в python классов итераторов. Есть объекты поддерживающие iterator protocol.

А что такое iterator protocol? Это атрибуты __iter__ и next как я понимаю?
А кто их создает для function object?

Скажем есть функция:

function Foo(p1) 
{
  if( p1 != 0 ) yield p1;
  return 0;
}


она генератор? (по смыслу она может и не быть никогда генератором)
Если "да" то на какой фазе у ней создаются эти __iter__ и next?

Далее... обычно параметры передаются в функцию на стеке и там же лежит call frame. За для эффективности.
В случае исполнения первого bc_yield в генераторе параметры и local variables должны быть ммм... unstack call frame — перенесены в
heap. Я чего-то это дело не нашел ...

а нашел... PyFrame_New() ... ну в общем да ...

Мало того что абсолютно все call frames (т.е. вызовы питоновских функций)
оказываются живут в С heap (все те же malloc/free) так еще на каждый вызов питоновской функции это на
самом деле вызов Cшной функции (PyEval_EvalFrameEx?) с 140 байт локальных переменных.

И для чего нужно завершать итератор с помощью exception (StopIteration)?
Кто его кстати бросает в случае генератора?

CS>>Вот этот пример из статьи

CS>>во первых некоректен ибо два таких итератора на одной такой коллекции
CS>>приведут к непредсказуемым результатам — self.current_item будет использоваться
CS>>во всех for f in bytevalue активных для данного instance.
К>Не понял. Что конкретно не будет работать? Здесь например всё предсказуемо:
К>
К>str="hello"
К>a=ByteValue(str) # current_item для a и b разные
К>b=ByteValue(str) # 
К>


Блин, забываю какждый раз что в Питоне нет new. Тогда понятно, спасибо за разъяснение.

CS>>На самом деле generators достаточно дорогое удовольствие — нужно хранить

CS>>их call frames в хипе, а не на стеке.
К>Не нашёл дорогих операций:
К>
К>        case YIELD_VALUE:
К>            retval = POP();
            f->>f_stacktop = stack_pointer;
К>            why = WHY_YIELD;
К>            goto fast_yield;
К>fast_yield:
К>...
К>    if (tstate->frame->f_exc_type != NULL)
К>        reset_exc_info(tstate);
К>    }
К>...
К>    exit_eval_frame:
К>    Py_LeaveRecursiveCall();
К>    tstate->frame = f->f_back;

К>    return retval;
К>


Да, уже понял, спасибо. Как оказалось frames в Питоне создаются на каждый вызов в хипе, а не на стеке.
Т.е. yield действительно дешевый в этом случае. Это вызов функций дорогой. И весьма я бы сказал.
Re[6]: [ANN] Введение в итераторы и генераторы в питоне
От: Константин Россия  
Дата: 27.02.09 22:33
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>А что такое iterator protocol? Это атрибуты __iter__ и next как я понимаю?

CS>А кто их создает для function object?
CS>
CS>function Foo(p1) 
CS>{
CS>  if( p1 != 0 ) yield p1;
CS>  return 0;
CS>}
CS>

CS>она генератор? (по смыслу она может и не быть никогда генератором)
CS>Если "да" то на какой фазе у ней создаются эти __iter__ и next?

Она генератор всегда, так как внутри тела есть yield. В случае p1==0 генерируется пустая последовательность. В дополнение, генератор не может возвращать значение, то есть return 0 нужно будет заменить на return.

Инетересное описание по теме: Optimizing Python Generator Functions in the AST (Thomas Lee)
+ ещё немного сказок Iterators and Generators (Alex Martelli)

CS>И для чего нужно завершать итератор с помощью exception (StopIteration)?

Вот что по этому поводу пишет Guido van Rossum
CS>Кто его кстати бросает в случае генератора?
Насколько я понимаю, return и конец метода в этом случае преобразуются в raise StopIteration
Re[7]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 28.02.09 01:54
Оценка:
Здравствуйте, Константин, Вы писали:

К>Здравствуйте, c-smile, Вы писали:


CS>>А что такое iterator protocol? Это атрибуты __iter__ и next как я понимаю?

CS>>А кто их создает для function object?
CS>>
CS>>function Foo(p1) 
CS>>{
CS>>  if( p1 != 0 ) yield p1;
CS>>  return 0;
CS>>}
CS>>

CS>>она генератор? (по смыслу она может и не быть никогда генератором)
CS>>Если "да" то на какой фазе у ней создаются эти __iter__ и next?

К>Она генератор всегда, так как внутри тела есть yield. В случае p1==0 генерируется пустая последовательность. В дополнение, генератор не может возвращать значение, то есть return 0 нужно будет заменить на return.


Ну и зря. Технически любую функцию можно превратить в генератор. Причем с zero payload.

К>Инетересное описание по теме: Optimizing Python Generator Functions in the AST (Thomas Lee)

К>+ ещё немного сказок Iterators and Generators (Alex Martelli)

Угу. Так много буковок про в общем-то простую идиому.

CS>>И для чего нужно завершать итератор с помощью exception (StopIteration)?

К>Вот что по этому поводу пишет Guido van Rossum

Гвидо не прав.

A special value has the problem that if a sequence ever
contains that special value, a loop over that sequence will
end prematurely without any warning. If the experience with
null-terminated C strings hasn't taught us the problems this
can cause, imagine the trouble a Python introspection tool
would have iterating over a list of all built-in names,
assuming that the special End value was a built-in name!


Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.

CS>>Кто его кстати бросает в случае генератора?

К>Насколько я понимаю, return и конец метода в этом случае преобразуются в raise StopIteration

Блин, как это все мне напоминает "память больше не ресурс" от слонов известного цвета.
Re[8]: [ANN] Введение в итераторы и генераторы в питоне
От: Кодёнок  
Дата: 01.03.09 09:21
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>>>И для чего нужно завершать итератор с помощью exception (StopIteration)?

К>>Вот что по этому поводу пишет Guido van Rossum

CS>Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.


Ты про None? Как быть со списком [None, None, None, 'Value']?
Re[9]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 01.03.09 20:45
Оценка:
Здравствуйте, Кодёнок, Вы писали:

CS>>Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.


Кё>Ты про None? Как быть со списком [None, None, None, 'Value']?


Я не знаю что такое None в Python. Это то что возвращает return; (пустой return)?
Re[10]: [ANN] Введение в итераторы и генераторы в питоне
От: Константин Россия  
Дата: 02.03.09 09:12
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>>>Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.

CS>Я не знаю что такое None в Python. Это то что возвращает return; (пустой return)?

Это он и есть, вполне себе полноценный объект:
def f(): return
print type(f())

печатает

<type 'NoneType'>

Re[10]: [ANN] Введение в итераторы и генераторы в питоне
От: Кодёнок  
Дата: 02.03.09 10:20
Оценка: 37 (2)
Здравствуйте, c-smile, Вы писали:

CS>>>Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.

Кё>>Ты про None? Как быть со списком [None, None, None, 'Value']?
CS>Я не знаю что такое None в Python. Это то что возвращает return; (пустой return)?

Да, и результат функции всегда можно класть в список, даже у тебя в TIScript:

    function f()
    {
    }

    var a = [f(), f(), f()]; // [nothing,nothing,nothing]


def f():
    return

a = [f(), f(), f()]
print a # [None, None, None]
Re[11]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 02.03.09 21:23
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>Здравствуйте, c-smile, Вы писали:


CS>>>>Есть такое value называется void или nothing. Технически оно наличествует всегда. Надо было его использовать.

Кё>>>Ты про None? Как быть со списком [None, None, None, 'Value']?
CS>>Я не знаю что такое None в Python. Это то что возвращает return; (пустой return)?

Кё>Да, и результат функции всегда можно класть в список, даже у тебя в TIScript:


Кё>
Кё>    function f()
Кё>    {
Кё>    }

Кё>    var a = [f(), f(), f()]; // [nothing,nothing,nothing]
Кё>


Да действительно это сейчас можно сделать, но ... !

Есть четыре байткода (у меня) которые записывают значения для хранения:
BC_ESET (offset,value) - сохранение в локальную переменную/параметр функции.
BC_VSET (index,value) - сохранение в элемент массива.
BC_PSET (key,value) - сохранение в property объекта.
BC_GSET (key,value) - сохранение в global/namespace объекте.

достаточно изменить эти методы на предмет проверки на VM::nothingValue и этот
самый nothing никогда не появится в коллекциях и значениях переменных, т.е. в enumerable things.

Вот простая функция которая это делает у меня:
inline value value_to_set(value t) { return t == VM::nothingValue? VM::undefinedValue : t; }


Т.е. все 'nothing' становится non-storable value, при сохранении заменяется на 'undefined'
Короче не rocket science.
Re[12]: [ANN] Введение в итераторы и генераторы в питоне
От: Кодёнок  
Дата: 03.03.09 08:40
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Т.е. все 'nothing' становится non-storable value, при сохранении заменяется на 'undefined'

CS>Короче не rocket science.

Хз Как быть с:

a[0] = f()
assert(a[0] == f()) // should fail?
Re[13]: [ANN] Введение в итераторы и генераторы в питоне
От: c-smile Канада http://terrainformatica.com
Дата: 03.03.09 17:29
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>Здравствуйте, c-smile, Вы писали:


CS>>Т.е. все 'nothing' становится non-storable value, при сохранении заменяется на 'undefined'

CS>>Короче не rocket science.

Кё>Хз Как быть с:


Кё>
Кё>a[0] = f()
Кё>assert(a[0] == f()) // should fail?
Кё>


А вот это вопрос философический.

1) Во первых никто не обещает что a[0] = anything; a[0] == anything -> true
Set an element of a collection by index/key это вычисляемая операция.
Т.е. value может в принципе замещено каким-то другим в коде.
Т.е. on "should fail?" answer is "it may fail".

2) В принципе можно рассмотреть логическую систему в которой
undefined == <nothing>, но вместе с тем undefined === <nothing> -> false

3a) Вариант А: в случае присвоения something = <nothing> можно бросать исключение.
3b) Вариант B: Сравнение something == <nothing> приводит к исключению. Ибо nothing это неопределенность.

Что лучше?

Я лично склоняюсь к #2
Re: [ANN] Введение в итераторы и генераторы в питоне
От: kronos_vano  
Дата: 07.03.09 06:08
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Здесь: http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/


M>Статья на английском

На питоне можно как-нить аналогично:
[1,2,3].each do |x|
  puts x
end
power = lambda {|x| puts x*x }
mul_2 = lambda {|x| puts x+x }
[1,2,3].each(&power)
[1,2,3].each(&mul_2)
Re[14]: [ANN] Введение в итераторы и генераторы в питоне
От: Кодёнок  
Дата: 07.03.09 09:53
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>А вот это вопрос философический.

CS>1) Во первых никто не обещает что a[0] = anything; a[0] == anything -> true

Вполне естественно ожидать, что после a = b будет истинно a == b. Если кто-то допустим сделает из “a” property, самопроизвольно меняющей значение на рандом, то это просто нехорошо.

CS>2) В принципе можно рассмотреть логическую систему в которой

CS>3a) Вариант А: в случае присвоения something = <nothing> можно бросать исключение.
CS>3b) Вариант B: Сравнение something == <nothing> приводит к исключению. Ибо nothing это неопределенность.
CS>Что лучше?

Я не понимаю, зачем в языке три значения «нет информации» — null, undefined, nothing (в Javascript вроде два? nothing нет; по мне, тоже не очень). В питоне как-то логичнее — если чего-то нет, то его нет, к нему нельзя получить доступ (a[b] → исключение, если ключа b нет в словаре), если отсутствие надо обозначить (null), используется None (a[b] = None, если надо специально что-то сохранить), других вариантов нет.
Re[2]: [ANN] Введение в итераторы и генераторы в питоне
От: Кодёнок  
Дата: 07.03.09 10:08
Оценка:
Здравствуйте, kronos_vano, Вы писали:

_>На питоне можно как-нить аналогично:

_>[1,2,3].each do |x|
_> puts x
_>end
_>power = lambda {|x| puts x*x }
_>mul_2 = lambda {|x| puts x+x }
_>[1,2,3].each(&power)
_>[1,2,3].each(&mul_2)

Это вопрос или утверждение?
for x in [1,2,3]:
    print(x)
power = lambda x: print(x*x)
mul_2 = lambda x: print(x+x)
list(map(power, [1,2,3])) # если нужно значение
all(map(power, [1,2,3])) # просто вычислить все
for x in map(mul_2, [1,2,3]): pass # более читабельно
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.