В чем преимущество использования yield в __iter__()?

В чем преимущество использования генератора (yield) внутри __iter__() функционировать? После прочтения Python Cookbook я понимаю: "Если вы хотите, чтобы генератор предоставлял пользователю дополнительное состояние, не забывайте, что вы можете легко реализовать его как класс, поместив код функции генератора в __iter__() Метод ".

import io

class playyield:
    def __init__(self,fp):
        self.completefp = fp

    def __iter__(self):
        for line in self.completefp:
            if 'python' in line:
                yield line

if __name__ =='__main__':
    with io.open(r'K:\Data\somefile.txt','r') as fp:
        playyieldobj = playyield(fp)
        for i in playyieldobj:
            print I

Вопросы:

  1. Что здесь означает дополнительное состояние?
  2. В чем преимущество использования yield внутри __iter__ () вместо использования отдельной функции для yield?

1 ответ

Решение

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

In [7]: class IterableContainer:
   ...:     def __init__(self, data=(1,2,3,4,5)):
   ...:         self.data = data
   ...:     def __iter__(self):
   ...:         return IterableContainerIterator(self.data)
   ...:

In [8]: class IterableContainerIterator:
   ...:     def __init__(self, data):
   ...:         self.data = data
   ...:         self._pos = 0
   ...:     def __iter__(self):
   ...:         return self
   ...:     def __next__(self):
   ...:         try:
   ...:              item = self.data[self._pos]
   ...:         except IndexError:
   ...:             raise StopIteration
   ...:         self._pos += 1
   ...:         return item
   ...:

In [9]: container = IterableContainer()

In [10]: for x in container:
    ...:     print(x)
    ...:
1
2
3
4
5

Конечно, приведенный выше пример надуман, но, надеюсь, вы поймете это. С генераторами это может быть просто:

In [11]: class IterableContainer:
    ...:     def __init__(self, data=(1,2,3,4,5)):
    ...:         self.data = data
    ...:     def __iter__(self):
    ...:         for x in self.data:
    ...:             yield x
    ...:
    ...:

In [12]: list(IterableContainer())
Out[12]: [1, 2, 3, 4, 5]

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

In [19]: class IterableContainerIterator:
    ...:     def __init__(self, data):
    ...:         self.data = data
    ...:         self._pos = 0
    ...:     def __iter__(self):
    ...:         return self
    ...:     def __next__(self):
    ...:         try:
    ...:              item = self.data[self._pos]
    ...:         except IndexError:
    ...:             raise StopIteration
    ...:         self._pos += 1
    ...:         return item
    ...:     def rewind(self):
    ...:         self._pos = min(0, self._pos - 1)
    ...:

In [20]: class IterableContainer:
    ...:     def __init__(self, data=(1,2,3,4,5)):
    ...:         self.data = data
    ...:     def __iter__(self):
    ...:         return IterableContainerIterator(self.data)
    ...:

In [21]: container = IterableContainer()

In [22]: it = iter(container)

In [23]: next(it)
Out[23]: 1

In [24]: next(it)
Out[24]: 2

In [25]: it.rewind()

In [26]: next(it)
Out[26]: 1

In [27]: next(it)
Out[27]: 2

In [28]: next(it)
Out[28]: 3

In [29]: next(it)
Out[29]: 4

In [30]: next(it)
Out[30]: 5

In [31]: it.rewind()

In [32]: next(it)
Out[32]: 1
import json
import urllib

serviceurl = 'http://www.py4e.com/code3/geojson.py'

while True:
    address = input("Enter location:")
    if len(address) <1: break

    url = serviceurl+ urllib.urlencode ({"sensor": "false,", "address":address})
    print ("retrieving", url)
    uh = urllib.urlopen(url)
    data = uh.read()
    print ("retrieved", len(data), "characters")
    try: js=json.loads(str(data))
    except: js= None
    if "status" not in js or js['status'] != "OK":
        print ("=== failure to retrieve ===")
        print (data)
        continue

    print (json.dumps(js, indent = 4))

    lat = js["results"] [0] ["geometry"] ["location"]["lat"]
    lng = js["results"] [0] ["geometry"] ["location"]["lng"]
    print ("lat", lat, "lng", lng)
    location = js["results"][0] ["formatted_address"]
    print (location)


Другие вопросы по тегам