2016-11-15 153 views
2

我正在使用python 2.7,但試圖做一個代碼來檢查一個對象是否是與python 3+兼容的basestring的子類。我試圖採取的辦法suggested here並在此過程中發現一個行爲,我不明白異常處理中這種奇怪的行爲是什麼?

如果我做的:

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

沒有任何反應。

如果我稍微修改剛內除了代碼:

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

然後 「a」 被打印。

我不明白如何在except塊中添加某些東西會影響異常的觸發。

我查了相同的代碼的功能外:

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

,但沒有被印刷在這種情況下。

回答

3

在第一種情況下,很容易,名稱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錯誤之前使用。這是運行時的錯誤,而不是定義時的錯誤。第三種情況與第一種情況類似 - 「局部變量」的概念不適用於全局範圍。 「

+1

」**請注意,函數本地的名稱是在函數定義時確定的**「 這正是我錯過的!謝謝! – alvarosg

5

當您將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中,使用LOAD_GLOBAL操作碼來檢索basestring

相關問題