Python局部变量存在,但NameError:“全局名称'tb'未定义”

0 投票
3 回答
1692 浏览
提问于 2025-04-17 21:13

在下面的代码中,我明白 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']

撰写回答