py函數式編程(高階函數map/reduce/filter/sorted、閉包函數/返回函數、匿名函數lamber、@裝飾器decorator、偏函數functool.partial())

#py函數式編程.py
#高階函數map/reduce/filter/sorted、閉包函數/返回函數、匿名函數lamber、@裝飾器decorator、偏函數functool.partial()


# =============================================================================
# #Py函數式編程.py
# #高階函數、閉包函數/返回函數、匿名函數lamber、@裝飾器decorator、偏函數functool.partial()
# #參考鏈接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056
# =============================================================================

'''
深入:
一、import functools  
#functools 模塊中主要包含了一些函數裝飾器和便捷的功能函數。

1、使用:@functools.wraps(func)
通過給decorator裝飾器主函數內return返回的子函數 加上裝飾器functools.wraps(func) 來糾正被此裝飾器裝飾過的func函數屬性。

2、#使用:functools.partial()用來創建一個偏函數,即把一個函數的某些參數給固定住(即設置默認值),返回一個新函數。
#注意:偏函數實質上是一個 @裝飾器 的原理,即在參數列表裏追加進去 設置默認值的參數。
#如:int2('1000') 實則是運行的 int('1000',base=2)



#使用:
一、高階函數(map/reduce/filter/sorted等)
高階函數就是一個函數就可以接收另一個函數作爲參數的函數。

1、map(func,*iterables)     將參數func函數 作用於 參數*iterables裏的每一個內容,返回一個可迭代對象。
#注意 map(,)返回一個迭代器iterator,需要序列化才能輸出。
`
2、reduce(func,*iterables,initial)  將參數func函數 作用於 參數序列上,並把結果繼續和序列的下一個元素做累計計算。並最終返回值。
#注意 reduce(,) 直接返回累計作用的值。

3、filter(func,iters) 過濾序列序列。用參數func函數 過濾 參數iters序列,爲True則留下,爲False則丟棄。 
#注意 filter(,)返回一個迭代器iterator,需要序列化才能輸出。

4、sorted(iterable, key=None, reverse=False) 對參數iterable序列list進行排序,
#注意 參數key可用來接收一個函數來實現自定義的排序。
#注意 參數reverse爲True則反向排序


二、返回函數(閉包)
閉包函數
#高階函數除了可以接受函數作爲參數外,還可以把函數作爲 結果值返回。
#閉包 即在 外部函數中 又定義了 內部函數,並且,內部函數 可以引用 外部函數 的參數和局部變量。
#當 外部函數 返回 內部函數 時,相關參數和變量都保存在返回的函數中,這種閉包程序結構擁有極大的威力,稱謂“閉包”。
#參考網址:https://blog.csdn.net/sc_lilei/article/details/80464645

1、使用:通過獲取主函數的魔法屬性__closure__返回的值來確定主函數是否存在閉包。
#__closure__屬性返回一個元組對象,包含了閉包引用的外部變量。
#對閉包主函數的__closure__屬性迭代後通過cell_contents來輸出閉包引用的外部變量
#如果主函數 沒有return子函數,就不存在閉包,主函數不存在_closure__屬性,返回None
#如果主函數 return的子函數不引用外部變量,也就不存在閉包,主函數的__closure__屬性同樣返回None。 

2、注意:閉包函數中的return返回內容指向(return返回子函數名稱 而不是調用函數)、函數作用域。

3、注意:閉包時牢記一點:返回函數(子函數、內部函數)不要引用任何 循環變量,或者後續會發生變化的變量。
#如果子函數一定要引用循環變量,方法是再創建一個函數。
#用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:


三、匿名函數lambda
#關鍵字lambda表示匿名函數,lambda x : x*x,冒號前面表示函數參數,冒號後面表達式表示函數內容
1、#使用:匿名函數可以賦值變量,lambda也是一個函數對象,也可以賦值給變量,然後通過變量來調用匿名函數。
2、#使用:匿名函數可以作爲返回值返回,如在函數定義裏最後return lambda :x*x + y*y
#注意:匿名函數作爲返回值返回,等同於return子函數,就是一個閉包函數。

3、#注意:匿名函數有個限制:只能有一個表達式,不用填寫return,返回值就是該表達式的結果


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

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

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



五、偏函數functools.partial()
#functools 模塊中主要包含了一些函數裝飾器和便捷的功能函數。其中一個就是偏函數(Partial function)。
#偏函數可以通過設定參數的默認值,從而降低函數調用的難度。

1、#使用:functools.partial()用來創建一個偏函數,即把一個函數的某些參數給固定住(即設置默認值),返回一個新函數。
2、#注意:偏函數實質上是一個 @裝飾器 的原理,即在參數列表裏追加進去 設置默認值的參數。
#如:int2('1000') 實則是運行的 int('1000',base=2)


'''




'''
函數式編程就是一種抽象程度很高的編程範式。

函數是Python內建支持的一種封裝,我們通過把大段代碼拆成函數,通過一層一層的函數調用,就可以把複雜任務分解成簡單的任務,這種分解可以稱之爲面向過程的程序設計。
函數就是面向過程的程序設計的基本單元。

純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之爲沒有副作用。
而允許使用變量的程序設計語言,由於函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。

函數式編程的一個特點就是,允許把函數本身作爲參數傳入另一個函數,還允許返回一個函數!
Python允許使用變量,因此,Python不是純函數式編程語言。
'''

#################### 高階函數
#高階函數
#高階函數就是一個函數就可以接收另一個函數作爲參數的函數。
#一個函數就可以接收另一個函數作爲 參數,這種函數就稱之爲高階函數。
def add(x,y,fun):
    return fun(x)+fun(y)
print(add(-5,6,abs))


##########
#使用 map(func,*iterables) 將參數func函數 作用於 參數*iterables裏的每一個內容,返回一個可迭代對象。
#注意 map(,)函數返回的是一個迭代器iterato,可迭代對象,需要list獲取序列化才能輸出。

help(map)
type(map)

def f(x):
    return x*x
r=map(f,[1,2,3,4,5,6,7,8,9])
print(list(r))               #注意 map(,)函數返回的是一個可迭代對象,需要list獲取序列化才能輸出。

#通過map(,)函數將int內容批量轉換成str內容
list(map(str,[1,2,3,4,5,6,7,8,9]))


##########
#使用 reduce(func,*iterables,initial) 將參數func函數 作用於 參數序列上,並把結果繼續和序列的下一個元素做累計計算。並最終返回值。
#注意 reduce(,) 直接返回累計作用的值。
#參數: func指定參數,iterables指定作用序列,initial指定初始化的序列值。
#注意:如果傳入了 initial 參數值, 那麼首先傳的就不是 iterables 的第一個和第二個元素,而是 initial值 和 iterables的第一個元素

from functools import reduce
help(reduce)

def add(x,y):
    return x+y
reduce(add,[1,3,5,7,9])    #注意 reduce(,)函數返回一個累計後的值。

#####
#示例1:
#如果考慮到字符串str也是一個序列,可作爲reduce的參數2,則配合map(),就可以寫出把str轉換爲int的函數
from functools import reduce

def fn(x,y):
    return x*10 + y
