【问题标题】:Simple function that returns a number incremented by 1 on each call, without globals?一个简单的函数,在每次调用时返回一个递增 1 的数字,没有全局变量?
【发布时间】:2016-11-22 03:51:48
【问题描述】:

我正在尝试编写一个 python 函数,它在第一次调用时返回 1。在第二次调用时,返回 2。在第三次调用时,返回 3。等等。

目前,我已经使用全局变量实现了这一点:

index = 0

def foo():
    global index
    index += 1
    return index

3次调用函数时:

print(foo())
print(foo())
print(foo())

它返回预期的值:

1
2
3

但是,我了解到使用全局变量是不好的做法。所以,我想知道不使用全局变量是否可以达到相同的结果。

有什么建议吗?

感谢您的帮助。

【问题讨论】:

    标签: python increment


    【解决方案1】:

    可以使用函数属性:

    def f():
        f.counter = getattr(f, 'counter', 0) + 1
        return f.counter
    

    或闭包:

    def w():
        counter = 0
        def f():
            nonlocal counter
            counter += 1
            return counter
        return f
    

    【讨论】:

    • nonlocal 是 Python3 唯一的 IIRC。 wrt/ 到“函数属性”技巧,我绝对不会推荐它,因为它真的很脆弱(在f 定义之后添加g = f; f = lambda : None,你会发现原因)。
    【解决方案2】:

    使用闭包:

    def make_inc():
        val = [0]
        def inc():
            val[0] += 1
            return val[0]
        return inc
    
    inc = make_inc()
    print inc()
    print inc()
    print inc()
    

    使用类(OOPL 中最明显的解决方案):

    class Inc(object):
        def __init__(self):
            self._val = 0
    
        def __call__(self):
            self._val += 1
            return self._val
    
    
    inc = Inc()
    print inc()
    print inc()
    print inc()
    

    使用生成器(不能直接调用,您必须使用.next() 方法):

    def incgen():
        val = 0
        while True:
            val += 1
            yield val
    
    
    inc = incgen()
    print inc.next()
    print inc.next()
    print inc.next()
    

    【讨论】:

    • 你的生成器代码应该使用next(inc); next(inc)而不是inc.next(),因为前者适用于python2.6+和python3,而后者只适用于python2.x
    • @Bakuriu 是的,对不起,我仍然要切换到 py3K
    【解决方案3】:

    我将为 Sergey 的回答提供另一种解决方案:exploit mutable default arguments!

    def f(_=[0]):
        _[0] += 1
        return _[0]
    

    这样做的缺点是用户可能会错误地使用一个参数调用该函数并且它不会收到错误消息。

    【讨论】:

      【解决方案4】:

      你可以使用一个对象吗?

      class Incrementer:
          def __init__(self):
              self.value = 0
      
          def __call__(self):
              print(self.value)
              self.value += 1
      

      然后实例化后就可以像函数一样调用了:

      >>> a = Incrementer()
      >>> a()
      0
      >>>a()
      1
      

      【讨论】:

        【解决方案5】:

        你也可以使用生成器。

        def yrange(n):
            i = 0
            while i < n:
                yield i
                i += 1
        

        输出:

        >>> y = yrange(3)
        >>> y
        <generator object yrange at 0x401f30>
        >>> y.next()
        0
        >>> y.next()
        1
        >>> y.next()
        2
        >>> y.next()
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        StopIteration
        

        【讨论】:

          【解决方案6】:

          您还可以改进生成器,使其更灵活:

          def counter_gen():
              count = 0
              while True:
                  res = yield count
                  count += res
          
          >>> counter = counter_gen()
          >>> counter.send(None) #initialize generator
          >>> 0
          >>> counter.send(1)
          >>> 1
          >>> counter.send(3)
          >>> 4  
          

          【讨论】:

            【解决方案7】:

            除了@bruno desthuilliers 在他的第一个“闭包解决方案”中所做的之外,我想指出的是,使用列表而不是做一些闭包是可以的。

            我的意思是,如果你以这种方式修改它,它会正常工作。

            index = [0]
            
            def foo():
                index[0] = index[0] + 1
                return index[0]
            

            我们使用了一个列表而不是使用一个简单的变量,它不会尝试创建另一个名为 index 的列表,因为要创建一个列表,您需要这样的东西,

            另一个列表= []

            另一个列表=列表(())

            为什么在使用简单变量时会出现问题,

            脚本尝试对变量 index 进行赋值,这会在本地范围内创建一个新名称。然后,当 Python 执行赋值的右侧时,它会在本地范围内找到名称索引,但尚未对其进行任何分配。

            【讨论】:

            • 这个解决方案的问题是它仍然依赖于可变的全局状态,所以它没有解决 OP 的问题。
            猜你喜欢
            • 2019-03-09
            • 2021-12-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多