【问题标题】:python: attributes on a generator objectpython:生成器对象的属性
【发布时间】:2010-10-31 16:36:31
【问题描述】:

是否可以在生成器对象上创建属性?

这是一个非常简单的例子:

def filter(x):
    for line in myContent:
        if line == x:
            yield x

现在说我有很多这样的过滤器生成器对象漂浮在周围......也许其中一些是匿名的......我想稍后再回去询问他们过滤的内容。有没有办法我可以 a) 询问生成器对象的 x 值或 b) 设置具有 x 值的属性,以便以后可以询问?

谢谢

【问题讨论】:

    标签: python


    【解决方案1】:

    是的。

    class Filter( object ):
        def __init__( self, content ):
            self.content = content
        def __call__( self, someParam ):
            self.someParam = someParam
            for line in self.content:
                if line == someParam:
                    yield line
    

    【讨论】:

      【解决方案2】:

      不幸的是,生成器对象(调用生成器函数返回的结果)不支持添加任意属性。您可以通过使用由生成器对象索引的外部字典在某种程度上解决它,因为这些对象 可用作字典的键。所以你喜欢做什么,说:

      a = filter(23)
      b = filter(45)
      ...
      a.foo = 67
      ...
      x = random.choice([a,b])
      if hasattr(x, 'foo'): munge(x.foo)
      

      你可以这样做:

      foos = dict()
      a = filter(23)
      b = filter(45)
      ...
      foos[a] = 67
      ...
      x = random.choice([a,b])
      if x in foos: munge(foos[x])
      

      对于任何更高级的东西,使用类而不是生成器(毕竟,类的一个或多个方法可以是生成器)。

      【讨论】:

        【解决方案3】:

        没有。您不能在生成器上设置任意属性。

        正如 S. Lott 指出的那样,您可以拥有一个 看起来 像生成器,而 作用 像生成器的对象。如果它看起来像一只鸭子,而且行为也像一只鸭子,那么您就已经有了鸭子打字的定义。

        但是,如果没有适当的代理方法,它将不支持像 gi_frame 这样的生成器属性。

        【讨论】:

          【解决方案4】:

          考虑问题,一种让生成器携带一组属性的方法。这有点疯狂——我强烈推荐 Alex Martelli 的建议而不是这个——但它在某些情况下可能有用。

          my_content = ['cat', 'dog days', 'catfish', 'dog', 'catalog']
          
          def filter(x):
              _query = 'I\'m looking for %r' % x
          
              def _filter():
                  query = yield None
                  for line in my_content:
                      while query:
                          query = yield _query
          
                      if line.startswith(x):
                          query = yield line
          
                  while query:
                      query = yield _query
          
              _f = _filter()
              _f.next()
              return _f
          
          for d in filter('dog'):
              print 'Found %s' % d
          
          cats = filter('cat')
          for c in cats:
              looking = cats.send(True)
              print 'Found %s (filter %r)' % (c, looking)
          

          如果您想询问生成器过滤的是什么,只需调用 send 并使用计算结果为 true 的值。当然,这段代码可能太聪明了一半。谨慎使用。

          【讨论】:

            【解决方案5】:

            如果您想查询它们以进行调试,那么以下函数会有所帮助:

            import inspect
            
            def inspect_generator(g):
                sourcecode = open(g.gi_code.co_filename).readlines()
                gline = g.gi_code.co_firstlineno
                generator_code = inspect.getblock(sourcecode[gline-1:])
            
                output = "Generator %r from %r\n" % (g.gi_code.co_name, g.gi_code.co_filename)
                output += "".join("%4s: %s" % (idx+gline, line) for idx, line in enumerate(generator_code))
            
                output += "Local variables:\n"
                output += "".join("%s = %r\n" % (key,value) for key,value in g.gi_frame.f_locals.items())
            
                return output
            
            print inspect_generator(filter(6))
            """Output:
            Generator 'filter' from 'generator_introspection.py'
               1: def filter(x):
               2:     for line in myContent:
               3:         if line == x:
               4:             yield x
            Local variables:
            x = 6
            """
            

            如果您想询问它们以实现功能,那么实现迭代器协议的类可能是一个更好的主意。

            【讨论】:

              【解决方案6】:

              我刚刚在这里写了一个装饰器来做到这一点: http://code.activestate.com/recipes/577057-generator-attributes/

              【讨论】:

                【解决方案7】:

                我意识到这是一个很晚的答案,但是......

                您的代码可以稍后只检查生成器的变量,而不是存储和稍后读取一些附加属性,使用:

                filter.gi_frame.f_locals

                我猜 Ants Aasma 暗示了这一点。

                【讨论】:

                • 我尝试了您的建议来检查生成器的变量。在尚未达到StopIteration 的情况下,它仍然有效。之后,gi_frame 变为 None
                猜你喜欢
                • 1970-01-01
                • 2014-03-01
                • 2015-08-30
                • 1970-01-01
                • 1970-01-01
                • 2016-05-03
                • 2022-11-11
                • 1970-01-01
                • 2011-07-13
                相关资源
                最近更新 更多