1. 程式人生 > 實用技巧 >函式物件,函式的巢狀,名稱空間與作用域,閉包函式

函式物件,函式的巢狀,名稱空間與作用域,閉包函式

一、函式物件

在python中,函式是第一類物件,函式是第一等公民

本質:函式可以當變數用

1、可以賦值,可以被引用

def func():      #func = 函式的記憶體地址
	print('from func')
f=func
print(f)

2、可以當作引數傳遞給另外一個函式

def func():
	print('from func')
def foo(x):
	print(x)
	x()
foo(func)

3、可以當做函式的返回值

def func():
	print('from func')
def foo(x):
	return x
res = foo(func)
print(res)

4、可以當做容器型別的元素

def func():
	print('from func')
l=[func,]
print(l)
l[0]()

案例===利用該特性,優雅的取代多分支的if

def withdraw():   
	print("提款")
def tranfer():   
	print('轉賬')
def check_balance():  
	print("查詢餘額")
def save():  
	print("存款")
func_dic = {   
    "1": ["提款",withdraw],   
    "2": ["轉賬",tranfer],   
    "3": ["查詢餘額",check_balance],   
    "4": ["存款",save]}
while True:    
	print("0 退出")   
	for k,v in func_dic.items():      
		print(k,v[0])   
	choice = input(">>>: ").strip()   
	if choice == '0':        
		break   
	if choice in func_dic:    
		func_dic[choice][1]()   
	else:      
		print("輸入錯誤")

二、函式的巢狀

1、函式的巢狀呼叫

def max(x,y):
    return x if x > y else y

def max4(a,b,c,d):
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3
print(max4(1,2,3,4))

2、函式的巢狀定義

def f1():
    x = 10
    def f2():
        print('from f2')
    print(x)
    print(f2)
f1()
print(x)
print(f2)

三、名稱空間與作用域

1、什麼是名稱空間?

名稱空間:存放名字的地方,三種名稱空間

1)內建名稱空間:存放內建的名字

​ 生命週期:python直譯器啟動則刪除,關閉則銷燬

2)全域性名稱空間:存放的是頂級的名字

​ 生命週期:執行python檔案時則產生,python檔案執行完畢則銷燬

3)區域性名稱空間: 存放的是函式內的名字

​ 生命週期:呼叫函式則產生,函式呼叫完畢則銷燬

2、名稱空間的載入順序

python test.py
#1、python直譯器先啟動,因而首先載入的是:內建名稱空間
#2、執行test.py檔案,然後以檔案為基礎,載入全域性名稱空間
#3、在執行檔案的過程中如果呼叫函式,則臨時產生區域性名稱空間

3、名字的查詢順序

區域性名稱空間--->全域性名稱空間--->內建名稱空間
需要注意的是:在全域性無法檢視區域性的,在區域性可以檢視全域性的,如下示例
max=1
def f1():
    # max=2
    def f2():
        # max=3
        print(max)
    f2()
f1()
print(max)
核心:名字的訪問優先順序    基於當前所在的位置向外查詢        函式內->外層函式->。。。->全域性->內建

​ p s:名稱空間代名詞 LEGB (L代表區域性,E代表區域性之外的層,G代表全域性,B代表內建

LEGB 代表名字查詢順序: locals -> enclosing function -> globals -> __builtins__
locals 是函式內的名字空間,包括區域性變數和形參
enclosing 外部巢狀函式的名字空間(閉包中常見)
globals 全域性變數,函式定義所在模組的名字空間
builtins 內建模組的名字空間

4、作用域

#1、作用域即範圍
        - 全域性範圍(內建名稱空間與全域性名稱空間屬於該範圍):全域性存活,全域性有效
      - 區域性範圍(區域性名稱空間屬於該範圍):臨時存活,區域性有效
#2、稱空間與作用域的關係是在函式定義階段(掃描語法時)就確立的,與什麼時候呼叫以及呼叫位置無關,如下
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000
f3(f1())

#3、檢視作用域:globals(),locals()
..global與nonlocal關鍵字
x = 111
def func():
   global x     # 生命變數名是來自於全域性的
   x =  222
func()
print(x)
=====================
x = 111
def f1():
    x = 222
    def f2():
        nonlocal x  # 宣告變數名是來自於外層函式的,不是全域性
        x = 333
    f2()
    print(x)
f1()

四、閉包函式

閉:指的該函式是定義在函式內的函式
包:指的就是該函式引用了一個外層函式作用域的名字

ef outter():    
    x = 111   
	def wrapper():     
		print(x)  
	return wrapper  # 千萬別括號
f = outter()
print(f)
def foo():  
	x=222  
	f()
foo()
內部函式包含對外部作用域而非全域性作用域的引用
提示:之前我們都是通過引數將外部的值傳給函式,閉包提供了另外一種思路,包起來嘍,包起呦,包起來哇
       def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #檢視閉包的元素
為函式體程式碼傳參的方案
方案一:直接用引數傳
def wrapper(x):
	print(x)
wrapper(111)
wrapper(222)
wrapper(333)

方案二:閉包函式
def outter(x):
   x = 111
   def wrapper():
   		print(x)
	return wrapper
f1 = outter(111)
f1()
f2 = outter(222)
f2()

五、裝飾器

1、什麼是裝飾器

裝飾器就是一個用來為被裝飾物件新增新功能的工具,是閉包函式的一種應用場景

2、為何要用裝飾器

開放封閉原則:一旦軟體上線執行之後,應該對修改原始碼封閉,對擴充套件功能開放

強調裝飾器的原則:1 不修改被裝飾物件的原始碼 2 不修改被裝飾物件的呼叫方式
裝飾器的目標:在遵循1和2的前提下,為被裝飾物件新增上新功能

3、如何實現裝飾器

需求:為函式新增統計執行時間的功能
& 無參裝飾器
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()
&  無參裝飾器
& 有參裝飾器
def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')
& 有參裝飾器