python作用域 scope
2014-05-15 18:32 youxin 阅读(2112) 评论(0) 编辑 收藏 举报可以先看: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
param
outside of func2. This doesn’t work if you need the default value to be dependent on what params func2 receives. - Use a second variable,
param2
inside 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/