Python基礎九函式進階(一)
Q:Python執行程式碼時,遇到函式是怎麼做到的?
A:從Python直譯器開始執行之後,就在記憶體中開闢一個空間,每當遇到一個變數的時候,就把變數名和值之間的對應關係記錄下來,但是當遇到函式定義的時候,直譯器只是象徵性的將函式名讀入記憶體,表示知道這個函式存在了,至於函式內部的變數跟邏輯,直譯器根本不關心。
當執行到函式呼叫的時候,Python直譯器會再開闢一塊記憶體來儲存這個函式裡面的內容,這個時候,才關注函式裡面有哪些變數,而函式中的變數會儲存在新開闢出來的記憶體中,函式中的變數只能在函式內部使用,並且會鎖著函式執行完畢,這塊記憶體中的所有內容也會被清空。
這個“存放名字與值得空間”被我們叫做——名稱空間
程式碼在執行開始,建立的儲存”變數名與值得關係“的空間叫做全域性名稱空間;在函式的執行中開闢的臨時空間叫做區域性名稱空間。
一、聊一聊名稱空間和作用域
>>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. SparsePython之禪is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way maynot be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! python之禪 python之禪
在python之禪中說:名稱空間是一種絕妙的理念,讓我們盡情的使用發揮吧!
名稱空間一共分三種:
全域性名稱空間
區域性名稱空間
內建名稱空間
內建名稱空間中存放了Python直譯器為我們提供的名字:
input,print,str,list,tuple等他們都是我們可以直接及使用的方法。
三種名稱空間之間的載入與取值順序:
載入順序:
內部名稱空間 --> 全域性名稱空間 --> 區域性名稱空間
取值順序:
在區域性呼叫:區域性名稱空間 --> 全域性名稱空間 --> 內建名稱空間
在全域性呼叫:全域性名稱空間 --> 內建名稱空間
總而言之,言而總之,程式在尋找變數的時候,是從內向外,從小到大,一層層的去尋找的。
作用域
作用域就是作用範圍,按照生效範圍可以分為全域性作用域和區域性作用域。
全域性作用域:包含內建名稱空間、全域性名稱空間,在整個檔案的任意位置都能被引用、全域性有效
區域性作用域:區域性名稱空間,只能在區域性範圍內生效
globals()和locals()方法
print(locals()) print(globals())
def func(): a = 12 b = 20 print(locals()) print(globals()) func() 在區域性呼叫locals和globals
globals可以檢視當前作用域的變數
locals可以檢視全域性作用與的變數
global關鍵字,nonlocal關鍵字。
global:
1宣告一個全域性變數。
2在區域性作用域想要對全域性作用域的全域性變數進行修改時,需要用到global(限於字串,數字)
def func(): global a a = 3 func() print(a) count = 1 def search(): global count count = 2 search() print(count) global關鍵字舉例
but:對於可變資料型別(list,dict,set)可以直接飲用不用通過global。(至於為什麼我想應該是Python函式引用變數的方式是‘傳值’而非‘引用’。具體待後續求證後更新。
nonlocal:
宣告非全域性作用域的各個父級作用域,在區域性作用域中,對父級作用域(或者更外層作用域非全域性作用域)的變數記性引用和修改,並且引用的哪層,從那層及以下此變數全部發上改變。
def add_b(): b = 42 def do_global(): b = 10 print(b) def dd_nonlocal(): nonlocal b b = b + 20 print(b) dd_nonlocal() print(b) do_global() print(b) add_b() nonlocal關鍵字舉例
二、函式的巢狀和作用域鏈
函式的巢狀呼叫
def max2(x,y): m = x if x>y else y return m def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 # max4(23,-7,31,11) 函式的巢狀呼叫
函式的巢狀定義
def f1(): print("in f1") def f2(): print("in f2") f2() f1() ########### def f1(): def f2(): def f3(): print("in f3") print("in f2") f3() print("in f1") f2() f1() 函式的巢狀定義
函式的作用域鏈:小範圍作用域可以使用大範圍的變數,但是反之不行,他是單向的。
def f1(): a = 1 def f2(): def f3(): print(a) f3() f2() f1() ################ def f1(): a = 1 def f2(): a = 2 f2() print('a in f1 : ',a) f1() 作用域鏈應用舉例
三、函式名的本質
透過現象看本質,函式名本質上就是記憶體地址
1可以被引用
def func(): print('in func') f = func print(f)
2可以被當做容器型別的元素
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #呼叫 l[0]() d['f2']() 可以當做容器型別的元素
3可以當做函式的引數和返回值
def f1(): print('f1') def func1(argv): argv() return argv f = func1(f1) f() 可以當做函式的引數和返回值