[19/10/13-星期日] Python中的函數

1、函數python

# 第五章 函數

## 函數簡介(function)
    - 函數也是一個對象
    - 對象是內存中專門用來存儲數據的一塊區域
    - 函數能夠用來保存一些可執行的代碼,而且能夠在須要時,對這些語句進行屢次的調用
    - 建立函數:
        def 函數名([形參1,形參2,...形參n]) :
            代碼塊
        - 函數名必需要符號標識符的規範
            (能夠包含字母、數字、下劃線、可是不能以數字開頭)    
    - 函數中保存的代碼不會當即執行,須要調用函數代碼纔會執行
    - 調用函數:
        函數對象()
    - 定義函數通常都是要實現某種功能的    

## 函數的參數
    - 在定義函數時,能夠在函數名後的()中定義數量不等的形參,
        多個形參之間使用,隔開
    - 形參(形式參數),定義形參就至關於在函數內部聲明瞭變量,可是並不賦值
    - 實參(實際參數)
        - 若是函數定義時,指定了形參,那麼在調用函數時也必須傳遞實參,
            實參將會賦值給對應的形參,簡單來講,有幾個形參就得傳幾個實參

    - 練習1:
        定義一個函數,能夠用來求任意三個數的乘積

    - 練習2:
        定義一個函數,能夠根據不一樣的用戶名顯示不一樣的歡迎信息    

## 函數式編程
    - 在Python中,函數是一等對象
    - 一等對象通常都會具備以下特色:
        ① 對象是在運行時建立的
        ② 能賦值給變量或做爲數據結構中的元素
        ③ 能做爲參數傳遞
        ④ 能做爲返回值返回
        
    - 高階函數
        - 高階函數至少要符合如下兩個特色中的一個
          ① 接收一個或多個函數做爲參數
          ② 將函數做爲返回值返回 

    - 裝飾器         

2、函數和參數編程

# 好比有以下三行代碼,這三行代碼是一個完整的功能
# print('Hello')
# print('你好')
# print('再見')

# 定義一個函數
def fn() :
    print('這是個人第一個函數!')
    print('hello')
    print('今每天氣真不錯!')

# 打印fn
# print(fn) <function fn at 0x03D2B618>
# print(type(fn)) <class 'function'>

# fn是函數對象  fn()調用函數
# print是函數對象 print()調用函數
# fn()

# 定義一個函數,能夠用來求任意兩個數的和
# def sum() :
#     a = 123
#     b = 456
#     print(a + b)

# sum()

# 定義函數時指定形參
def fn2(a , b) :
    # print('a =',a)
    # print('b =',b)
    print(a,"+",b,"=",a + b)

# 調用函數時,來傳遞實參
fn2(10,20)
fn2(123,456)


# 求任意三個數的乘積
def mul(a,b,c):
    print(a*b*c)

# 根據不一樣的用戶名顯示不一樣的歡迎信息   
def welcome(username):
    print('歡迎',username,'光臨')

# mul(1,2,3)   
# welcome('孫悟空') 

# 定義一個函數
# 定義形參時,能夠爲形參指定默認值
# 指定了默認值之後,若是用戶傳遞了參數則默認值沒有任何做用
#   若是用戶沒有傳遞,則默認值就會生效
def fn(a = 5 , b = 10 , c = 20):
    print('a =',a)
    print('b =',b)
    print('c =',c)

# fn(1 , 2 , 3)
# fn(1 , 2)
# fn()

# 實參的傳遞方式
# 位置參數
# 位置參數就是將對應位置的實參複製給對應位置的形參
# 第一個實參賦值給第一個形參,第二個實參賦值給第二個形參 。。。
# fn(1 , 2 , 3)

# 關鍵字參數
# 關鍵字參數,能夠不按照形參定義的順序去傳遞,而直接根據參數名去傳遞參數
# fn(b=1 , c=2 , a=3)
# print('hello' , end='')
# 位置參數和關鍵字參數能夠混合使用
# 混合使用關鍵字和位置參數時,必須將位置參數寫到前面
# fn(1,c=30)

def fn2(a):
    print('a =',a)

# 函數在調用時,解析器不會檢查實參的類型
# 實參能夠傳遞任意類型的對象
b = 123
b = True
b = 'hello'
b = None
b = [1,2,3]

# fn2(b)    
fn2(fn)

def fn3(a , b):
    print(a+b)

# fn3(123,"456")

