1. 程式人生 > >閉包與裝飾器簡單概括

閉包與裝飾器簡單概括

     裝飾器在Python2.4 就開始使用了,裝飾器可以說是一個比較厲害的功能.但是 我也是剛開始學裝飾器的時候,比較不好理解裝飾器的思想. 我希望我的這篇文章,能給剛開始學習裝飾器的人,帶來更簡單的理解.當然也非常感謝,有那麼多人寫過相關的內容,我也是不斷看別人的部落格,學習.現在我也想分享一下裝飾器,希望可以給剛開始學裝飾器的你,提供一些幫助.

1 閉包

在電腦科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。

1.引入 巢狀函式

def print_msg():
    msg = "zen of python"

    def printer():
        # printer是巢狀函式
        print(msg)

    printer()
    
print_msg()

那麼有沒有一種可能即使脫離了函式本身的作用範圍,區域性變數還可以被訪問得到呢?答案是閉包

def print_msg():
    # print_msg 是外圍函式
    msg = "zen of python"
    def printer():
        # printer 是巢狀函式
        print(msg)
    return
printer another = print_msg() another()

現在來實現一個功能 有一個avg 函式 , 它 的作用是計算不斷 增加的均值 例如整個歷史中某個商品的的平均收盤價,. 每天都會增加新的價格.因此平均值耀考慮到目前為止所有的價格.

avg(10)
10
avg(15)
12.5
avg(10)
11.66


class Average:

    def __init__(self):
        self.prices = []

    def __call__(self, price, *args, **kwargs):
        self.prices.append(price)
        total = sum(self
.prices) return total / len(self.prices) if __name__ == '__main__': avg = Average() print(avg(10)) print(avg(15)) print(avg(10))

這樣實現看起來沒有問題, 來看下 這個實現

def make_averager():
    prices = []

    def averager(price):
        prices.append(price)
        total = sum(prices)
        return total / len(prices)

    return averager


if __name__ == '__main__':
    avg = make_averager()

    print(avg(10))
    print(avg(15))
    print(avg(10))
    


# avg.__code__.co_varnames
# ('price', 'total')
# avg.__code__.co_freevars
# ('prices',)

思考 ??? avg 如何 儲存歷史的值的呢?

對 Average 這個應該比較清楚, self.prices 來儲存這些資訊的呢?
而 make_average 如何儲存這些資訊的呢?
這裡我用綠色畫出來的東西,就是閉包 prices 是什麼呢? 其實就是一個自由的變數. (free variable)

2 裝飾器的執行時間

裝飾器 是在匯入模組 的時候就已經執行.
這和普通的函式有點不太一樣

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco.py
@time: 2018/4/23 下午11:11

"""

registry = []


def register(fun):
    print('running register({})'.format(fun))
    registry.append(fun)
    return fun


@register
def f1():
    print('running f1()')


@register
def f2():
    print('running f2()')


def f3():
    print('running f3()')


def main():
    print('running main()')
    print('registry --> ', registry)


if __name__ == '__main__':
    main()
    
# 結果如下
running register(<function f1 at 0x10eb366a8>)
running register(<function f2 at 0x10eb9b620>)
running main()
registry -->  [<function f1 at 0x10eb366a8>, <function f2 at 0x10eb9b620>]

3 裝飾器

裝飾器是什麼??

裝飾器本質是一個函式,用來修飾你要裝飾的函式,就是那麼簡單,不要想的太複雜了. 

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco2.py
@time: 2018/5/1 上午12:08

"""
import logging


def foo():
    print("I am  foo")


def deco(func2):
    func2()


def use_logging(func):
    logging.warning("%s is running" % func.__name__)
    func()


def use_logging2(func):
    def wrapper():
        logging.warning("%s is running" % func.__name__)
        return func()

    return wrapper


# @use_logging
def fun1():
    print('I am  fun1')


@use_logging2
def fun2():
    print('I am  fun2')


def fun3():
    print('I am  fun3')






if __name__ == '__main__':
    deco(foo)

    use_logging(fun1)

當你的函式需要引數的時候怎麼辦呢?

def wrapper(*args, **kwargs):
        # args是一個數組,kwargs一個字典
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

2 裝飾器 想要儲存源資訊

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco.py
@time: 2018/5/7 下午5:54

