在__iter __()中使用yield有什么好处?

时间:2017-08-15 02:07:52

标签: python yield-keyword

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使用单独的函数有什么好处?

2 个答案:

答案 0 :(得分:6)

如果你想遵循最佳实践,没有生成器函数,你必须实现这样的东西:

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

答案 1 :(得分:0)

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)