1. 程式人生 > >[筆記]閉包與裝飾器

[筆記]閉包與裝飾器

一:閉包

1.1作用:
  • 閉包會比使用類佔用更少的資源,而類則在檔案執行時建立,一般程式執行完畢後作用域才釋放,
  • 保持當前的執行環境,
  • 避免使用全域性值,
  • 並提供某種形式的資料隱藏。
1.2定義:

閉包是由函式和與其相關的引用環境[^1]組合而成的實體,將函式與其所操作的某些資料(環境)關聯起來,即使生成閉包環境的父函式釋放但這些變數的值始終儲存在記憶體中,不同的引用環境和相同的函式組合可以產生不同的例項。

1.3例項
def outer(n):
	count = 0
	def inner():
		nonlocal count
		count  = 2
		print(n+count)
	print(inner.__closure__)
	print(inner.__closure__[0].cell_contents)
	return inner
	#呼叫outer的時候就產生了一個閉包—inner,並且該閉包持有自由變數—n,count
	#改寫外部變數需要nonlocal,會優先尋找層級關係與閉包作用域最近的外部變數
	#cell物件組成的元組,自由變數儲存在閉包的 cell_contents中
1.4閉包分析
 35           0 LOAD_CONST               1 (0)
              2 STORE_DEREF              0 (count)  # Stores TOS into the cell contained in slot i of the cell and free variable storage

 37           4 LOAD_CLOSURE             0 (count) # Pushes a reference to the cell contained in slot i of the cell and free variable storage
              6 LOAD_CLOSURE             1 (n)
              8 BUILD_TUPLE              2
             10 LOAD_CONST               2 (<code object inner at 0x00000261B6FD28A0, file "C:/Users/hp/Desktop/Myproject/myflask/file/disst.py", line 37>)
             12 LOAD_CONST               3 ('outer.<locals>.inner')
             14 MAKE_FUNCTION            8
             16 STORE_FAST               1 (inner)

 44          18 LOAD_FAST                1 (inner)
             20 RETURN_VALUE

二、裝飾器

本質:函式,對原函式包裝返回另一個函式 作用:在不破壞原有結構的前提下,為已經存在的物件新增額外的功能,提高了程式的可重複利用性,並增加了程式的可讀性。 特性:在被裝飾的函式定義後立即執行(通常在Python載入模組時),被裝飾的函式只在明確呼叫時執行。 應用場景:經常用於有切面需求的場景,比如:插入日誌、效能測試、事務處理、快取、許可權校驗等場景。

1.1簡單裝飾器
def wrapper(func):
	print("wrapper")
    def inner(*args,**kwargs):
        print('在被裝飾的函式執行之前做的事')
        ret = func(*args,**kwargs)
        print('在被裝飾的函式執行之後做的事')
        return ret
    return inner
       
# python 直譯器發現@wrapper,就去呼叫與其對應的函式
# test=wrapper(test) 執行後,左側的test實際上是一個inner函式的例項物件@wrapper  
def test():
	print("it is a decorator test")   			
執行結果:wrapper
# 由此可見裝飾器函式在被裝飾函式定義後立即執行
1.2多層裝飾器
set_fun1
set_func2
call_fun2
call_fun1
test

載入順序:set_fun2–>set_fun1 執行順序:set_fun1–>set_fun2 即外層裝飾器會對內層裝飾器裝飾的結果進行再裝飾。 同test = set_fun2(set_fun1(test))

1.3帶參裝飾器
def add_para(paragram):
	def wrapper(func):
	    def inner(*args,**kwargs):
	    	if paragram == 1:
	       		print('在被裝飾的函式執行之前做的事')
	        ret = func(*args,**kwargs)
	        print('在被裝飾的函式執行之後做的事')
	        return ret
	    return inner
    return wrapper
@add_para(1)  
# 實際上先執行了add_para(1),返回了繫結paragram的wrapper裝飾器
def destination():
     print("目標函式!")
# 對原有裝飾器封裝返回一個裝飾器,呼叫時能把引數傳遞到裝飾器的環境中。
1.4類裝飾器
class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')
@Foo
def bar():
    print ('bar')
bar()
帶參類裝飾器
class Animal(object):
    def __init__(self, animal='dog'):
        self.animal = animal
    def __call__(self, func):  # 接受函式
        def wrapper(*args, **kwargs):
            print("[{animal}]: {func}()".format(
                animal=self.animal, func=func.__name__))
            func(*args, **kwargs)
        return wrapper  # 返回函式
        
@Animal()
def say(voice):
    print("say {}!".format(voice))
1.5 functools.wraps
from functools import wraps
def decorator_a(func):
    print('Get in decorator_a')
    # @wraps(func)
    def inner_a(*args, **kwargs):
        print('1:',func.__name__,func.__doc__)
        print('Get in inner_a')
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def test(x):
    '''這是一個測試'''
    print('Get in test')
    return x 

test(3)
print('2:',test.__name__, test.__doc__)
結果:
Get in decorator_a
1: test 這是一個測試
Get in inner_a
Get in test
2: inner_a None
# 可以看到原函式的資訊docstring和name被替換,通過functools包中提供了一個叫wraps的decorator來消除這樣的副作用。

三、必要知識:

第一類物件:

一個實體,可以動態建立,銷燬,傳遞給函式,作為值返回,並且具有作為程式語言中其他變數的所有許可權。

特性:

1、函式名字是對函式的引用。 2、可以賦值給其它變數(函式物件的引用計數不斷地增加,本質上這些變數最終指向的都是同一個函式物件)。 3、可以作為引數傳遞。 4、可以作為函式的返回值。 Python函式作為一個物件,擁有物件模型的三個通用屬性:id、型別、和值。

引用環境: