使用python有些年头了,自认为对Python的基本知识很了解了,今天发生的一件事让我对Python有了更多的认识,写成文章做个记录。
同事让我帮忙看以下一段代码,具体内容和函数名字可以不用太过在意,命名上做了一些特殊处理,但是不影响代码逻辑和要表达的意思。
for
循环中当node_type
为fb
时,将FBX对象返回并作为全局变量dut
(node_name
的内容是字符串dut
),接下来print dut
,报错。
def setup_module(module):
with step('Set_setup'):
function_1_run(topo_info)
function_2_run(topo_info)
function_3_run(topo_info, set_resource, 'device_config')
for node in topo_info.nodes:
if node.type == 'fb':
globals()[node.name] = FBX(node.manage_ip,'admin','111111')
print node.name
print globals()[node.name]
print dut
else:
globals()[node.name] = Device()
globals()[node.name].node_info = node
if not get_config_file(topo_info, set_resource, 'device_config'):
test = dut
print test
with cli_ctx(dut) as dut:
dut.cli.cmd_list = \
[
'configure',
'interface fastEthernet 2',
'ip address 192.168.1.5/24'
]
dut.cli.exec_cmd()
log.info('cli_ctx 1 end')
export_firebox_topo(topo_info, set_resource, 'device_config')
错误信息如下:
==================================== ERRORS ====================================
___________________ ERROR at setup of some_module_1 ___________________
module = <module 'some_name_python' from '/path/to/file/some_name_python.py'>
def setup_module(module):
with step('Set_setup'):
function_1_run(topo_info)
function_2_run(topo_info)
function_3_run(topo_info, set_resource, 'device_config')
for node in topo_info.nodes:
if node.type == 'fb':
globals()[node.name] = FBX(node.manage_ip,'admin','111111')
print node.name
print globals()[node.name]
> print dut
E UnboundLocalError: local variable 'dut' referenced before assignment
some_name_python.py:54: UnboundLocalError
========================== 1 error in 155.62 seconds ===========================
同事提到,如果将with cli_ctx as dut
这个block删除掉,代码执行正常。
听到这里,我的第一反应是变量作用域的问题,但是也无法道出其中原委,于是建议同事,将context manager那一段代码改成with cli_ctx as d
,重新尝试一下是否有问题,同时我在网上继续搜索相关的原因,之后由结果和理论结合分析问题的原因。
幸运的是,代码修改以后,执行正常,我也找到了一些文章来解释这个问题,我的第一感觉也没有错,的确是变量作用域的问题,代码在执行过程中,print dut
实际上是在访问Local variable,而不是我们期望的global variable dut
。
参考Python的官方文档和搜索到的资料,总结出具体原因如下。
- 当搜索一个变量的时候,先从局部作用域开始搜索,如果在局部作用域没有找到那个变量,就会在全局变量中找这个变量,如果找不到抛出异常Unbound-LocalError。
- 如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改,那么python会认为它是一个局部变量。因为对变量的定义在代码块以外,当前代码块中没有变量的定义和赋值,所以报错。
- 在我们的代码中,全局变量
dut
虽然创建了,但是由于在函数代码块中,下文中有context managercli_ctx
对变量dut
进行了赋值操作,导致在函数block中,dut
成为了局部变量,而非全局变量。 - 对变量赋值的操作
=
是很明显的语句,其他不是那么明显的赋值操作有:for
循环中的赋值,except
语句中的赋值,with...as...{var}
中的var
。
大坑啊,基础不够牢靠还是。
Reference
Python 2.7.13 Documentation - Language Reference - 4. Execution Model
Stackoverflow - Short Description of the Scoping Rules?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。