"""

import time
from functools import wraps

def timethis(func):
    '''
    Decorator that reports the execution time.
    '''

    # @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end - start)
        return result

    return wrapper


@timethis
def countdown(n):
    '''
    Counts down
    '''
    while n > 0:
        n -= 1


countdown(100000)

# countdown = timethis(countdown)


#  在命令列裡面看看
countdown(100000)
countdown 0.011469841003417969
countdown.__name__
'wrapper'
countdown.__doc__


# 如果加上 wraps
# func __name__, __doc__ 還儲存著.
countdown 0.00823211669921875
countdown.__name__
'countdown'
countdown.__doc__
'\n    Counts down\n    '

3 帶引數的裝飾器

就是裝飾器,也加上引數.
就是在加一層函式,進行包裝.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco.py
@time: 2018/4/28 下午7:43

裝飾器功能,檢查函式的入參, 如果全為空, 或者0 ,None,
直接不進行模型計算,直接返回一個 假的分數. 
如果引數有一個不為空,則進行 fun函式進行計算. 


"""
import inspect
import functools


def checked_arguments(score, prob):
    def _checked_arguments(f):
        @functools.wraps(f)
        def wrapper(*a, **k):
            d = inspect.getcallargs(f, *a, **k)
            valid_val = check_arguments(d)
            if valid_val:
                return f(*a, **k)
            else:
                return {
                    score: -1,
                    prob: 999
                }

        return wrapper

    return _checked_arguments


def check_arguments(d):
    val = d.values()
    return any(val)


@checked_arguments('payday_loan', 'pydayloan_prob')
def fun(a, b, c, d, *args, **kwargs):
    return a + 1, b + 1, c + 1, d + 1


if __name__ == "__main__":
    values = fun(1, 2, 3, 4, 5)
    # values = fun(None, None, None, None)

    print(values)

來讓我們看一下,如何實現,對模型函式,進行入參檢查的,

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco2.py
@time: 2018/5/10 下午11:31

https://docs.python.org/3/library/inspect.html#inspect.Signature.bind
https://docs.python.org/3/library/inspect.html#inspect.BoundArguments

"""
import functools
import inspect


def checked_arguments(f):
    @functools.wraps(f)
    def wrapper(*a, **k):
        sig = inspect.signature(f).bind(*a, **k)
        sig.apply_defaults()
        d = sig.arguments

        valid_val = check_arguments(d)
        if valid_val:
            return f(*a, **k)
        else:
            return {
                'score': -1,
                'prob': 999
            }

    return wrapper



def check_arguments(d):
    args = d.get('args')
    kw = d.get('kwargs')

    if not any(args):
        if not any(kw.values()):
            return False

    return True


@checked_arguments
def run(*args, **kwargs):
    """
    模型函式,計算分值用的.
    :param args:
    :param kwargs:
    :return:
    """
    return (args, kwargs)


class PayDayLoanBD:

    def __init__(self, data):
        self.data = data

    def calculate(self):
        d1 = dict()
        d1.update(self.data)

        result = run(**d1)
        return result




if __name__ == '__main__':
    data = {'name': 'frank', 'tongdun_1': 15, 'hangju': 18, 'jd_prob': 32.5}
    # data = {'name': None, 'tongdun_1': None, 'hangju': None, 'jd_prob': None}
    bd = PayDayLoanBD(data=data)

    val = bd.calculate()

    print(val)



來看下面的方法,如果修飾的不是一個普通的函式,而是一個成員函式,
這裡寫的有點問題. ... 傳入不傳入self, 是看你是否用了self, 如果沒有用到self,就沒有必要顯示的傳入self, 這個變數 會儲存在*args 這個元祖裡面...如果裝飾器裡面需要用到self.xxx ,這個時候就需要顯示的傳入self,來拿值. 就需要注意了, 需要傳 self,寫到包裝函式裡面就可以了.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco3.py
@time: 2018/4/15 下午7:43

"""
import functools


def run(**kwargs):
    """
    模型函式,計算分值用的.
    :param args:
    :param kwargs:
    :return:
    """
    return (kwargs)


def _check_arguments(d):
    return any(d.values())


def checked_arguments(score, prob):
    def _checked_arguments(f):
        @functools.wraps(f)
        def wrapper(self, *args, **kwargs):
            valid_val = _check_arguments(self.data)
            if valid_val:
                return f(self, *args, **kwargs)
            else:
                return {
                    score: -1,
                    prob: 999
                }

        return wrapper

    return _checked_arguments


