В чем преимущество использования 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
Вопросы:
- Что здесь означает дополнительное состояние?
- В чем преимущество использования
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)