
正文
python作用域 scope
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
可以先看:http://www.cnblogs.com/youxin/p/3645734.html
几个概念:
python能够改变变量作用域的代码段是def、class、lamda.
if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的
变量搜索路径是:本地变量->全局变量
作用域搜索规则:
LEGB Rule.
L. Local. (Names assigned in any way within a function (
def
or
lambda
)), and not declared global in that function.
E. Enclosing function locals. (Name in the local scope of any and all enclosing functions (
def
or
lambda
), form inner to outer.
“封闭式”的作用域规则适应于函数定义函数时,也就是说,在函数体内定义了一个新的函数。这个函数体内的函数是外函数的局部命名空间中的一部分,意味着只有在外函数执行期间才能够运行
G. Global (module). Names assigned at the top-level of a module file, or declared global in a
def
within the file.
B. Built-in (Python). Names preassigned in the built-in names module :
open
,
range
,
SyntaxError
,
下面一个程序,运行时为什么报错:
def func1(param=None):
def func2():
if not param:
param = 'default'
print param
# Just return func2.
return func2
if __name__ == '__main__':
func1('test')()
Traceback (most recent call last):
File "test.py", line 11, in
func1('test')()
File "test.py", line 3, in func2
if not param:
UnboundLocalError: local variable 'param' referenced before assignment
为什么?param是func1的。
先看下面的类似例子:
def foo():
m=3
def bar(): a=4
return a+m
return bar
foo()()
运行正常,可是如果加上一句:
if not m:
m=1
立刻报类似错误:UnboundLocalError: local variable 'm' referenced before assignment。
错误的原因是python闭包原理;
def foo():
m=3
n=5
def bar():
a=4
return m+n+a
return bar >>>bar = foo()
>>>bar()
12
cell对象的引入,是为了实现被多个作用域引用的变量。
对每一个这样的变量,都用一个cell对象来保存 其值 。 拿之前的示例来说,m和n既在foo函数的作用域中被引用,又在bar
函数的作用域中被引用,所以m, n引用的值,都会在一个cell对象中。
这两个int型的cell分别存储了m和n的值。
无论是在外部函数中定义,还是在内部函数中调用,引用的指向都是cell对象中的值。
内部函数无法修改cell对象中的值,如果尝试修改m的值,编译器会认为m是函数
bar的局部变量,同时foo代码块中的m也会被认为是函数foo的局部变量,两个m分别在各自的作用域下起作用。
所以我们看到了:
if not param:
param=5
尝试给param赋值,param就成了局部变量,那么之前的if not param就报错,因为param还没有赋值 。还是不清楚为什么?参考:http://www.cnblogs.com/btchenguang/archive/2012/08/29/2662571.html
隔几天有发现了答案:
http://www.cnblogs.com/youxin/p/3383059.html
def logging_decorator(func):
def wrapper():
wrapper.count += 1
print "The function I modify has been called {0} time(s)".format(wrapper.count)
func()
wrapper.count = 0
return wrapper def a_function():
print "I'm a normal function."
你也许正疑惑为什么我们的计数器是wrapper的一个属性而不是一个普通的变量。难道wrapper的闭包环境不是让我们访问在其局部作用域中声明的任意变量么?是的,但有个问题。
但有个问题。Python中,闭包允许对其函数作用域链中任一变量的进行任意读操作,但只允许对可变对象(列表、字典、等等)进行写操作。整数在Python中是非可变对象,因此我们不能修改
wrapper
内部整型变量的值。相反,我们将计数器作为
wrapper
的一个属性—一个可变对象,因此可以随我们自己增大它的值。。
def func1():
param=[1,2]
def func2(): if not param:
param.append(5) print param
#just return func2
return func2 func1()();
程序运行正常。
如果改成:
if not param:
param=[3,4]
还是报同样的错。
所以,闭包对外部的可变变量不能使用=赋值,可以修改。
If you’re curious, you can read about the principles of LEGB. You have to understand a bit about compilers and the AST to get what’s going on behind the scenes. You might think that replacing lines 3-4 with:
param = param or 'default'
Might work. But no. You can’t assign the same parameter at the local level if the enclosing level defines it. Even this fails:
param = param
What to do?
There are a few ways to get around this.
-
Assign
paramoutside of func2. This doesn’t work if you need the default value to be dependent on what params func2 receives. -
Use a second variable,
param2inside of func2 (posted below).
Here is the solution suggested by our commenter Avazu:
def func1(param=None):
def func2(param2=param):
if not param2:
param2 = 'default'
print param2
# Just return func2.
return func2
上面例子参考:http://blog.mozilla.org/webdev/2011/01/31/python-scoping-understanding-legb/





