Python嵌套函数变量作用域

Ste*_*liu 82 python variables scope

我已经阅读了关于该主题的几乎所有其他问题,但我的代码仍然不起作用.

我想我错过了一些关于python变量范围的东西.

这是我的代码:

PRICE_RANGES = {
                64:(25, 0.35),
                32:(13, 0.40),
                16:(7, 0.45),
                8:(4, 0.5)
                }

def get_order_total(quantity):
    global PRICE_RANGES
    _total = 0
    _i = PRICE_RANGES.iterkeys()
    def recurse(_i):
        try:
            key = _i.next()
            if quantity % key != quantity:
                _total += PRICE_RANGES[key][0]
            return recurse(_i) 
        except StopIteration:
            return (key, quantity % key)

    res = recurse(_i)
Run Code Online (Sandbox Code Playgroud)

我明白了

"全局名称'_total'未定义"

我知道问题在于_total作业,但我不明白为什么.不recurse()应该访问父函数的变量?

有人可以向我解释一下我对python变量范围缺少什么吗?

moo*_*oos 137

这是一个了解大卫答案精髓的插图.

def outer():
    a = 0
    b = 1

    def inner():
        print a
        print b
        #b = 4

    inner()

outer()
Run Code Online (Sandbox Code Playgroud)

随着声明的b = 4注释,此代码输出0 1,正是您所期望的.

但如果您取消注释该行,print b就会出现错误

UnboundLocalError: local variable 'b' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

似乎神秘的是,存在的b = 4力量会以某种方式b消失在它之前的线上.但David引用的文本解释了为什么:在静态分析期间,解释器确定b被分配给in inner,因此它是一个局部变量inner.打印行在b分配之前尝试打印内部范围.

  • +1我很困惑,但现在我看到会发生什么.我是AC程序员,每当我开始喜欢Python这样的东西就会出现并为我遗址. (27认同)
  • @sudo你能详细说明这个评论吗? (8认同)

Mic*_*man 106

在Python 3中,您可以使用该nonlocal语句访问非本地非全局范围.

  • 这是一个非常重要的贡献!我不知道这个声明。感谢您指出。也许您可以扩展您的答案以包含一个示例。 (7认同)
  • 现在这确实是正确的答案。Python 2 已于 2020 年初消亡,因此 Python3 特定的答案现在几乎适用于所有人。 (3认同)

Dav*_*ebb 56

当我运行你的代码时,我收到此错误:

UnboundLocalError: local variable '_total' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

此问题是由此行引起的:

_total += PRICE_RANGES[key][0]
Run Code Online (Sandbox Code Playgroud)

有关范围和命名空间的文档说明了 这一点:

Python的一个特殊之处在于 - 如果没有global语句生效 - 对名称的赋值总是进入最里面的范围.分配不复制数据 - 它们只是将名称绑定到对象.

因为这条线有效地说:

_total = _total + PRICE_RANGES[key][0]
Run Code Online (Sandbox Code Playgroud)

_total在命名空间中创建recurse().既然_total是新的和未分配的,你不能在添加中使用它.


小智 30

也可以使用函数属性,而不是声明特殊对象或映射或数组.这使变量的范围确实清晰.

def sumsquares(x,y):
  def addsquare(n):
    sumsquares.total += n*n

  sumsquares.total = 0
  addsquare(x)
  addsquare(y)
  return sumsquares.total
Run Code Online (Sandbox Code Playgroud)

当然这个属性属于函数(defintion),而不属于函数调用.所以必须注意线程和递归.

  • 对于python 2,如果注意上面的免责声明,这应该是接受的答案.我发现它甚至允许你嵌套一个可以操纵外部函数状态的信号处理程序 - 我不会暗示 - 只是重构一些神奇可怕的遗留代码的第一步,这些代码到处都有全局变量.现在再次重构,并以正确的方式做到...... (2认同)

Cli*_*nna 16

这是redman解决方案的变体,但使用适当的命名空间而不是数组来封装变量:

def foo():
    class local:
        counter = 0
    def bar():
        print(local.counter)
        local.counter += 1
    bar()
    bar()
    bar()

foo()
foo()
Run Code Online (Sandbox Code Playgroud)

我不确定在python社区中以这种方式使用类对象是否被认为是一种丑陋的黑客或正确的编码技术,但它在python 2.x和3.x中运行良好(用2.7.3和3.2.3测试) ).我也不确定这个解决方案的运行时效率.


red*_*man 8

你可能已经得到了你的问题的答案.但我想指出一种方式,我通常使用列表来解决这个问题.例如,如果我想这样做:

X=0
While X<20:
    Do something. ..
    X+=1
Run Code Online (Sandbox Code Playgroud)

我会这样做:

X=[0]
While X<20:
   Do something....
   X[0]+=1
Run Code Online (Sandbox Code Playgroud)

这样,X永远不是局部变量

  • 怎么这么可怕?什么是bettwr解决方案? (9认同)
  • 不,请不要.这是一个可怕而低效的解决方案. (3认同)
  • 请注意,`while`共享其容器的命名空间.所以它不适合你的例子. (3认同)