python的閉包及裝飾器


閉包:python


閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即便已經離開了創造它的環境也不例外。因此,有另外一種說法認爲閉包是由函數和與其相關的引用環境組合而成的實體bash

一、函數是一個對象閉包

二、函數執行完成後內部變量回收ide

三、函數屬性函數

四、函數的返回值優化

實例1、spa

分別檢測分數科目總分爲100、150兩種狀況的成績對象

初級代碼以下:ip

# -*- coding:utf-8 -*-

###檢測分數總數爲100分的及格狀況
def ck_100(val):
    passline = 60
    if val >= passline:
        print "當前科目總分爲100,你的成績爲: %d,成績經過 " %val
    else:
        print "當前科目總分爲100,你的成績爲: %d,成績不經過 " %val

###檢測分數總數爲150分的及格狀況
def ck_150(val):
    passline = 90
    if val >= passline:
        print "當前科目總分爲150,你的成績爲: %d,成績經過 " %val
    else:
        print "當前科目總分爲100,你的成績爲: %d,成績不經過" %val

#100總分科目
ck_100(90)
ck_100(55)
#150總分科目
ck_150(110)
ck_150(88)

執行結果以下:utf-8

>>> ================================ RESTART ================================
>>> 
當前科目總分爲100,你的成績爲: 90,成績經過 
當前科目總分爲100,你的成績爲: 55,成績不經過 
當前科目總分爲150,你的成績爲: 110,成績經過 
當前科目總分爲100,你的成績爲: 88,成績不經過
>>>


使用閉包優化後的代碼:

# -*- coding:utf-8 -*-

###根據不一樣科目設置及格線
def set_passline(passline):
###檢測傳入分數是否及格
    def cmp(val):
        if val >= passline:
            print "及格線爲: %d, 您的分數爲: %d ,恭喜及格" %(passline,val)
        else:
            print "及格線爲: %d, 您的分數爲: %d ,遺憾不及格" %(passline,val)
    return cmp

#總分爲100的科目
score_100 = set_passline(60)
score_100(89)
score_100(55)
#總分爲150的科目
score_150 = set_passline(90)
score_150(99)
score_150(88)


執行結果:

>>> ================================ RESTART ================================
>>> 
及格線爲: 60, 您的分數爲: 89 ,恭喜及格
及格線爲: 60, 您的分數爲: 55 ,遺憾不及格
及格線爲: 90, 您的分數爲: 99 ,恭喜及格
及格線爲: 90, 您的分數爲: 88 ,遺憾不及格


Tips:

能夠看代碼量少了一半;

閉包就是咱們內置函數對 enclosing 做用域  變量  的使用


實例2、

# -*- coding:utf-8 -*-
###求成績總分
def my_sum(*args):
    return sum(args)

###求成績平均分
def my_average(*args):
    return sum(args)/len(args)

print my_sum(1,2,3,4,5)
print my_average(1,2,3,4,5)

執行結果:

>>> ================================ RESTART ================================
>>> 
15
3
>>>

發現上述代碼不夠健壯,若是你傳入字符串或空的列表就會報錯

print my_sum(1,2,3,4,5,'6')
    return sum(args)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>>

修改代碼,進行檢查傳入數據的類型及長度

# -*- coding:utf-8 -*-
###求成績總分
def my_sum(*args):
    if len(args) == 0:
        return 0
    for val in args:
        if not isinstance(val,int):
            print "傳入參數有非整數性..."
            return 0
    return sum(args)

###求成績平均分
def my_average(*args):
    if len(args) == 0:
        return 0
    for val in args:
        if not isinstance(val,int):
            print "傳入參數有非整數性..."
            return 0
    return sum(args)/len(args)

print my_sum(1,2,3,4,5)
print my_average(1,2,3,4,5)
print my_sum(1,2,3,4,5,'6')

執行結果:

>>> ================================ RESTART ================================
>>> 
15
3
傳入參數有非整數性...
0

健壯性是能夠了,不過發現其中有一部門代碼是重複的,可是執行的 函數 又是不一樣的,分別爲 my_sum 、my_average


使用閉包優化後的代碼:

# -*- coding:utf-8 -*-
###求成績總分
def my_sum(*args):
    return sum(args)

###求成績平均分
def my_average(*args):
    return sum(args)/len(args)

def dec(func):
    def in_dec(*args):
        if len(args) == 0:
            print "傳入參數列表爲0"
            return 0
        for val in args:
            if not isinstance(val,int):
                print "傳入非整數參數"
                return 0
        return func(*args)
    return in_dec

#從新定義my_sum

