【问题标题】:What is this strange behavior in exception handling?异常处理中的这种奇怪行为是什么?
【发布时间】:2017-03-30 00:09:34
【问题描述】:

我正在使用 python 2.7,但尝试编写一个代码来检查对象是否也是与 python 3+ 兼容的 basestring 的子类。我尝试遵循suggested here的方法,发现过程中有一个我不理解的行为

如果我这样做:

def foo():
    try: basestring
    except NameError:
        print "a"
foo()

什么都没有发生。

如果我在 except 内稍微修改该代码:

def foo():
    try: basestring
    except NameError:
        print "a"
        basestring=str
foo()

然后打印“a”。

我不明白向 except 块添加内容会如何影响异常的触发。

我在函数外检查了相同的代码:

try:
    basestring
except NameError:
    print("a")
    basestring=str

但在这种情况下不会打印任何内容。

【问题讨论】:

    标签: python python-2.7 exception-handling


    【解决方案1】:

    当您将basestring = str 添加到函数时,您是在告诉python basestring 应该被视为局部变量。但是,在执行第一条语句时,没有名称为 basestring 的局部变量(只有一个全局变量),因此 python 引发了一个 UnboundLocalError

    由于UnboundLocalError 继承自NameError,您的异常处理被触发,您将看到a 打印出来。


    如果您对细节感兴趣——我们可以使用dis 将其分解:

    import dis
    
    def foo():
        try:
            basestring
        except NameError:
            print("a")
            basestring=str
    
    def bar():
        try:
            basestring
        except NameError:
            print("a")
    
    dis.dis(foo)
    print('--' * 20)
    dis.dis(bar)
    

    请注意,对于foobasestring 是使用LOAD_FAST 操作码检索的(这意味着它正在寻找一个局部变量)。然而,在bar 中,basestring 是使用LOAD_GLOBAL 操作码检索的。

    【讨论】:

      【解决方案2】:

      在第一种情况下很简单,名称 basestring__builtins__.basestring 上解析。 try 块没有引发异常,因此行为应该符合预期。

      在第二种情况下,这很棘手。在函数内部使用名称 basestring 会使该名称成为函数的局部变量。 请注意,函数的本地名称是在函数定义时确定的。在执行函数的第一行时,Python 已经知道名称basestring 是函数的局部变量。

      >>> def foo():
      ...     basestring
      ...     potato
      ...     errorerrorerror
      ...     
      >>> print foo.func_code.co_names
      ('basestring', 'potato', 'errorerrorerror')
      >>> print foo.func_code.co_varnames
      ()
      

      拨打foo() 将拨打NameError 在线potato。和下面的bar()对比对比一下,就会出NameError就行了basestring

      >>> def bar():
      ...     basestring
      ...     potato
      ...     errorerrorerror
      ...     basestring = "D'Addario EXL160 Medium"
      ...     
      >>> print bar.func_code.co_names
      ('potato', 'errorerrorerror')
      >>> print bar.func_code.co_varnames
      ('basestring',)
      

      因此,引发的异常是由于在绑定到对象之前使用了名称,这是 Python 中的错误。这是运行时的错误,而不是定义时的错误。第三种情况与第一种情况类似——“局部变量”的概念不适用于全局范围。

      【讨论】:

      • "请注意,函数的本地名称是在函数定义时确定的" 这正是我所缺少的!谢谢!
      猜你喜欢
      • 2014-01-21
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多