py裝飾器(functools.wraps(func)裝飾器、functools模塊、@property裝飾器)


"""
py裝飾器.py
(functools.wraps(func)裝飾器、functools模塊、@property裝飾器)

裝飾器@(decorator裝飾器 = 高階函數 + 閉包函數)
裝飾器本質上是接受一個函數作爲參數(高階函數行爲),並返回一個函數(閉包函數行爲)。
返回函數即子函數 中定義添加內容後 再返回主函數的參數函數。
從而實現不修改 參數函數的基礎上 在代碼運行期間動態增加功能的方式。

1、#使用:如果裝飾器decorator本身需要傳入參數,那就需要編寫一個返回裝飾器的高階函數(即三層嵌套裝飾器函數)。

2、#使用:functools.wraps(func)裝飾器
#注意:裝飾器裝飾過的函數的原屬性已經改變,因爲裝飾器內部是閉包主函數return返回了子函數。
#解決辦法是 通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。

3、#深入:import functools  
#functools 模塊中主要包含了一些函數裝飾器和便捷的功能函數。
3.1、#使用:@functools.wraps(func)
通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。
3.2、#使用:functools.partial()用來創建一個偏函數,即把一個函數的某些參數給固定住(即設置默認值),返回一個新函數。
#注意:偏函數實質上是一個 @裝飾器 的原理,即在參數列表裏追加進去 設置默認值的參數。
#如:int2('1000') 實則是運行的 int('1000',base=2)


4、@property(@property裝飾器爲可讀屬性 / 裝飾函數.setter裝飾器爲可寫屬性)
#使用:@property裝飾器 負責把一個方法變成屬性來調用,即:@property裝飾器相當於getter方法可讀屬性。
#注意:@property裝飾器 本身又會創建另一個裝飾器,即:使用:裝飾函數.setter裝飾器 相當於 setter可寫屬性。

"""



################### 裝飾器@(decorator裝飾器 = 高階函數 + 閉包函數)
#裝飾器本質上是接受一個函數作爲參數(高階函數行爲),並返回一個函數(閉包函數行爲)。
#返回函數即子函數 中定義添加內容後 再返回主函數的參數函數。
#從而實現不修改 參數函數的基礎上 在代碼運行期間動態增加功能的方式。

#使用:functools.wraps(func)裝飾器
#注意:裝飾器裝飾過的函數的原屬性已經改變,因爲裝飾器內部是閉包主函數return返回了子函數。
#解決辦法是 通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。
#深入:import functools  使用:@functools.wraps(func)


#####
#定義一個本身不需要傳入參數的裝飾器 打印日誌的裝飾器decorator
def log(func):
    """接收一個函數作爲參數,稱爲高階函數
    返回子函數,稱爲閉包函數"""
    def wrapper(*args,**kw):
        """返回主函數的參數函數,從而達到不修改參數函數的基礎上 動態增加print功能"""
        print('開始調用函數:%s()' %func.__name__)
        """動態增加print功能後,繼續調用原函數()執行"""
        return func(*args,**kw)
    return wrapper
"""使用:@decorator放到 func函數定義前,相當於執行了 func=decorator(func),重新賦值定義了一個同名新函數"""
@log    
def now():
    print('2019/11/08')
now()
"""等同於不添加裝飾器@log情況下的如下表示:"""
#log(now)()
#now=log(now)
#now()


#####
#使用:如果裝飾器decorator本身需要傳入參數,那就需要編寫一個返回裝飾器的高階函數(即三層嵌套裝飾器函數)。
#定義一個本身需要傳入參數的裝飾 打印日誌的裝飾器
def log(who):
    def decorator(func):
        def wrapper(*args,**kw):
            print('%s開始調用函數:%s()' %(who,func.__name__))
            return func(*args,**kw)
        return wrapper
    return decorator
@log('小王')
def now():
    print('2019/11/09')
now()
"""等同於不添加#log('小王')的如下表示:"""
#log('小張')(now)()
#now=log('小張')(now)
#now()



#####
#使用:functools.wraps(func)裝飾器
#注意:裝飾器裝飾過的函數的原屬性已經改變,因爲裝飾器內部是閉包主函數return返回了子函數。
#解決辦法是 通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。

#深入:import functools  
#functools 模塊中主要包含了一些函數裝飾器和便捷的功能函數。
#2.1、使用:@functools.wraps(func)
#通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。

import functools

def log(func):
#    @functools.wraps(func)
    def wrapper(*args,**kw):
        print('開始調用函數:%s()' %func.__name__)
        return func(*args,**kw)
    return wrapper
@log
def now():
    print('2019/11/09')
now()
print(now.__name__)

#對比添加了 @functools.wraps(func) 裝飾器之後的 now.__name__

import functools

def log(who):
    def decorator(func):
        """使用:functools.wraps(func)裝飾器
        通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 
        來糾正被此裝飾器裝飾過的func函數屬性。
        """
        @functools.wraps(func)
        def wrapper(*args,**kw):
            print('%s \n %s開始調用函數:%s' %('接下來使用了functools.wraps(func)技術',who,func.__name__))
            return func(*args,**kw)
        return wrapper
    return decorator
@log('小劉')
def now():
    print('2019/11/10')
now()
now.__name__





#################### @property(@property裝飾器爲可讀屬性 / 裝飾函數.setter裝飾器爲可寫屬性)
#使用:@property裝飾器 負責把一個方法變成屬性來調用,即:@property裝飾器相當於getter方法可讀屬性。
#注意:@property裝飾器 本身又會創建另一個裝飾器,即:使用:裝飾函數.setter裝飾器 相當於 setter可寫屬性。

class Student(object):    
    """#使用:@property裝飾器 負責把一個方法變成屬性來調用;
    即:@property裝飾器相當於getter方法可讀屬性。"""
    @property
    def score(self):
        return self._score
    
    """#注意:@property裝飾器 本身又會創建另一個裝飾器;
    即:使用:@裝飾函數.setter裝飾器 相當於 setter可寫屬性。"""
    @score.setter
    def score(self,value):
        if not isinstance(value,int):
            raise ValueError('成績需要是個整數')
        if value<0 or value>100:
            raise ValueError('成績需要時0~100')
        self._score=value
s=Student()
s.score=100
s.score


#當代碼有了@property裝飾器以後,我們就知道屬性很可能不是直接暴露的,而是通過getter方式讀取 和 setter方法寫入的。
#定義只讀屬性,即:只定義@property裝飾器(getter方法),補丁已setter方法。
#定義可讀可寫屬性,即:定義@property裝飾器 及 @裝飾函數.setter裝飾器
#如:
class Student(object):
    """定義可讀可寫的birth屬性"""
    @property
    def birth(self):
        return self.__birth
    @birth.setter
    def birth(self,value):
        self.__birth=value
        
    """定義只讀屬性(無@裝飾函數.setter裝飾器)"""
    @property
    def age(self):
        return 2015-self.__birth











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章