比如:
def foo(): a = 10 def foo2(): a = 20
回複內容:
這不是王垠說的麼,王垠專業就是研究程式設計語言。研究語言的博士生針對各種語言特性做的分析,對我們都是有很多啟發的。但你說的這種外面有個a,函數裡面又有個a的情況,我想問問,其實實際開發中,是不是每天都要處理內外層變數名字衝突的問題?我們有兩種選擇:
一種做法是象王垠說的:區別變數聲明和賦值,就是類似聲明時前面加個 var一樣,
另外的做法象龜多做的:沒有 var,只有在衝突的時候加一些 global nolocal之類的關鍵字。
哪種做法好,請你想想大多數情況下,99%的時候你是不會傻到內外用一樣的名字的吧,好的編程習慣甚至全域變數和局部變數在命名上都是區別的,不會出現你說的衝突。系統設計就是需要不斷的做出選擇,龜多也一樣做出了他的選擇,讓你 99%的情況下都不需要為每個變數去寫一個 var聲明去,以此保證語言的簡潔性和優雅性。直到你碰到 1%的極端的情況時,用一下 nolocal global又有什麼困難的呢?
然而這些對使用者優雅的特性,對於寫 PySonar的王垠來講,卻無外乎一種折磨,要在靜態分析中間指出他們的區別來是很痛苦的,這是肯定的事情,層次越進階的語言越不容易做靜態分析,所以王垠被噁心到了,狂噴 global nolocal也是可以理解的。
所以別人云亦云,多想想說這些話是在什麼背景下說出來的,這些客觀條件和自己所處的情況相同否,再做出自己的判斷。
------------------補充一個目前大家都沒有提到的方面。
題主,先不論是不是Python 的設計缺陷,你通過以上例子得出的那個結論,是存在以偏概全的問題的。題主的例子表達的觀點實際上為:Python中,在沒有聲明的情況下,子函數中的同名變數無法改變外圍同名變數。不過,這種網上廣為流傳的說法是錯誤的。
幾行代碼可以說明這個問題:
def foo(): a_int=10 a_list_with_method=[0,1,10] def foo2(): a_int=20 a_list_with_method.append(11) foo2() print(a_int) #Python2.x 請用print a_int print(a_list_with_method) #Python2.x 請用print a_list_with_methodfoo()
首先,不要求完全精確的情況下,Python 其實可以說是“沒有變數”,有的只是“名字綁定”,每一個名字可以視為一個執行個體(instance)的引用,變數只是大家沿用了更熟悉的概念而已。也就是說,`a = 20` 這句代碼裡,`a` 只是一個名字(引用)。
然後,回到題主的代碼,這是一個 Python 的名字搜尋機制的問題,因為 Python2.x 的實現是本地範圍找不到的話就去全域範圍找,那麼就會出現題主說的“在foo2中沒有辦法改變foo中a的值”的問題,這個問題後來在 Python3.x 中通過引入 `nonlocal` 解決了。
最後,“python不區分變數定義和賦值,是否是一個設計上的缺陷?”,這是一個偽問題,開篇說過了 Python 其實可以說是“沒有變數”,所以何來“定義”之說,說到“缺陷”就更是強加的罪名了。python的closure設計的確是有缺陷的。不只是這個地方,還有引用傳參卻又沒有強制唯讀。
但是,我跟你說,這是個feature,不是缺陷。python設計哲學根本不鼓勵你用詞法範圍這種花式文法,按照python的邏輯,你應該老老實實用自變數匯入所有的參數。那些nonlocal和global都是後門,不推薦使用。
所謂的python設計哲學,就是簡單直接,遵從感覺,不向硬體妥協,也不向數學屈膝。範圍這種複雜的數學問題,明智的方法是繞開它,不要去思考。寫個python還要思考範圍,那跟寫C++有什麼區別?當然,如果你一定要思考這種問題,C++歡迎你(逃這個不能算缺陷吧...想實現這種功能應該是有其它的辦法來實現的。對於有些語言,比如C來說,函數是不可以嵌套的。
我倒是覺得Python不區分聲明和賦值的話,有些地方變數名拼字錯了,運行也可能不報錯,然後就出現各種神奇的bug了。因為python中所有東西都是對象,賦值其實只是引用綁定,所以這個情況某種意義上是必然的。
這也與python遵循的基本原則有關。如果遵守簡單唯一表示的原則,那麼並沒有什麼不方便的地方;如果說這個原則不好,那也沒辦法了……
在foo2中沒有辦法改變foo中a的值,這能否說因為python不區分變數定義和賦值從而導致的一個缺陷?python3引入的nonlocal恰好證明了這一點?
我想說的是,你從一個錯誤的論據匯出了一個正確的結論。。
Python的name rebinding(assign to)機制的確有缺陷,只能rebind到local和module-global,而不能rebind到outer scope。
但Python是有變數聲明的,比如global語句就是用來綁定global變數的,而local聲明是隱式的。Python 3新加的nonlocal修補了不能綁定到outer scope的問題。
PEP 3104 -- Access to Names in Outer Scopes
你確定是設計的缺陷,而不是美德(merit)?我覺得應該是缺陷吧,考慮如下情形
variable = 10
bla bla bla...
varable = 3//此處由於拼字,少打了個i,認為是新變數
variable += 16
計算錯誤初學Python,有幾年c++經驗,我說幾句我理解的。
對於任何變數來說,他都應該遵循最小範圍,既然Python不區分賦值既建立,那好那我就建立吧!別的我不管,你想用其他範圍的,好吧!你指定吧!
至於說為什麼賦值既建立,可能是因為Python沒有強制要求聲明資料類型的緣故吧!至於說最佳化,預設是引用形式,只是記憶體效能方面的最佳化吧!