my_sum = dec(my_sum)

#此處調用dec,做用就是將 參數爲函數的 func 變爲 in_dec 函數的一個屬性 ( enclosing 屬性 ),而且將 in_dec 函數自己返回,返回的函數還具備 in_dec 自己的功能屬性 (檢測參數的做用)
#而且 my_sum 進行了從新定義,返回值爲 傳入函數處理後的 返回值

print my_sum(1,2,3,4,5)
print my_sum()
print my_sum(1,2,3,4,5,'6')
#從新定義my_average
my_average = dec(my_average)
print my_average(1,2,3,4,5)
print my_average()
print my_average(1,2,3,4,5,'6')

執行結果:

>>> ================================ RESTART ================================
>>> 
15
傳入參數列表爲0
0
傳入非整數參數
0
3
傳入參數列表爲0
0
傳入非整數參數
0
>>>

裝飾器:

一、用來裝飾函數

二、返回一個新的函數對象

三、被裝飾函數標識符指向返回的函數對象

四、語法糖: @deco

上述閉包的例子,in_dec 被 dec 所裝飾,那能夠說裝飾器其實就是閉包的一個本質使用


爲了演示 裝飾器 的功能,以上述代碼爲例,進行 裝飾器 寫法

# -*- coding:utf-8 -*-

def dec(func):
    print "call dec"
    def in_dec(*args):
        print "call in_dec"
        if len(args) == 0:
            print "傳入參數列表爲0"
            return 0
        for val in args:
            if not isinstance(val,int):
                print "傳入非整數參數"
                return 0
        return func(*args)
    return in_dec    


@dec
###求成績總分
def my_sum(*args):
    return sum(args)

###求成績平均分
def my_average(*args):
    return sum(args)/len(args)

因此沒有顯示調用,可是執行如下:

>>> ================================ RESTART ================================
>>> 
call dec
>>>

結果證實 調用 了 dec 函數,那應該是 裝飾器語法糖 @dec 時進行了調用,可是它返回了一個函數,那被誰接受了呢? 是被他裝飾的 my_sum 接受了。

爲了證實,來調用下 my_sum

print my_sum(1,2,3,4,5)
print my_sum(1,2,3,4,5,'6')

結果:

>>> ================================ RESTART ================================
>>> 
call dec      ####### 語法糖調用一次裝飾函數 dec
call in_dec
15
call in_dec
傳入非整數參數
0
>>>

證實是對的;

@dec 至關於上述的

my_sum = dec(my_sum)

也就能夠認爲 被從新賦值的 my_sum 至關於裝飾器 dec 的內部函數 in_dec

裝飾器 dec 的參數 func 至關於 被修飾的函數 my_sum

裝飾器 實例2、

# -*- coding:utf-8 -*-

def dec(func):
    print "call dec"
    def in_dec():
        print "call in_dec"
        func()

@dec
def test_dec():
    print "call test_dec"

print type(test_dec)
test_dec()

執行結果:

>>> ================================ RESTART ================================
>>> 
call dec
<type 'NoneType'>

Traceback (most recent call last):
  File "D:\xisuo\xx\q.py", line 15, in <module>
    test_dec()
TypeError: 'NoneType' object is not callable
>>>

能夠看到這裏的 test_dec 是 NoneType類型,沒法調用,那是由於咱們在裝飾器函數沒有進行顯示的 return ,python 默認返回 None,

因此裝飾器函數必須顯示的進行 return in_func (這裏爲 return in_dec)


修改後 執行

# -*- coding:utf-8 -*-

def dec(func):
    print "call dec"
    def in_dec():
        print "call in_dec"
        func()
    return in_dec      #結論:必須返回

@dec
def test_dec():
    print "call test_dec"

print type(test_dec)
test_dec()


>>> ================================ RESTART ================================
>>> 
call dec
<type 'function'>
call in_dec
call test_dec
>>>

上面提到了(紅字)函數間的關係(至關於對象引用關係),那賦值的被修飾函數的 參數 就必須三個函數都對應起來

# -*- coding:utf-8 -*-

def dec(func):
    print "call dec"
    def in_dec(x,y):      ##參數要對應起來
        print "call in_dec"
        func(x,y)       ##參數要對應起來
    return in_dec

@dec
def test_dec(x,y):     ##參數要對應起來
    print "call test_dec ",x + y

print type(test_dec)
test_dec(3,5)

執行結果:


>>> ================================ RESTART ================================
>>> 
call dec
<type 'function'>
call in_dec
call test_dec  8
>>>
相關文章
相關標籤/搜索