def fn4(a):
    # 在函數中對形參進行從新賦值,不會影響其餘的變量
    # a = 20
    # a是一個列表,嘗試修改列表中的元素
    # 若是形參執行的是一個對象,當咱們經過形參去修改對象時
    #   會影響到全部指向該對象的變量
    a[0] = 30
    print('a =',a,id(a))

c = 10   
c = [1,2,3] 

# fn4(c)
# fn4(c.copy())
# fn4(c[:])

# print('c =',c,id(c))

# 不定長的參數
# 定義一個函數,能夠求任意個數字的和
def sum(*nums):
    # 定義一個變量,來保存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    print(result)


# sum(123,456,789,10,20,30,40)

# 在定義函數時,能夠在形參前邊加上一個*,這樣這個形參將會獲取到全部的實參
# 它將會將全部的實參保存到一個元組中
# a,b,*c = (1,2,3,4,5,6)

# *a會接受全部的位置實參,而且會將這些實參統一保存到一個元組中(裝包)
def fn(*a):
    print("a =",a,type(a))

# fn(1,2,3,4,5)
# 帶星號的形參只能有一個
# 帶星號的參數,能夠和其餘參數配合使用
# 第一個參數給a,第二個參數給b,剩下的都保存到c的元組中
# def fn2(a,b,*c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 可變參數不是必須寫在最後,可是注意,帶*的參數後的全部參數,必須以關鍵字參數的形式傳遞
# 第一個參數給a,剩下的位置參數給b的元組,c必須使用關鍵字參數
# def fn2(a,*b,c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 全部的位置參數都給a,b和c必須使用關鍵字參數
# def fn2(*a,b,c):
#     print('a =',a)
#     print('b =',b)
#     print('c =',c)

# 若是在形參的開頭直接寫一個*,則要求咱們的全部的參數必須以關鍵字參數的形式傳遞
def fn2(*,a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)
# fn2(a=3,b=4,c=5)

# *形參只能接收位置參數,而不能接收關鍵字參數
# def fn3(*a) :
#     print('a =',a)

# **形參能夠接收其餘的關鍵字參數,它會將這些參數統一保存到一個字典中
#   字典的key就是參數的名字,字典的value就是參數的值
# **形參只能有一個,而且必須寫在全部參數的最後
def fn3(b,c,**a) :
    print('a =',a,type(a))
    print('b =',b)
    print('c =',c)

# fn3(b=1,d=2,c=3,e=10,f=20)

# 參數的解包(拆包)
def fn4(a,b,c):
    print('a =',a)
    print('b =',b)
    print('c =',c)

# 建立一個元組
t = (10,20,30)

# 傳遞實參時,也能夠在序列類型的參數前添加星號,這樣他會自動將序列中的元素依次做爲參數傳遞
# 這裏要求序列中元素的個數必須和形參的個數的一致
# fn4(*t)    

# 建立一個字典
d = {'a':100,'b':200,'c':300}
# 經過 **來對一個字典進行解包操做
fn4(**d)

3、返回值數據結構

# 返回值,返回值就是函數執行之後返回的結果
# 能夠經過 return 來指定函數的返回值
# 能夠之間使用函數的返回值,也能夠經過一個變量來接收函數的返回值

def sum(*nums):
    # 定義一個變量,來保存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    print(result)

# sum(123,456,789)
 

# return 後邊跟什麼值,函數就會返回什麼值
# return 後邊能夠跟任意的對象,返回值甚至能夠是一個函數
def fn():
    # return 'Hello'
    # return [1,2,3]
    # return {'k':'v'}
    def fn2() :
        print('hello')

    return fn2 # 返回值也能夠是一個函數

r = fn() # 這個函數的執行結果就是它的返回值
# r()
# print(fn())
# print(r)

# 若是僅僅寫一個return 或者 不寫return,則至關於return None 
def fn2() :
    a = 10
    return 

# 在函數中,return後的代碼都不會執行,return 一旦執行函數自動結束
def fn3():
    print('hello')
    return
    print('abc')

# r = fn3()
# print(r)

def fn4() :
    for i in range(5):
        if i == 3 :
            # break 用來退出當前循環
            # continue 用來跳過當次循環
            return # return 用來結束函數fn4
        print(i)
    print('循環執行完畢!')

# fn4()

def sum(*nums):
    # 定義一個變量,來保存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums :
        result += n
    return result

r = sum(123,456,789)

# print(r + 778)

def fn5():
    return 10