class ScorecardLargeModel:

    def __init__(self, data):
        self.data = data

    @checked_arguments('cashloan_ab_risk_score', 'cashloan_ab_risk_prob')
    def fun(self):
        kwargs = dict(id_card_number='19920908059')
        kwargs.update(self.data)
        result = run(**kwargs)
        return result


if __name__ == "__main__":
    data = {'0': None, '1': 1111, '2': 2222, '3': None, '4': None, '5': None, '6': None, '7': None, '8': None, '9': None}
    # data = {'0': None, '1': None, '6': None, '7': None, '8': None, '9': None}
    large = ScorecardLargeModel(data)
    val = large.fun()
    print(val)

4 當裝飾器修飾成員函式的時候

這裡寫的有點問題. ... 傳入不傳入self, 是看你是否用了self, 如果沒有用到self,就沒有必要顯示的傳入self, 這個變數 會儲存在*args 這個元祖裡面...如果裝飾器裡面需要用到self.xxx ,這個時候就需要顯示的傳入self,來拿值.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author: Frank 
@contact: [email protected]
@file: test_deco4.py
@time: 2018/4/15 下午9:43

#  當裝飾器 修飾  成員函式的時候.

該如何處理呢?

"""
import functools
import time


def fn_timer(fn):
    """
    計算 fn 的運算時間
    :param fn:
    :return:
    """

    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        end = time.time()
        print(fn.__name__ + '  total running time %s seconds' % str(end - start))
        return result

    return wrapper


def _check_arguments(d):
    return any(d.values())


def checked_arguments(f):
    """
    儲存的code
    :param f:
    :return:
    """

    @functools.wraps(f)
    def wrapper(self, *args, **kwargs):
        valid_val = _check_arguments(self.data)
        if valid_val:
            return f(self, *args, **kwargs)
        else:
            return {
                'score': -1,
                'prob': 999
            }

    return wrapper


class ScorecardLargeModel:
    
    def __init__(self, data):
        self.data = data

    @checked_arguments
    def fun1(self, months):
        return 'fun1  running. months={}'.format(months)

    @fn_timer
    def fun2(self, name):
        time.sleep(0.5)
        return "fun2 running. name={}".format(name)


if __name__ == "__main__":
    # data = {'0': None, '1': 1111, '2': 2222, '3': None, '4': None, '5': None, '6': None, '7': None, '8': None, '9': None}
    data1 = {'0': None, '1': None, '6': None, '7': None, '8': None, '9': None}

    large = ScorecardLargeModel(data1)
    val = large.fun1(3)
    print(val)

    val2 = large.fun2('frank')
    print(val2)



# {'score': -1, 'prob': 999}
# fun2  total running time 0.5017671585083008 seconds
# fun2 running. name=frank

參考連結

總結: 本文總結裝飾器的一些基本的使用, 裝飾器的概念,以及裝飾的執行時間,帶引數的裝飾器等. 

相關推薦

裝飾簡單概括

     裝飾器在Python2.4 就開始使用了,裝飾器可以說是一個比較厲害的功能.但是 我也是剛開始學裝飾器的時候,比較不好理解裝飾器的思想. 我希望我的這篇文章,能給剛開始學習裝飾器的人,帶來更簡單的理解.當然也非常感謝,有那麼多人寫過相關的內容,我也是不斷看別人的部落

Python--裝飾

python 閉包 裝飾器 閉包的意義:返回的函數對象,不僅僅是一個函數對象,在該函數外還包裹了一層作用域,這使得,該函數無論在何處調用,優先使用自己外層包裹的作用域 #應用領域:延遲計算(原來我們是傳參,現在我們是包起來)from urllib.request import urlopen

裝飾

函數名 for循環 a + b 使用 style 裝飾 post 內存地址 lee 一、函數名   函數名在本質上就是函數的內存地址,函數名有以下功能:     函數名可以賦值給別的變量;;     函數名還可以當做容器類型裏面的元素,(列表、字典);     函數名可以當

Python高級--裝飾

定義 修改 pass alt 區別 strong 分享 func 基礎 前言:在Python中,閉包是一種非常有用的功能!它通常與裝飾器一起搭配使用,可以在不改變被裝飾函數的功能的基礎上,完成更多的功能。如權限認證。 一、如何定義閉包   1.閉包就是兩個嵌套的函數,外層函

python裝飾

添加 war 返回值 AR set ret 功能 rgs 區別 閉包閉包:兩個函數的嵌套,外部函數返回內部函數的引用,外部函數一定有參數def 外部函數(參數):   def 內部函數():     pass return 內部函數 他跟函數之間的

13、python中的函數(裝飾

屬性 新的 做的 一個 too 實現 inf 高級 器) 一、嵌套函數 函數的內部又再定義另一個函數,這個函數就叫嵌套函數,裏面含函數就叫內部函數。 示例: 二、返回函數 函數可以接收函數對象作為參數,同理函數也能返回一個函數對象作為返回值。

Python高階——裝飾

閉包 1.函式引數: (1)函式名存放的是函式的地址 (2)函式名()存放的是函式內的程式碼 (3)函式名只是函式程式碼空間的引用,當函式名賦值給一個物件的時候,就是引用傳遞 def func01(): print("func01 is sho

[筆記]裝飾

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

實力講解,一文讀懂Python裝飾

什麼是裝飾器? 裝飾器(Decorator)相對簡單,咱們先介紹它:“裝飾器的功能是將被裝飾的函式當作引數傳遞給與裝飾器對應的函式(名稱相同的函式),並返回包裝後的被裝飾的函式”,聽起來有點繞,沒關係,直接看示意圖,其中 a 為與裝飾器 @a 對應的函式, b 為裝飾器修飾的函式,裝飾器@a的作

Python:裝飾

閉包: 在函式內部再定義一個函式,並且內部這個函式用到了外邊函式的變數,那麼將內部函式以及用到的一些變數稱之為閉包。 # 兩個函式巢狀,外層函式返回內層函式的引用,  外層函式必須傳引數-->外層函式不傳參相當於只定義內層函式,沒有什麼用。 注意點: 由於閉

Day4 裝飾decorator、叠代生成器、面向過程編程、三元表達式、列表解析生成器表達式、序列化反序列化

反序 bsp pic nbsp tor 序列 space 列表解析 列表 http://pic.cnhubei.com/space.php?uid=1774&do=album&id=1362489http://pic.cnhubei.com/space.ph

函數嵌套嘗試裝飾

不能 都沒有 定義 type tro ann bsp local nds 什麽是函數嵌套: 在函數裏面再定義一個函數 def foo(): print(‘from foo‘) def test(): pass def father(au

Day 19 函數之裝飾

false print glob src true success 返回值 count please 一、什麽是裝飾器 器即函數 裝飾即修飾,意指為其他函數添加新功能 裝飾器定義:本質就是函數,功能是為其他函數添加新功能 二、裝飾器遵循的原則 1.不修改被裝飾函數

python裝飾(轉)

lee type ade 機制 並且 change -1 pri neu 一、python閉包 1、內嵌函數 >>> def func1(): ... print (‘func1 running...‘) ... def func2(

函數的裝飾

time pre col 美的 style 修改 功能 技術 def 函數的閉包:   #1.閉 必須是內部的函數   #2.包 引用了外部作用域中的變量 命名空間:   一共有三種命名空間從大範圍到小範圍的順序:內置命名空間、全局命名空間、局部命名空間 作用域

函數進階:裝飾

fun 閉包、裝飾器 擴展 輸出 關系 返回 空間 名字空間 局部變量 命名空間(又稱“名稱空間”): 存放名字的地方 (概念性的東西) 例如:變量x = 1, 1存放在內存中,命名空間就是存放名字x與1綁定關系的地方。 名稱空間有3種: locals:是函數內的(或者

函數名,裝飾

span play 分享 技術 splay 容器類 函數的參數 spl pri #輸出的__closure__有cell元素 :是閉包函數 def func(): name = ‘eva‘ def inner(): print(name)

裝飾

print conf 文章 tar 函數的參數 led world 方法 str 閉包 1. 函數引用 def test1(): print("--- in test1 func----") # 調用函數 test1() # 引用函數 ret = test1

Python 基礎第十一天(裝飾初識)

過程 理解 繼續 記錄 turn 格式 -s pos 變量賦值 今日內容: 函數名的應用 閉包 裝飾器的初識 裝飾器的傳參 1.函數名的應用 函數名是函數的名字. 本質:變量,特殊的變量。是函數的內存地址 函數名() 可以執行此函數 (1)單獨打印函數名,可以得到函數的內存

Python裝飾

global col line AR bar lis 它的 class failed (1)python的LEGB: LEGB是指:按照L>E>G>B 的順序優先級進行變量查找。 L:local函數內部作用域,是最底層的單個函數