Python局部变量存在,但NameError:“全局名称'tb'未定义”
在下面的代码中,我明白 tb 没有被定义,但当我使用 pdb 时,我看到 tb 是被定义的,并且在 populatingNodeSlotPort 函数的局部范围内,locals 也被填充了很多值。
这是 scenarioUniform1PartA.py 文件
import json
import sbt
import sys
sys.path.append('staging/mgmt/ifc/ishell/')
def Get_Test_Block():
import inspect
for Entry in inspect.stack():
Args = inspect.getargvalues(Entry[0])
# print 'Args is ', Args
if Args[3].has_key('self') and hasattr(
Args[3]['self'],'Test_Block_Yaml_Data'):
return Args[3]['self']
raise ValueError, 'Could not determine current test block.'
def load(scenarioObj):
scenarioInit(scenarioObj)
log = logging.getLogger('sbt')
vmTopo = []
readScale(scenarioObj)
tb = Get_Test_Block()
tb.Harness_Object.Build_Mo_Objects()
# manipulating values of some variables here
return locals()
接下来是 scenarioUniform1.py 文件
from scenarioUniform1PartA import *
def populatingNodeSlotPort(scenarioObj):
for k,v in load(scenarioObj).items():
vars()[k] = v
tbType = 'unknown'
import pdb;pdb.set_trace()
tbType = tb.Nodes.Get_Nodes_By_Type(Defines.NODE_TYPE_LEAF)[0]['Mode']
pdb 回溯信息
> /local/vchauhan/updatedBox/mgmt.git/test/system/sbt/scenarioUniform1.py(28)populatingNodeSlotPort()
-> tbType = tb.Nodes.Get_Nodes_By_Type(Defines.NODE_TYPE_LEAF)[0]['Mode']
(Pdb) print tb
<Classes_ScenarioLoad.ScenarioLoad object at 0x49dab50>
(Pdb) locals()['tb']
<Classes_ScenarioLoad.ScenarioLoad object at 0x49dab50>
(Pdb) n
NameError: "global name 'tb' is not defined"
> /local/vchauhan/updatedBox/mgmt.git/test/system/sbt/scenarioUniform1.py(28)populatingNodeSlotPort()
供参考
(Pdb) locals()
{'vmTopologyFileName': '/tmp/vmm_topos.yml', 'new_size': 1, 'nodeToBeDecomCom': 3, 'vmTopo': [{'VCenter_List': [{'Name': 'vserver51-222'}], 'ESX_List': [{'VCenter': 'vserver51-222', 'Name': 'esx51-443'}, {'VCenter': 'vserver51-222', 'Name': 'esx51-444'}], 'Connection_List': [{'Dest': 'leaf1', 'Source': 'esx51-443'}, {'Dest': 'leaf2', 'Source': 'esx51-444'}], 'VShield_List': [{'Name': 'vshield51-222'}], 'VM_List': [{'Name': 'c6-222'}, {'Name': 'd6-222'}]}], 'size': 4, 'vmTopoName': 'vchauhan', 'log': <logging.Logger instance at 0x30abb90>, 'scenarioObj': <sbt.Scenario object at 0x337c790>, 'nodeId': 1, 'nodeToBeShutdown': 2, 'clusterShardInstanceStepCounter': 4, 'unWiredIfcCount': 2, 'tb': <Classes_ScenarioLoad.ScenarioLoad object at 0x49dab50>, 'applyOnIfc': 1, 'halfUnWiredIfcCount': 2, 'Command_String': '../../tools/res.py sh -s vchauhan', '__exception__': (<type 'exceptions.NameError'>, "global name 'tb' is not defined"), 'tbType': 'unknown', 'Output': '--------------------------------------------------------------------------------\n6 are in production\n--------------------------------------------------------------------------------\nDevice Name Type IP Reserved By Other Info\n--------------------------------------------------------------------------------\nvserver51-222 vcenter 192.168.82.249 vchauhan \nesx51-443 esx 192.168.81.145 vchauhan multiPnic\nesx51-444 esx 192.168.81.146 vchauhan multiPnic\nvshield51-222 vshield 192.168.81.162 vchauhan \nc6-222 pc 0.0.0.0 vchauhan \nd6-222 pc 0.0.0.0 vchauhan \n--------------------------------------------------------------------------------\n\n', 'wiredIfcCount': 1, 'wiNodeStepCounter': 20, 'i': 2, 'k': 'applianceClusterPolStepCounter', 'temp_size': 1, 'v': 100, 'pdb': <module 'pdb' from '/usr/lib64/python2.6/pdb.pyc'>, 'applianceClusterPolStepCounter': 100}
3 个回答
0
如果你想动态地修改变量,建议使用 globals
而不是 locals
。因为修改 locals
这个字典并不能保证会有任何效果。在 CPython 中,修改 locals
确实不会有任何作用。
1
首先,你在调试这个问题时遇到困难,正是说明了你不应该这样做的原因。为什么要把变量注入到 locals()
里呢?直接使用 load(scenarioObj)
给你的那个 dict
就好了。
my_dict = load(scenarioObj)
my_dict['tb'].Nodes.whatever...
同样的道理,尽量不要在 load
里返回 locals()
。要明确返回你需要的数据,格式可以是字典、namedtuple
或者自定义类,随便你。
之所以会出现这个问题,是因为你在“聪明反被聪明误”。想想这两个看起来相似的函数:
def f():
x = []
x.append(1)
def g():
locals()['x'] = [] #n.b. locals() is equivalent to vars()
x.append(1)
看起来是一样的,对吧?其实不是。
dis.dis(f)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (x)
3 6 LOAD_FAST 0 (x) #NOTE: loading x as a LOCAL
9 LOAD_ATTR 0 (append)
12 LOAD_CONST 1 (1)
15 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
18 POP_TOP
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
dis.dis(g)
2 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (locals)
6 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
9 LOAD_CONST 1 ('x')
12 STORE_SUBSCR
3 13 LOAD_GLOBAL 1 (x) #NOTE: loading x as a GLOBAL
16 LOAD_ATTR 2 (append)
19 LOAD_CONST 2 (1)
22 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
看看这两行注释的代码——第一个函数认为 x
是局部变量,而第二个函数认为 x
是全局变量。Python 解释器假设如果你在函数内部没有明确给 x
赋值,然后就调用它的方法,那你一定是在指全局变量。值得注意的是,解释器在编译函数的时候就决定了 x
是局部还是全局,而这个过程发生在你实际调用函数之前。所以在那个时候,解释器根本无法检测到动态注入变量到 locals
的情况。
1
你确定想要玩这种把函数返回的值自动修改本地变量的游戏吗?
为什么不直接返回一个 dict
(字典)然后用里面的值呢:
def load(scenarioObj):
return {'tb': tb, ...}
...
def populatingNodeSlotPort(scenarioObj):
data = load(scenarioObj)
tb = data['tb']
tbType = tb.Nodes.Get_Nodes_By_Type(Defines.NODE_TYPE_LEAF)[0]['Mode']