# fn5 和 fn5()的區別
print(fn5) # fn5是函數對象,打印fn5實際是在打印函數對象 <function fn5 at 0x05771BB8>
print(fn5()) # fn5()是在調用函數,打印fn5()其實是在打印fn5()函數的返回值 10

4、文檔字符串與做用域閉包

# help()是Python中的內置函數
# 經過help()函數能夠查詢python中的函數的用法
# 語法:help(函數對象)
# help(print) # 獲取print()函數的使用說明

# 文檔字符串(doc str)
# 在定義函數時,能夠在函數內部編寫文檔字符串,文檔字符串就是函數的說明
#   當咱們編寫了文檔字符串時,就能夠經過help()函數來查看函數的說明
#   文檔字符串很是簡單,其實直接在函數的第一行寫一個字符串就是文檔字符串
def fn(a:int,b:bool,c:str='hello') -> int:
    '''
    這是一個文檔字符串的示例

    函數的做用:。。。。。
    函數的參數:
        a,做用,類型,默認值。。。。
        b,做用,類型,默認值。。。。
        c,做用,類型,默認值。。。。
    '''
    return 10

help(fn)
# 做用域(scope)
# 做用域指的是變量生效的區域
b = 20 # 全局變量

def fn():
    a = 10 # a定義在了函數內部,因此他的做用域就是函數內部,函數外部沒法訪問
    print('函數內部:','a =',a)
    print('函數內部:','b =',b)

# fn()    
  

# print('函數外部:','a =',a)
# print('函數外部:','b =',b)

# 在Python中一共有兩種做用域
#  全局做用域
#   - 全局做用域在程序執行時建立,在程序執行結束時銷燬
#   - 全部函數之外的區域都是全局做用域
#   - 在全局做用域中定義的變量,都屬於全局變量,全局變量能夠在程序的任意位置被訪問
#   
#  函數做用域
#   - 函數做用域在函數調用時建立,在調用結束時銷燬
#   - 函數每調用一次就會產生一個新的函數做用域
#   - 在函數做用域中定義的變量,都是局部變量,它只能在函數內部被訪問
#   
#  變量的查找
#   - 當咱們使用變量時,會優先在當前做用域中尋找該變量,若是有則使用,
#       若是沒有則繼續去上一級做用域中尋找,若是有則使用,
#       若是依然沒有則繼續去上一級做用域中尋找,以此類推
#       直到找到全局做用域,依然沒有找到,則會拋出異常
#           NameError: name 'a' is not defined

def fn2():
    def fn3():
        print('fn3中:','a =',a)
    fn3()

# fn2()    

a = 20

def fn3():
    # a = 10 # 在函數中爲變量賦值時,默認都是爲局部變量賦值
    # 若是但願在函數內部修改全局變量,則須要使用global關鍵字,來聲明變量
    global a # 聲明在函數內部的使用a是全局變量,此時再去修改a時,就是在修改全局的a
    a = 10 # 修改全局變量
    print('函數內部:','a =',a)

# fn3()
# print('函數外部:','a =',a)


# 命名空間(namespace)
# 命名空間指的是變量存儲的位置,每個變量都須要存儲到指定的命名空間當中
# 每個做用域都會有一個它對應的命名空間
# 全局命名空間,用來保存全局變量。函數命名空間用來保存函數中的變量
# 命名空間實際上就是一個字典,是一個專門用來存儲變量的字典

# locals()用來獲取當前做用域的命名空間
# 若是在全局做用域中調用locals()則獲取全局命名空間,若是在函數做用域中調用locals()則獲取函數命名空間
# 返回的是一個字典
scope = locals() # 當前命名空間
print(type(scope))
# print(a)
# print(scope['a'])
# 向scope中添加一個key-value
scope['c'] = 1000 # 向字典中添加key-value就至關於在全局中建立了一個變量(通常不建議這麼作)
# print(c)

def fn4():
    a = 10
    # scope = locals() # 在函數內部調用locals()會獲取到函數的命名空間
    # scope['b'] = 20 # 能夠經過scope來操做函數的命名空間,可是也是不建議這麼作

    # globals() 函數能夠用來在任意位置獲取全局命名空間
    global_scope = globals()
    # print(global_scope['a'])
    global_scope['a'] = 30
    # print(scope)

fn4()    

5、遞歸app

# 嘗試求10的階乘(10!)
# 1! = 1
# 2! = 1*2 = 2
# 3! = 1*2*3 = 6
# 4! = 1*2*3*4 = 24

