【问题标题】:What is the current value of a Python itertools counterPython itertools 计数器的当前值是多少
【发布时间】:2016-11-01 06:34:54
【问题描述】:

Python (2.7.9) 中的itertools.count 计数器对于线程安全计数非常方便。我怎样才能得到计数器的当前值呢?

每次调用next()时,计数器都会递增并返回最后一个值:

import itertools
x = itertools.count()
print x.next()  # 0
print x.next()  # 1
print x.next()  # 2

到目前为止,一切都很好。

如果不调用next(),我找不到获取计数器当前值的方法,这会产生增加计数器的不良副作用,或者使用repr() 函数。

从上面开始:

print repr(x)  # "count(3)"

所以你可以解析repr() 的输出。类似的东西

current_value = int(repr(x)[6:-1])

可以解决问题,但真的很丑。

有没有办法更直接地获取计数器的当前值?

【问题讨论】:

  • 它在哪里说它是线程安全的?
  • @MattiVirkkunen 我读过this answer,在自己测试之后,看来next(count()) 确实是线程安全的。
  • 测试线程安全并不容易。此外,如果您依靠 GIL 来保证其线程安全,那么与仅使用普通变量和 += 真的有什么区别吗?
  • @Carl 它是在一个名为 count_repr() 的 C 函数中实现的。
  • @MattiVirkkunen += 绝对不是线程安全的。 stackoverflow.com/questions/1717393/…

标签: python itertools


【解决方案1】:

根据documentation 没有办法访问函数的当前值。 itertools.count() 是来自 itertools 模块的 generator 方法。因此,通常的做法是将生成器的当前值简单地分配给变量。

只存储下一次调用的结果:

current_value = x.next()

或(Python版本≥2.6的内置python方法

current_value = next(x)

如果您想添加一些语法糖,您可以创建一个包装函数或实用装饰器类,但分配是标准的。

【讨论】:

    【解决方案2】:

    It 是一个生成器,想要做你想做的事情并不容易。

    如果您想在多个地方使用它的值,我建议您通过.next() 获取一个值并将其存储在一个变量中。如果您担心在这 2 次使用之间计数器可能会增加,则无论如何您都需要将它们都放在 critical section 中。

    如果您不想让这些检查产生的额外 '+1' 污染该计数器,您可以再使用一个计数器来计算检查(也将它放在关键部分)。从前者中减去后者会给你你所需要的。

    另外,你真的确定线程安全吗?文档页面没有关于线程的任何内容。

    【讨论】:

      【解决方案3】:

      使用来源,卢克!

      根据module implementation,这是不可能的。

      typedef struct {
          PyObject_HEAD
          Py_ssize_t cnt;
          PyObject *long_cnt;
          PyObject *long_step;
      } countobject;
      

      当前状态存储在cntlong_cnt 成员中,并且它们都没有暴露在对象API 中。正如您所建议的,唯一可以检索到的地方是对象__repr__

      请注意,在解析字符串时,您必须考虑非单数增量情况。 repr(itertools.count(123, 4)) 等于 'count(123, 4)' - 在这种情况下,您所建议的逻辑将失败。

      【讨论】:

      • 太好了,感谢您很好地解决了这个问题。现在我明白了为什么这是不可能的,至少:-)
      【解决方案4】:

      今天遇到了同样的事情。这是我最终得到的结果:

      class alt_count:
      
          def __init__(self, start=0, step=1):
              self.current = start - step
              self.step = step
      
          def __next__(self):
              self.current = self.current + self.step
              return self.current
      

      应该为您提供几乎所有 itertools.count 功能以及 current 属性。

      i = alt_count()
      print(next(i))  # 0
      print(next(i))  # 1
      print(i.current)  # 1
      

      如果不需要当前值,我发现使用这个简单的closure 也可以。请注意,nonlocal 仅适用于 Python 版本 > 3。

      def alt_next_maker(start=0, step=1):
          res = start - step
      
          def alt_next():
              nonlocal res, step
              res = res + step
              return res
      
          return alt_next
      

      如果您不想使用itertools 模块,可以用作简单的替代方案。

      alt_next = alt_next_maker()
      print(alt_next())  # 0
      print(alt_next())  # 1
      

      文档还提到以下内容是等效的:

      def count(start=0, step=1):
          # count(10) --> 10 11 12 13 14 ...
          # count(2.5, 0.5) -> 2.5 3.0 3.5 ...
          n = start
          while True:
              yield n
              n += step
      

      【讨论】:

        【解决方案5】:

        在不推进迭代器的情况下获得下一个值的另一个技巧是滥用复制协议:

        >>> c = itertools.count()
        >>> c.__reduce__()[1][0]
        0
        >>> next(c)
        0
        >>> c.__reduce__()[1][0]
        1
        

        或者只是从对象副本中获取:

        >>> from copy import copy
        >>> next(copy(c))
        1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-03-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-24
          • 1970-01-01
          • 2014-04-15
          • 2017-03-13
          相关资源
          最近更新 更多