為什麼 Python 的類不構成作用域(scope)?
為什麼要區分scope和namespace?到底是從namespace查找變數還是scope
為什麼以下代碼報錯?
第三行會報a沒有定義,所以可以看出搜索變數名a的過程是inner-&>main為什麼跳過了class的命名空間?
class A(object):
a = 3b = list(a + i for i in range(10))
就是為了使用方便,如果class也是scope,class里定義的方法就會成為class的閉包,這在語義上是說不通的,也會造成麻煩,比如__name__不是當前模塊的name而是class的。另外,手工給class添加方法時的特性會和在class語句中創建不同。
現有的幾個回答,你們好像都被題主帶溝里去啦,來,題主你試試簡單的:
class A:
a = 123
b = a
至少在我的環境下可以執行通過,這個問題並不是你所認為的作用域的問題(雖然是另一個作用域問題),類中的代碼執行規則可以看以前的一個回答
冒泡:Python 有哪些好玩的語法糖?
所以,是你這個generator導致的
具體問題就是我上面回答裡面說的,雖然類下的代碼塊在init過程中是作為一個函數來執行,但是和普通函數有幾個區別,其中一個就是:類代碼塊中定義的函數,不視為這個代碼塊「假函數」的閉包,你看:
class A:
a = 123
def g():
print a
g()
效果一樣吧,在調用g的時候也出現了找不到global變數"a"的異常,而你把上面的class A換成def f()然後調用f就沒事,這時候g是f的一個閉包函數,可以引用f的局部變數a
那麼題主代碼明明沒有出現定義函數的代碼,為啥也會有這個問題呢,其實就是因為,那個generator的元素求值部分,是作為一個匿名函數來編譯的,也就是說把"a+i"這個表達式編譯成了一個獨立的code對象,然後這個對象會被generator對象引用,當你遍歷generator的時候,其實是一個個地執行這個小函數來返回值,而這個小函數在編譯的時候,由於不能作為閉包來看待,裡面的"a"自然就跟上面的a沒啥關係,而是作為global var來編譯了
所以你把這行改成"b = [a + i for i in range(10)]"就沒事了,列表解析式是立即求所有元素值,所以不需要一個匿名函數做媒介
與這個問題類似的,還有一個eval的坑,也是作用域混亂,不過我忘了具體常式是啥樣的了,反正是碰到過
==========================
忽然想起eval的坑了,幾個例子如下,原因可以自己分析(均為python2.x):
def f(): #運行拋異常
a = 123
def g():
exec "print a"
g()
def f(): #直接編譯失敗
a = 123
def g():
exec "print a"
print a
g()
def f(): #運行拋異常
a = 123
def g():
print eval("a")
g()
def f(): #可以正常列印兩個123,猜猜為什麼
a = 123
def g():
print eval("a")
print a
g()
因為python很傻,都需要你顯示的處理對應對象
著作權歸作者所有。
商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
作者:劉星宇
鏈接:https://www.cnblogs.com/starliu/p/4707936.html
來源:博客園
Namespace and Scope(命名空間和作用域)
Namespace只是從名字到對象的一個映射(a mapping from name to objects)。大部分namespace都是按Python中的字典來實現的。有一些常見的namespace:built-in中的集合( abs() 函數等)、一個模塊中的全局變數等。從某種意義上來說,一個對象(object)的所有屬性(attribute)也構成了一個namespace。在程序執行期間,可能(其實是肯定)會有多個名空間同時存在。不同namespace的創建/銷毀時間也不同。此外,兩個不同namespace中的兩個相同名字的變數之間沒有任何聯繫。scope有了namespace基礎之後,讓我們再來看看scope。Scope是Python程序的一塊文本區域(textual region)。在該文本區域中,對namespace是可以直接訪問,而不需要通過屬性來訪問。Scope是定義程序該如何搜索確切地「名字-對象」的名空間的層級關係。(The 「scope」 in Python defines the 「hirerchy level」 in which we search namespaces forcertain 「name-to-object」 mappings.)
Tip直接訪問:對一個變數名的引用會在所有namespace中查找該變數,而不是通過屬性訪問。屬性訪問:所有名字後加 . 的都認為是屬性訪問。如 module_name.func_name ,需要指定 func_name 的名空間,屬於屬性訪問。而 abs(-1),abs 屬於直接訪問。兩者之間有什麼聯繫呢?Important在Python中,scope是由namespace按特定的層級結構組合起來的。scope一定是namespace,但namespace不一定是scope.LEGB-rule
在一個Python程序運行中,至少有4個scopes是存在的。直接訪問一個變數可能在這四個namespace中逐一搜索。Local(innermost)包含局部變數。比如一個函數/方法內部。Enclosing包含了非局部(non-local)也非全局(non-global)的變數。比如兩個嵌套函數,內層函數可能搜索外層函數的namespace,但該namespace對內層函數而言既非局部也非全局。 Global(next-to-last)當前腳本的最外層。
比如當前模塊的全局變數。 Built-in(outtermost)Python __builtin__ 模塊。包含了內建的變數/關鍵字等。 那麼,這麼多的作用域,Python是按什麼順序搜索對應作用域的呢?著名的」LEGB-rule」,即scope的搜索順序:ImportantLocal -&> Enclosing -&> Global -&> Built-in怎麼個意思呢?
當有一個變數在 local 域中找不到時,Python會找上一層的作用域,即 enclosing 域(該域不一定存在)。enclosing 域還找不到的時候,再往上一層,搜索模塊內的 global 域。最後,會在 built-in 域中搜索。對於最終沒有搜索到時,Python會拋出一個 NameError 異常。作用域可以嵌套。比如模塊導入時。這也是為什麼不推薦使用 from a_module import * 的原因,導入的變數可能被當前模塊覆蓋。Assignment rule看似python作用域到此為止已經很清晰了,讓我們再看一段代碼:def outer(): a = 0 b = 1def inner(): print a print b inner()outer()你覺得結果是什麼呢?So easy是不是?如果多加一句呢? def outer():
a = 0b = 1 def inner(): print a print b # b += 1 # A b = 4 # B inner()outer()結果又會是什麼呢?Traceback (most recent call last): File "a.py", line 34, in &outer() File "a.py", line 32, in outer inner() File "a.py", line 29, in inner print bUnboundLocalError: local variable "b" referenced before assignment是不是很奇怪?原因是這樣的:Python解釋器執行到 inner() 中的 print b 時,發現有個變數 b 在當前作用域(local)中無法找到該變數。它繼續嘗試把整塊代碼解釋完。Bingo! 找到了。那麼 b 是屬於 inner() 作用域的。既然對變數 b 的賦值(聲明)發生在 print 語句之後, print 語句執行時變數 b 是還未被聲明的,於是拋出錯誤:變數在賦值前就被引用。在這個例子中,只有A語句沒有B語句也會導致同樣的結果。因為 b += 1 等同於 b = b + 1。對於變數的作用域查找有了了解之後,還有兩條很重要的規則:賦值語句通常隱式地會創建一個局部(local)變數,即便該變數名已存在於賦值語句發生的上一層作用域中;如果沒有 global 關鍵字聲明變數,對一個變數的賦值總是認為該變數存在於最內層(innermost)的作用域中;也就是說在作用域內有沒有發生賦值是不一樣的。
這代碼有啥問題。。縮進嗎類定義就是一個函數扔給_build_class_ 這函數把f_locals給cls(大概吧沒看過源碼。。所以有個臨時的local
推薦閱讀:
※這個python程序不能再簡化了吧?
※python2.7,python3.3,對於小白,到底從哪個版本入手比較好?
※在python中,怎樣計算list的累積和?不能用loop或者library的function。
※想問怎麼用Python編一個 同時投12個骰子 計算每次投出至少出現兩個六的次數及概率的程序?
※剛安裝了pycharm, 寫了一句print "nice!" 都報錯是怎麼回事?