# print(1*2*3*4*5*6*7*8*9*10)

# 建立一個變量保存結果
# n = 10
# for i in range(1,10):
#     n *= i

# print(n)

#求階乘
# range()是一個函數,能夠用來生成一個天然數的序列
# r = range(5) # 生成一個這樣的序列[0,1,2,3,4]
# r = range(0,10,2)
# r = range(10,0,-1) #輸出10,9,8,7,6,5,4,3,2,1 這10個數
# 經過range()能夠建立一個執行指定次數的for循環
# for()循環除了建立方式之外,其他的都和while同樣,
#   包括else、包括break continue均可以在for循環中使用
#   而且for循環使用也更加簡單
# 將以前使用while循環作的練習,再使用for循環完成一次!
# for i in range(30):
#     print(i) #輸出0-29的天然數

# 建立一個函數,能夠用來求任意數的階乘
def factorial(n):
    '''
        該函數用來求任意數的階乘

        參數:
            n 要求階乘的數字
    '''

    # 建立一個變量,來保存結果
    result = n
    
    for i in range(1,n):
        result *= i

    return result    

# 求10的階乘    
# print(factorial(20))

# 遞歸式的函數
# 從前有座山,山裏有座廟,廟裏有個老和尚講故事,講的什麼故事呢?
#   從前有座山,山裏有座廟,廟裏有個老和尚講故事,講的什麼故事呢?....
# 遞歸簡單理解就是本身去引用本身!
# 遞歸式函數,在函數中本身調用本身!

# 無窮遞歸,若是這個函數被調用,程序的內存會溢出,效果相似於死循環
# def fn():
#     fn()
# fn()

# 遞歸是解決問題的一種方式,它和循環很像
#   它的總體思想是,將一個大問題分解爲一個個的小問題,直到問題沒法分解時,再去解決問題
# 遞歸式函數的兩個要件
#   1.基線條件
#       - 問題能夠被分解爲的最小問題,當知足基線條件時,遞歸就不在執行了
#   2.遞歸條件
#       - 將問題繼續分解的條件
# 遞歸和循環相似,基本是能夠互相代替的,
#   循環編寫起來比較容易,閱讀起來稍難
#   遞歸編寫起來難,可是方便閱讀
# 10! = 10 * 9!
# 9! = 9 * 8!
# 8! = 8 * 7!
# ...
# 1! = 1

def factorial(n):
    '''
        該函數用來求任意數的階乘

        參數:
            n 要求階乘的數字
    '''
    # 基線條件 判斷n是否爲1,若是爲1則此時不能再繼續遞歸
    if n == 1 :
        # 1的階乘就是1,直接返回1
        return 1

    # 遞歸條件    
    return n * factorial(n-1)

# print(factorial(10))

# 練習
#   建立一個函數 power 來爲任意數字作冪運算 n ** i
#   10 ** 5 = 10 * 10 ** 4
#   10 ** 4 = 10 * 10 ** 3
#   ...
#   10 ** 1 = 10
def power(n , i):
    '''
        power()用來爲任意的數字作冪運算

        參數:
            n 要作冪運算的數字
            i 作冪運算的次數
    '''
    # 基線條件
    if i == 1:
        # 求1次冪
        return n
    # 遞歸條件
    return n * power(n , i-1)

# print(power(8,6))    



#   
# 練習
#   建立一個函數,用來檢查一個任意的字符串是不是迴文字符串,若是是返回True,不然返回False
#   迴文字符串,字符串從前日後念和從後往前念是同樣的
#       abcba
#   abcdefgfedcba
#   先檢查第一個字符和最後一個字符是否一致,若是不一致則不是迴文字符串
#       若是一致,則看剩餘的部分是不是迴文字符串
#   檢查 abcdefgfedcba 是否是迴文
#   檢查 bcdefgfedcb 是否是迴文
#   檢查 cdefgfedc 是否是迴文
#   檢查 defgfed 是否是迴文
#   檢查 efgfe 是否是迴文
#   檢查 fgf 是否是迴文
#   檢查 g 是否是迴文

def hui_wen(s):
    '''
        該函數用來檢查指定的字符串是否迴文字符串,若是是返回True,不然返回False

        參數:
            s:就是要檢查的字符串
    '''
    # 基線條件
    if len(s) < 2 :
        # 字符串的長度小於2,則字符串必定是迴文
        return True
    elif s[0] != s[-1]:
        # 第一個字符和最後一個字符不相等,不是迴文字符串
        return False    
    # 遞歸條件    
    return hui_wen(s[1:-1])

