python的局部变量_Python局部变量的坑

最近碰到这么个问题,有这么个函数,用来将HTML转义字符变回原来的字符:

Python

def htmlescape(s):

if sys.version_info[0] == 3: # python 3.x

unichr = chr

def replc(match):

dict={'amp':'&','nbsp':' ','quot':'"','lt':'','copy':'©','reg':'®'}

if len(match.groups()) >= 2:

if match.group(1) == '#':

return unichr(int(match.group(2)))

else:

return dict.get(match.group(2), '?')

htmlre = re.compile("&(#?)(\d{1,5}|\w{1,8}|[a-z]+);")

return htmlre.sub(replc, s)

1

2

3

4

5

6

7

8

9

10

11

12

defhtmlescape(s):

ifsys.version_info[0]==3:# python 3.x

unichr=chr

defreplc(match):

dict={'amp':'&','nbsp':' ','quot':'"','lt':'','copy':'©','reg':'®'}

iflen(match.groups())>=2:

ifmatch.group(1)=='#':

returnunichr(int(match.group(2)))

else:

returndict.get(match.group(2),'?')

htmlre=re.compile("&(#?)(\d{1,5}|\w{1,8}|[a-z]+);")

returnhtmlre.sub(replc,s)

其中unichr用来将一个整数转换成Unicode字符,仅在Python2中存在。Python3中,chr可以同时处理ASCII字符和Unicode字符。所以我们在Python3环境中将unichr映射到chr上。

运行这段代码会在第8行报错:NameError: free variable ‘unichr’ referenced before assignment in enclosing scope。而且只有Python2会报错,Python3不会。

首先从问题上看,报错的原因是在闭包replc里unichr没有定义。

但是Python2明明是有unichr这个内置函数的,为啥就变成未定义呢?

为了搞清楚问题,我们用了一个最小化的测试用例:

Python

a = 1

def func():

if False:

a = 2

print(a)

1

2

3

4

5

a=1

deffunc():

ifFalse:

a=2

print(a)

运行到print那行报错“UnboundLocalError: local variable ‘d’ referenced before assignment”。我们注意到这时报错的是local variable没有定义。明明a之前是一个全局变量而且if根本不会执行啊。于是我们用dis模块来打印func函数的字节码:

3 0 LOAD_GLOBAL 0 (False)

3 POP_JUMP_IF_FALSE 15

4 6 LOAD_CONST 1 (2)

9 STORE_FAST 0 (a)

12 JUMP_FORWARD 0 (to 15)

5 >> 15 LOAD_FAST 0 (a)

18 PRINT_ITEM

19 PRINT_NEWLINE

20 LOAD_CONST 0 (None)

23 RETURN_VALUE

1

2

3

4

5

6

7

8

9

10

11

12

30LOAD_GLOBAL0(False)

3POP_JUMP_IF_FALSE15

46LOAD_CONST1(2)

9STORE_FAST0(a)

12JUMP_FORWARD0(to15)

5>>15LOAD_FAST0(a)

18PRINT_ITEM

19PRINT_NEWLINE

20LOAD_CONST0(None)

23RETURN_VALUE

确实python用了LOAD_FAST,说明print的是一个局部变量a。这看起来可能很难以理解,但其实大家一定碰到过下面这种情况:

Python

a = 1

def func():

a = 2

print(a)

1

2

3

4

a=1

deffunc():

a=2

print(a)

这段代码会在第三行报错,同样是UnboundLocalError。因为Python规定,在函数内仅被引用的变量默认为全局变量;如果在函数内被赋值,则默认为局部变量,除非有global关键词。而且显然,这个规则是在编译(生成字节码)时实现的,而不是在运行时确定的。

这个规则有人称为LEGB规则 (Local, Enclosed, Global, Builtin),就是说依次从局部,闭包,全局,内置的命名空间里寻找名字。Python的编译器在编译时按照这一规则决定究竟从哪里找变量。

在我们一开始的例子里,因为unichr在htmlescape这个局部作用域内出现了,所以Python认为可以从htmlescape的局部变量表里取到它。但是在Python2上它未被赋值,所以出现了NameError。

有了这个设定,有时我们可以用它来unset一个全局变量。(虽然好像没什么卵用。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值