Python itertools计数器的当前值是多少?

18

在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])

使用这个方法可以解决问题,但是它实在太丑了。

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


它在哪里明确说明它是线程安全的? - Matti Virkkunen
1
测试线程安全并不容易。此外,如果您依赖GIL来保持其线程安全性,那么与仅使用普通变量和+=有什么区别呢? - Matti Virkkunen
@Aya 但是 repr() 的值从哪里来? - Carl
1
@Carl 它是在一个名为 count_repr() 的 C 函数中实现的。 - Aya
1
@MattiVirkkunen += 绝对不是线程安全的。https://dev59.com/tXI-5IYBdhLWcg3wtaxq - Carl
显示剩余3条评论
5个回答

12

获取下一个值而不推进迭代器的另一种方法是滥用复制协议:

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

或者直接从对象副本中获取:

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

太浪费了,我喜欢。 - user6004556

8

使用源代码,Luke!

根据模块实现,这是不可能的。

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)' - 您在问题中提出的逻辑在这种情况下会失败。


1
太好了,谢谢你清楚地解释了这个问题。现在我明白为什么它是不可能的了,至少 :-) - Carl

3
根据文档,无法访问函数的当前值。itertools.count()itertools模块中的一个生成器方法。因此,通常的做法是将生成器的当前值赋值给一个变量。
只需存储下一次调用的结果即可:
current_value = x.next()

或者(Python版本≥2.6的内置Python方法
current_value = next(x)

你可以创建一个包装函数,或者一个实用的装饰器类来增加一些语法糖,但是赋值操作是标准做法。


1

It 是一个生成器,做你想要的事情不会轻松。

如果你想在几个地方使用它的值,我建议通过 .next() 获取一个值并将其存储在变量中。如果你担心计数器在这两个使用之间被增加,你需要将它们都放在 临界区 中。

如果你不想让计数器受到这些检查产生的额外 '+1' 的污染,你可以使用另一个计数器来计算检查次数(也将其放在临界区中)。从前者中减去后者将给你需要的结果。

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


0
今天遇到了同样的问题。这是我最终得到的:
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

如果当前值不需要,我发现使用这个简单的 闭包 也可以。请注意,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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接