# def hui_wen(s):
#     '''
#         該函數用來檢查指定的字符串是否迴文字符串,若是是返回True,不然返回False

#         參數:
#             s:就是要檢查的字符串
#     '''
#     # 基線條件
#     if len(s) < 2 :
#         # 字符串的長度小於2,則字符串必定是迴文
#         return True
#     # 遞歸條件    
#     return s[0] == s[-1] and hui_wen(s[1:-1])

print(hui_wen('abcdefgfedcba'))    

 6、高階函數函數式編程

# 高階函數
# 接收函數做爲參數,或者將函數做爲返回值的函數是高階函數
# 當咱們使用一個函數做爲參數時,其實是將指定的代碼傳遞進了目標函數

# 建立一個列表
l = [1,2,3,4,5,6,7,8,9,10]

# 定義一個函數
#   能夠將指定列表中的全部的偶數,保存到一個新的列表中返回

# 定義一個函數,用來檢查一個任意的數字是不是偶數
def fn2(i) :
    if i % 2 == 0 :
        return True

    return False    

# 這個函數用來檢查指定的數字是否大於5
def fn3(i):
    if i > 5 : 
        return True    
    return False

def fn(func , lst) :

    '''
        fn()函數能夠將指定列表中的全部偶數獲取出來,並保存到一個新列表中返回

        參數:
            lst:要進行篩選的列表
    '''
    # 建立一個新列表
    new_list = []

    # 對列表進行篩選
    for n in lst :
        # 判斷n的奇偶
        if func(n) :
            new_list.append(n)
        # if n > 5 :
        #     new_list.append(n)

            


    # 返回新列表
    return new_list

# def fn4(i):
#     if i % 3 == 0:
#         return True    
#     return False

def fn4(i):
    return i % 3 == 0
        
# print(fn(fn4 , l))

# filter()
# filter()能夠從序列中過濾出符合條件的元素,保存到一個新的序列中
# 參數:
#  1.函數,根據該函數來過濾序列(可迭代的結構)
#  2.須要過濾的序列(可迭代的結構)
# 返回值:
#   過濾後的新序列(可迭代的結構)

# fn4是做爲參數傳遞進filter()函數中
#   而fn4實際上只有一個做用,就是做爲filter()的參數
#   filter()調用完畢之後,fn4就已經沒用
# 匿名函數 lambda 函數表達式 (語法糖)
#   lambda函數表達式專門用來建立一些簡單的函數,他是函數建立的又一種方式
#   語法:lambda 參數列表 : 返回值
#   匿名函數通常都是做爲參數使用,其餘地方通常不會使用

def fn5(a , b):
    return a + b

# (lambda a,b : a + b)(10,20)
# 也能夠將匿名函數賦值給一個變量,通常不會這麼作
fn6 = lambda a,b : a + b
# print(fn6(10,30))


r = filter(lambda i : i > 5 , l)
# print(list(r))

# map()
# map()函數能夠對可跌代對象中的全部元素作指定的操做,而後將其添加到一個新的對象中返回
l = [1,2,3,4,5,6,7,8,9,10]

r = map(lambda i : i ** 2 , l)

# print(list(r))

# sort()
# 該方法用來對列表中的元素進行排序
# sort()方法默認是直接比較列表中的元素的大小
# 在sort()能夠接收一個關鍵字參數 , key
#   key須要一個函數做爲參數,當設置了函數做爲參數
#   每次都會以列表中的一個元素做爲參數來調用函數,而且使用函數的返回值來比較元素的大小
l = ['bb','aaaa','c','ddddddddd','fff']
# l.sort(key=len) #根據字符串的長度去排序

l = [2,5,'1',3,'6','4']
l.sort(key=int) #根據int型去排序 ,把每一個字符串轉成int去排序
# print(l)

# sorted()
# 這個函數和sort()的用法基本一致,可是sorted()能夠對任意的序列進行排序
#   而且使用sorted()排序不會影響原來的對象,而是返回一個新對象

l = [2,5,'1',3,'6','4']
# l = "123765816742634781" #這個字符串也能排序 

print('排序前:',l)
print(sorted(l,key=int))
print('排序後:',l)

閉包函數

