python中def中def是如何實現的?
比如這段代碼
每次調用f,內部的函數都會重新覆蓋定義一遍嗎?這豈不是要慢很多?不嵌套定義會顯得很難看,有什麼好的方法嗎?
另外python絕對不是單純地按行解釋運行,第一行input第二行出現語法錯誤會在一開始就(而不是按回車後)報錯,應該經過了詞法分析。
這要看你怎麼理解「定義」其實py的作法是:先編譯整段代碼,每個函數的代碼部分弄成一個code對象然後執行def這句的時候,把code對象和當時一些信息打包一個func對象,賦值給f1code對象是唯一的,但func對象是實時生成的
從技術上講,當 Python 分析完這個函數的時候,嵌套的 def 會成為函數 f 的內部成員,只是沒有公開出來。運行到內部 def f1() 語句所做的工作就是把已定義的內部成員綁定到局部變數 f1 上,這個操作是很快的,不用擔心重複解析的問題。
你在代碼里加一個跟蹤語句,就可以看到這個函數的內部表示,比如:
def f(n):
if n &< 0:
def f1(n):
print(-n)
print(locals after f1, locals())
f1(n)
elif n == 0:
def f2(n):
print(0)
print(locals after f2, locals())
f2(n)
elif n &> 0:
def f3(n):
print(n)
print(locals after f3, locals())
f3(n)
else:
assert 0
答案是:會重新定義一遍
首先,你猜的很對,.py 源文件會在第一次執行的時候編譯為 .pyc 位元組碼文件,進行了初步的詞法分析。
那到底會不會在每次調用 f 的時候都重新定義內部函數呢
看下面的代碼
def f(n):
if n &< 0:
def f1(n, set=[]):
set.append(n)
print(set)
f1(n)
f(-1)
f(-2)
這是什麼意思呢?
因為 Python 的函數本身也是一個對象,函數的默認參數是它的成員
所以如果默認參數是 可變類型(或者說引用類型),而且你在函數中改變了它,如
def f(n, set = []):
set.append(n)
print(set)
f(-1)
f(-2)
就會出現:
所以很顯然,f 會在每次調用的時候重新定義內部函數
表現在 f1 的默認參數沒有發生變化
雖然你擔心這可能會有性能問題,但 Python 毫不在意......(傲嬌
請看cpython的源碼
函數在編譯的時候就會被編譯為pyfunc對象
然後儲存在常量池
重新定義只是執行位元組碼
LOAD_CONST x(&
WRITE_NAME x(namexxx)
而已
更正:
(自答)大家自行感受奇妙的結果吧(不妨多複製幾行,但輸出的地址我只看到三個)
def f(n):
def g():
pass
if n:
print(g,"print in f")
else:
return g
f(1)
f(1)
f(1)
a = f(0);print(a,"a")
a = f(0);print(a,"a")
a = f(0);print(a,"a")
b = f(0);print(b,"b")
b = f(0);print(b,"b")
b = f(0);print(b,"b")
print(f(0),"f(0)")
print(f(0),"f(0)")
print(f(0),"f(0)")
print(=*10)
f(1)
f(1)
f(1)
a = f(0);print(a,"a")
a = f(0);print(a,"a")
a = f(0);print(a,"a")
b = f(0);print(b,"b")
b = f(0);print(b,"b")
b = f(0);print(b,"b")
print(f(0),"f(0)")
print(f(0),"f(0)")
print(f(0),"f(0)")
這這...雖然我也是學語言的,但不是二進位語言啊,是自然語言.....所以愛莫能助了...
呃,這個雖然我也很想幫助您,奈何本人只是學c的。
你先定義好這些函數,建立起它們和n的映射,然後使用就好了。具體可以把它們放到字典里,作為值。或者定義一個類,作為類的方法。根據n大小dispatch用二分法,if else語句塊多了很醜。
推薦閱讀:
※Julia快速入門(上)
※會一門C語言、Bash語言和linux系統和計算機系學生差多少?
※R語言學習第一章
※為啥QT creater發布exe如此麻煩?