def char2num(s):
    digits={'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
    return digits[s]       #注意參數s需要是一個str類型 來 對應字典的鍵明key

print(reduce(fn,map(char2num,'13579')))  #將字符串'13579'作爲一個可迭代的類型參數。

#####
#示例2
#使用匿名函數lambda來簡寫示例1
from functools import reduce
DIGITS={'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x,y: x*10 + y, map(char2num,s))

str2int('12580')


##########
#使用 filter(func,iters) 過濾序列序列。用參數func函數 過濾 參數iters序列,爲True則留下,爲False則丟棄。 
#注意 filter(,)返回一個迭代器iterator,需要序列化才能輸出。

#和map()類似,filter()也接收一個函數和一個序列。
#不同的是,filter()把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。

help(filter)

#示例在一個list中,刪掉偶數,值保留奇數:
def is_odd(n):
    return n%2 == 1    #符號 %求餘,//取整。
print(list(filter(is_odd,[1,2,3,4,6,7,8,9])))

#示例把一個序列中的空字符串刪掉:
def not_empty(s):
    return s and s.strip()    #.strip()方法用於移除字符串頭尾指定的字符(默認移除空格或換行符,注意只能刪除開頭和結尾)
print(list(filter(not_empty,['A','','B',None,'c','  '])))


#####
#示例用fielter求素數。(素數:一個正整數,如果只有1和它本身兩個因數,則叫做素數,也叫做質數。)

#計算素數的一個方法是埃氏篩法,它的算法理解起來非常簡單:
#首先,列出從2開始的所有自然數,構造一個序列:2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
#取序列的第一個數2,它一定是素數,然後用2把序列的2的倍數篩掉:3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
#取新序列的第一個數3,它一定是素數,然後用3把序列的3的倍數篩掉:5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
#取新序列的第一個數5,然後用5把序列的5的倍數篩掉:7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
#不斷篩下去,就可以得到所有的素數。

def _odd_iter():
    """先構造一個從3開始的奇數序列,
    注意 這是一個生成器,並且是一個無限序列的生成器。
    """
    n=1
    while True:
        n=n+2
        yield n

def _not_divisible(n):
    """然後定義一個篩選函數"""
    return lambda x: x%n > 0

def prines():
    """最後定義一個生成器,不斷返回下一個素數
    這個生成器先返回第一個素數2,然後,利用filter()過濾函數不斷產生篩選後的新的序列
    """
    yield 2
    it=_odd_iter()                          #初始化序列
    while True:
        n=next(it)
        yield n
        it= filter(_not_divisible(n),it)    #構造新序列

"""由於prines()也是一個無限序列,所有調用時需要設置一個退出循環的條件"""
for n in prines():
    if n<1000:
        print(n)
    else:
        break

#生成器 yield
#包含yield的語句被稱爲 生成器。
#包含 基線條件 和 遞歸條件。  即最後一個條件處理 和 循環的條件處理。
a=_odd_iter()
print(a)
next(a)


##########
#使用 sorted(iterable, key=None, reverse=False) 對參數iterable序列list進行排序,
#注意 參數key可用來接收一個函數來實現自定義的排序。
#注意 參數reverse爲True則反向排序

#示例 按絕對值大小排序
"""key參數指定的函數將作用於每一個元素上,並根據key函數返回的結果進行排序"""
sorted([36,5,-12,9,-21],key=abs)
sorted([36,5,-12,9,-21],key=abs,reverse=True)

help(sorted)




#################### 閉包函數(返回函數)
#使用 閉包函數
#高階函數除了可以接受函數作爲參數外,還可以把函數作爲 結果值返回。
#閉包 即在 外部函數中 又定義了 內部函數,並且,內部函數 可以引用 外部函數 的參數和局部變量。
#當 外部函數 返回 內部函數 時,相關參數和變量都保存在返回的函數中,這種閉包程序結構擁有極大的威力,稱謂“閉包”。
#參考網址:https://blog.csdn.net/sc_lilei/article/details/80464645


#注意:閉包函數中的return返回內容指向(return返回子函數名稱 而不是調用函數)、函數作用域。

#注意:閉包時牢記一點:返回函數(子函數、內部函數)不要引用任何循環變量,或者後續會發生變化的變量。
#如果子函數一定要引用循環變量,方法是再創建一個函數。
#用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:

#使用:通過獲取主函數的魔法屬性__closure__返回的值來確定主函數是否存在閉包。
#__closure__屬性返回一個元組對象,包含了閉包引用的外部變量。
#對閉包主函數的__closure__屬性迭代後通過cell_contents來輸出閉包引用的外部變量
#如果主函數 沒有return子函數,就不存在閉包,主函數不存在_closure__屬性,返回None
#如果主函數 return的子函數不引用外部變量,也就不存在閉包,主函數的__closure__屬性同樣返回None。 


#一個可變參數的求和函數
def calc_sum(*args):
    """該函數 立刻返回求和的結果ax"""
    ax=0
    for n in args:
        ax=ax+n
    return ax

#一個返回求和函數名的函數
def lazy_sum(*args):
    """該函數 返回一個函數名(不帶括號表示不運行函數),不立即求和,而是等到調用函數()時再進行求和;
    閉包:
    #閉包 即在 外部函數中 又定義了 內部函數,並且,內部函數 可以引用 外部函數 的參數和局部變量。
    #當 外部函數 返回 內部函數 時,相關參數和變量都保存在返回的函數中,這種閉包程序結構擁有極大的威力,稱謂“閉包”。

    """
    def sum():
        ax=0
        for n in args:
            ax=ax+n
        return ax
    """ 閉包return返回了子函數名,不是調用子函數()"""
    return sum    #注意這裏是返回的 函數名,不帶括號表示不運行函數。
f=lazy_sum(1,3,5,7,9)    #相當於賦值給f,賦值內容爲 主函數內return返回的子函數名
f()    #這裏纔是進行了 主函數內return返回的子函數的調用,即立即求和計算結果。


#注意:閉包時牢記一點:返回函數(子函數、內部函數)不要引用任何循環變量,或者後續會發生變化的變量。
def count():
    fs=[]
    for i in range(1,4):
        def f():
            """
            #注意:閉包時牢記一點:返回函數(子函數、內部函數)不要引用任何循環變量,或者後續會發生變化的變量。
            #如果子函數一定要引用循環變量,方法是再創建一個函數。
            #用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:
            
            如下內容即引用了循環變量,導致實例化後輸出結果都爲9
            """
            return i*i 
        
        '''for循環,追加 函數名 進 fs空列表'''
        fs.append(f)
    return fs
f1,f2,f3=count()    #等於 f1,f2,f3=[f, f, f]
f1()                #等於運行 f() 實質子函數f()裏i已經運行完畢爲最後的 3 。
f2()                #等於運行 f() 實質子函數f()裏i已經運行完畢爲最後的 3 。
f3()                #等於運行 f() 實質子函數f()裏i已經運行完畢爲最後的 3 。

#注意:閉包時牢記一點:返回函數(子函數、內部函數)不要引用任何循環變量,或者後續會發生變化的變量。
#如果子函數一定要引用循環變量,方法是再創建一個函數。
#用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:
def count():
    def f(j):
        def g():
            return j*j
        return g
    """一個空列表fs,接下來準備append追加進如內容:內容爲 函數名帶綁定後的參數i 即 f(1)、f(2)、f(3)"""
    fs=[]
    for i in range(1,4):
        fs.append(f(i))
    return fs
f1,f2,f3=count()    #這裏實質是列表多重賦值,等於 f1,f2,f3=[f(1),f(2),f(3)]
f1()
f2()
f3()
count()


#繼續加深對閉包的理解
_list = []
for i in range(3):
    def func():
        return i+1
    func.__num__= i
    """ 這裏實則添加完畢以後的 _list內容爲[func,func,func],但是每一個func的__num__不同"""
    _list.append(func)
for f in _list:
    print(f.__num__,
          f()
          )
#實際輸出
#0 3
#1 3
#2 3


#通過主函數的參數判斷 來return指定子函數。
def get_math_func(type) :
    # 定義一個計算平方的局部函數
    def square(n) :
        return n * n
    # 定義一個計算立方的局部函數
    def cube(n) :
        return n * n * n
    # 定義一個計算階乘的局部函數
    def factorial(n) :
        result = 1
        for index in range(2 , n + 1):
            result *= index
        return result
    # 返回局部函數
    if type == "square" :
        return square
    if type == "cube" :
        return cube
    else:
        return factorial
# 調用get_math_func(),程序返回一個嵌套函數
math_func = get_math_func("cube") # 得到cube函數
print(math_func(5)) # 輸出125

math_func = get_math_func("square") # 得到square函數
print(math_func(5)) # 輸出25

math_func = get_math_func("other") # 得到factorial函數
print(math_func(5)) # 輸出120


##########
#使用:通過獲取主函數的魔法屬性__closure__返回的值來確定主函數是否存在閉包。
#__closure__屬性返回一個元組對象,包含了閉包引用的外部變量。
#對閉包主函數的__closure__屬性迭代後通過cell_contents來輸出閉包引用的外部變量
#如果主函數 沒有return子函數,就不存在閉包,主函數不存在_closure__屬性,返回None
#如果主函數 return的子函數不引用外部變量,也就不存在閉包,主函數的__closure__屬性同樣返回None。 

# NO.1
def line_conf1(a, b):
    def line(x):
        return a * x + b
    return line
# NO.2
def line_conf2():
    a = 1
    b = 2
 
    def line(x):
        print(a * x + b)
    return line
# NO.3
def _line_(a,b):
    def line_c(c):
        def line(x):
            return a*(x**2)+b*x+c
        return line
    return line_c

L=line_conf2()
print(line_conf2().__closure__)      #通過獲取主函數的魔法屬性__closure__返回的值來確定主函數是否存在閉包。

for i in line_conf2().__closure__:
    print(i.cell_contents)           #對閉包主函數的__closure__屬性迭代後通過cell_contents來輸出閉包引用的外部變量


#####
#閉包的實際應用1
def who(name):
    def do(what):
        print(name,'say:',what)
    return do

lucy=who('lucy')
lucy('I want drink!')


#####
#閉包的實際應用
#閉包實現快速給不同項目記錄日誌:

import logging

def log_header(logger_name):
    """ """
    logging.basicConfig(level=logging.DEBUG, 
                        format='%(asctime)s [%(name)s] %(levelname)s  %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    logger = logging.getLogger(logger_name)
 
    
    def _logging(something,level):
        """ 返回的數(子函數),通過判斷參數2 來調用日誌輸出類型,輸出內容爲參數1 """
        if level == 'debug':
            logger.debug(something)
        elif level == 'warning':
            logger.warning(something)
        elif level == 'error':
            logger.error(something)
        else:
            """ 如果沒有傳入參數2日誌級別,則手動引發異常"""
            raise Exception("I dont know what you want to do?" )
    return _logging
 
project_1_logging = log_header('project_1')
project_2_logging = log_header('project_2')
 

def project_1():
    #多此一舉加函數
    project_1_logging('this is a debug info','debug')
    project_1_logging('this is a warning info','warning')
    project_1_logging('this is a error info','error')
 
def project_2():
    #多此一舉加函數
    project_2_logging('this is a debug info','debug')
    project_2_logging('this is a warning info','warning')
    project_2_logging('this is a critical info','error')
 
project_1()
project_2()




#################### 匿名函數
#關鍵字lambdab表示匿名函數,lambda x : x*x,冒號前面表示函數參數,冒號後面表達式表示函數內容
#使用:匿名函數可以賦值變量,lambda也是一個函數對象,也可以賦值給變量,然後通過變量來調用匿名函數。

#使用:匿名函數可以賦值變量,lambda也是一個函數對象,也可以賦值給變量,然後通過變量來調用匿名函數。

#使用:匿名函數可以作爲返回值返回,如在函數定義裏最後return lambda :x*x + y*y
#注意:匿名函數作爲返回值返回,等同於return子函數,就是一個閉包函數。

#注意:匿名函數有個限制:只能有一個表達式,不用填寫return,返回值就是該表達式的結果



#可以使用 lambda匿名函數 結合 map高階函數 來計算乘方
list(map(lambda x: x*x,[1,2,3,4,5,6,7,8,9]))

#使用:匿名函數可以賦值變量,lambda也是一個函數對象,也可以賦值給變量,然後通過變量來調用匿名函數。
f=lambda x: x*x
f(5)
f

#使用:匿名函數作爲返回值返回,如在函數定義裏最後return lambda :x*x + y*y
#注意:匿名函數作爲返回值返回,等同於return子函數,就是一個閉包函數。 
def build(x,y):
    """ 匿名函數作爲返回值返回,等同於return子函數,就是一個閉包函數
    注意:閉包函數注意 return的返回指向是否存在()的調用形式"""
    return lambda :x*x + y*y
b=build(5,10)
b()

#可以使用lambda匿名函數 結合 map高階函數 來過濾偶數
list(filter(lambda n: n%2==1,range(1,21)))




################### 裝飾器@(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__




#################### 偏函數functools.partial()
#functools 模塊中主要包含了一些函數裝飾器和便捷的功能函數。其中一個就是偏函數(Partial function)。
#偏函數可以通過設定參數的默認值,從而降低函數調用的難度。

#使用:functools.partial()用來創建一個偏函數,即把一個函數的某些參數給固定住(即設置默認值),返回一個新函數。
#注意:偏函數實質上是一個 @裝飾器 的原理,即在參數列表裏追加進去 設置默認值的參數。
#如:int2('1000') 實則是運行的 int('1000',base=2)


#修改int()函數的默認base參數,來達到 N進制的轉換。
int('1000', base=8)    #八進制:十位數上表示*8、百位數上表示64(8*8)、千位數上表示512(8*8*8)
int('10000',base=2)     #二進制:十位數上表示*2、百位數上表示4(2*2)、千位數上表示8(2*2*2)


#使用:functools.partial()用來創建一個偏函數,即把一個函數的某些參數給固定住(即設置默認值),返回一個新函數。
import functools

int2=functools.partial(int,base=2)
int2('1000')

#注意:偏函數實質上是一個 @裝飾器 的原理,即在參數列表裏追加進去 設置默認值的參數。
#如:int2('1000') 實則是運行的 int('1000',base=2)

max2=functools.partial(max,10)
max2(2,5,9)    #max2(2,5,9) 實則是運行的max(2,5,9,10)

















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