# 將函數做爲返回值返回,也是一種高階函數
# 這種高階函數咱們也稱爲叫作閉包,經過閉包能夠建立一些只有當前函數能訪問的變量
#   能夠將一些私有的數據藏到的閉包中

def fn():

    a = 10

    # 函數內部再定義一個函數
    def inner():
        print('我是fn2' , a)

    # 將內部函數 inner做爲返回值返回   
    return inner

# r是一個函數,是調用fn()後返回的函數
# 這個函數實在fn()內部定義,並非全局函數
# 因此這個函數老是能訪問到fn()函數內的變量
r = fn()    

# r()

# 求多個數的平均值
# nums = [50,30,20,10,77]

# sum()用來求一個列表中全部元素的和。一個函數
# print(sum(nums)/len(nums))

# 造成閉包的要件
#   ① 函數嵌套
#   ② 將內部函數做爲返回值返回
#   ③ 內部函數必需要使用到外部函數的變量

#相似於Java中的封裝
def make_averager():
    # 建立一個列表,用來保存數值
    nums = []

    # 建立一個函數,用來計算平均值
    def averager(n) :
        # 將n添加到列表中
        nums.append(n)
        # 求平均值
        return sum(nums)/len(nums)

    return averager

averager = make_averager()

print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))

裝飾器ui

# 建立幾個函數

def add(a , b):
    '''
        求任意兩個數的和
    '''
    r = a + b
    return r


def mul(a , b):
    '''
        求任意兩個數的積
    '''
    r = a * b
    return r    

# 但願函數能夠在計算前,打印開始計算,計算結束後打印計算完畢
#  咱們能夠直接經過修改函數中的代碼來完成這個需求,可是會產生如下一些問題
#   ① 若是要修改的函數過多,修改起來會比較麻煩
#   ② 而且不方便後期的維護
#   ③ 而且這樣作會違反開閉原則(OCP)
#           程序的設計,要求開發對程序的擴展,要關閉對程序的修改


# r = add(123,456)
# print(r)

# 咱們但願在不修改原函數的狀況下,來對函數進行擴展
def fn():
    print('我是fn函數....')

# 只須要根據現有的函數,來建立一個新的函數
def fn2():
    print('函數開始執行~~~')
    fn()
    print('函數執行結束~~~')

# fn2()    

def new_add(a,b):
    print('計算開始~~~')
    r = add(a,b)
    print('計算結束~~~')
    return r

# r = new_add(111,222)    
# print(r)

# 上邊的方式,已經能夠在不修改源代碼的狀況下對函數進行擴展了
#   可是,這種方式要求咱們每擴展一個函數就要手動建立一個新的函數,實在是太麻煩了
#   爲了解決這個問題,咱們建立一個函數,讓這個函數能夠自動的幫助咱們生產函數

def begin_end(old):
    '''
        用來對其餘函數進行擴展,使其餘函數能夠在執行前打印開始執行,執行後打印執行結束

        參數:
            old 要擴展的函數對象
    '''
    # 建立一個新函數
    def new_function(*args , **kwargs):
        print('開始執行~~~~')
        # 調用被擴展的函數
        result = old(*args , **kwargs)
        print('執行結束~~~~')
        # 返回函數的執行結果
        return result

    # 返回新函數        
    return new_function

f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)

# r = f()
# r = f2(123,456)
# r = f3(123,456)
# print(r)
# 向begin_end()這種函數咱們就稱它爲裝飾器
#   經過裝飾器,能夠在不修改原來函數的狀況下來對函數進行擴展
#   在開發中,咱們都是經過裝飾器來擴展函數的功能的
# 在定義函數時,能夠經過@裝飾器,來使用指定的裝飾器,來裝飾當前的函數
#   能夠同時爲一個函數指定多個裝飾器,這樣函數將會安裝從內向外的順序被裝飾 

def fn3(old):
    '''
        用來對其餘函數進行擴展,使其餘函數能夠在執行前打印開始執行,執行後打印執行結束

        參數:
            old 要擴展的函數對象
    '''
    # 建立一個新函數
    def new_function(*args , **kwargs):
        print('fn3裝飾~開始執行~~~~')
        # 調用被擴展的函數
        result = old(*args , **kwargs)
        print('fn3裝飾~執行結束~~~~')
        # 返回函數的執行結果
        return result

    # 返回新函數        
    return new_function

@fn3
@begin_end
def say_hello():
    print('你們好~~~')

say_hello()
相關文章
相關標籤/搜索