<button id="2ddn7"><acronym id="2ddn7"></acronym></button>

<button id="2ddn7"><acronym id="2ddn7"></acronym></button>

  • <button id="2ddn7"><object id="2ddn7"></object></button>
      1. <s id="2ddn7"></s>
        更多課程 選擇中心


        Python培訓

        400-111-8989

        Python 裝飾器使用指南

        • 發布:Python培訓
        • 來源:Python練習題庫
        • 時間:2017-08-16 14:12

        裝飾器是可調用的對象,其參數是另一個函數(被裝飾的函數)。

        裝飾器基礎知識

        首先看一下這段代碼

        def deco(fn): print "I am %s!" % fn.__name__@decodef func(): pass# outputI am func# 沒有執行func 函數 但是 deco 被執行了

        在用某個@decorator來修飾某個函數func時

        @decoratordef func(): pass

        其解釋器會解釋成下面這樣的語句:

        func = decorator(func)

        其實就是把一個函數當參數傳到另一個函數中,然后再回調,但是值得注意的是裝飾器必須返回一個函數給func

        裝飾器的一大特性是,能把被裝飾的函數替換成其他函數。第二大特性是,裝飾器在加載模塊時立即執行。

        裝飾器何時執行

        裝飾器的一個關鍵特性是,它們在被裝飾的函數定義后立即運行。這通常在導入是(python 加載模塊時)。

        看下下面的示例:

        registry = [] # registry 保存被@register 裝飾的函數的引用def register(func): # register 的參數是一個函數 print('running register(%s)' % func) # 打印被裝飾的函數 registry.append(func) # 把 func 存入 `registery` return func # 返回 func:必須返回函數,這里返回的函數與通過參數傳入的一樣@register # `f1` 和 `f2`被 `@register` 裝飾def f1(): print('running f1()')@registerdef f2(): print('running f2()')def f3(): # <7> print('running f3()')def main(): # main 打印 `registry`,然后調用 f1()、f2()和 f3() print('running main()') print('registry ->', registry) f1() f2() f3()if __name__=='__main__': main() # <9>

        運行代碼結果如下:

        running register(<function f1 at 0x1023fb378>)running register(<function f2 at 0x1023fb400>)running main()registry -> [<function f1 at 0x1023fb378>, <function f2 at 0x1023fb400>]running f1()running f2()running f3()

        從結果可以發現register 在模塊中其他函數之前運行了兩次。調用 register 時,傳給它的參數是被裝飾的函數(例如)。

        看完上邊的示例我們知道,函數被裝飾器裝飾后會變成裝飾器函數的一個參數,那這時就不得不說變量的作用域了。

        變量作用域

        先看下下邊這段代碼:

        def f1(a): print(locals()) print(a) print(b) f1(3)# output{'a': 3}3Traceback(most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f1NameError: global name 'b' is ot defined

        這里的錯誤是因為全局變量 b 沒有定義,如果我們先在函數外部給 b 賦值,再調用這個方法就不會報錯了。

        函數運行時會創建一個新的作用域(命名空間)。函數的命名空間隨著函數調用開始而開始,結束而銷毀。

        這個例子中 f1 的命名空間中只有 {'a': 3},所以 b 會被認為是全局變量。

        再看一個例子:

        b = 6def f2(a): print(a) print(globals()) print(locals()) print(b) b = 9 f2(3)# output3{ '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10c7f2dd8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '~/var_local.py', '__cached__': None, 'b': 6, 'f2': <function f2 at 0x10c7e7598>}{'a': 3}3Traceback(most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f1UnboundLocalError: local variable 'b' referenced before assignment

        這個例子和上一個例子不同是,我現在函數外部定義了全局變量b,但是執行f2 這個方法并沒有打印6,這是為什么呢?

        這是因為執行函數時 Python 會嘗試從局部變量中獲取 b,函數對于已經引用但未賦值的變量并不會自動聲明為局部變量,所以解釋器發現后邊的賦值之前有引用就會拋出UnboundLocalError 錯誤。

        Python 不要求聲明變量,但是假定在函數定義體中賦值的變量是局部變量。

        如果要讓解釋器把b當做全局變量,要使用global聲明:

        b = 6def f3(a): global b print(a) print(b) b = 9 f2(3)# output36

        閉包

        閉包是一種函數,它會保留定義函數時存在的自由變量的綁定,這樣調用函數時,雖然定義作用域不可用,但仍能使用那些綁定。

        介紹閉包前先要說明一下 Python 的函數參數

        函數的兩種參數

        函數有兩種參數

        位置參數

        命名參數

        def foo(x, y=0): return x - y

        python 中一切都是對象

        函數和python中其他一樣都是對象

        In[7]: class A(object): ...: passIn[8]: AOut[8]: __main__.AIn[9]: type(A)Out[9]: typeIn[10]: def foo(): ....: passIn[11]: type(foo)Out[11]: functionIn[12]: A.__class__Out[12]: typeIn[13]: foo.__class__Out[13]: functionIn[14]: a = 1In[15]: a.__class__Out[15]: int# 類 是對象In[16]: issubclass(A.__class__, object)Out[16]: True# 變量 是對象In[17]: issubclass(a.__class__, object)Out[17]: True# 函數 是對象In[18]: issubclass(foo.__class__, object)Out[18]: True

        所以函數也可以作為參數傳遞給其它函數,也可以被當做返回值返回

        def add(x, y): return x + ydef apply(func): return func>> a = apply(add)>> type(a)<type 'function'>>> a(1, 2)>> 3

        閉包的使用

        先來看一個示例:假設有個名為 avg 的函數,它的作用是計算不斷增加的系列值的均值;

        它是這么使用的:

        >>> avg(10)10>>> avg(11)10.5>>> avg(12)11

        那么我們考慮下,avg 從何而來,它又在哪里保存歷史值呢,這個用閉包如何實現呢?

        下邊的代碼是閉包的實現:

        def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager

        調用 make_averager 時,返回一個 averager 函數對象。每次調用 averager 時,它都會把參數添加到系列值中,然后計算當前平均值。

        avg = make_averager()>>> avg(10)10>>> avg(11)10.5>>> avg(12)11

        series 是make_averager 函數的局部變量,因為那個函數的定義體中初始化了series: series=[]。但在averager 函數中,series 是自由變量(指未在本地作用域中綁定的變量)。

        averager 的閉包延伸到那個函數的作用域之外,包含自由變量series的綁定。

        avg 就是一個閉包

        也可以說 make_averager 指向一個閉包

        或者說 make_averager 是閉包的工廠函數

        閉包可以認為是一個內層函數(averager),由一個變量指代,而這個變量相對于外層包含它的函數而言,是本地變量

        嵌套定義在非全局作用域里面的函數能夠記住它在被定義的時候它所處的封閉命名空間

        閉包 只是在形式和表現上像函數,但實際上不是函數。函數是一些可執行的代碼,這些代碼在函數被定義后就確定了,不會在執行時發生變化,所以一個函數只有一個實例。閉包在運行時可以有多個實例,不同的引用環境和相同的函數組合可以產生不同的實例。

        裝飾器

        實現一個簡單的裝飾器

        對一個已有的模塊做一些“修飾工作”,所謂修飾工作就是想給現有的模塊加上一些小裝飾(一些小功能,這些小功能可能好多模塊都會用到),但又不讓這個小裝飾(小功能)侵入到原有的模塊中的代碼里去

        def my_decorator(func): def wrapper(): print "Before the function runs" func() # 這行代碼可用,是因為 wrapper 的閉包中包含自由變量 func print "After the function runs" return wrapperdef my_func(): print "I am a stand alone function">> my_func()# outputI am a stand alone function# 然后,我們在這里裝飾這個函數# 將函數傳遞給裝飾器,裝飾器將動態地將其包裝在任何想執行的代碼中,然后返回一個新的函數>> my_func = my_decorator(my_func)>> my_func()#outputBefore the function runsI am a stand alone functionAfter the function runs# 也可以這么寫@ my_decoratordef my_func(): print "I am a stand alone function">> my_func()#outputBefore the function runsI am a stand alone functionAfter the function runs

        裝飾器是設計模式中裝飾器模式(英文版)的python實現。

        多個裝飾器

        裝飾器可以嵌套使用

        def bread(func): def wrapper(): print "</''''''\>" func() print "<\______/>" return wrapperdef ingredients(func): def wrapper(): print "#tomatoes#" func() print "~salad~" return wrapperdef sandwich(food="--ham--"): print food#### outputs:

        嵌套兩個裝飾器

        >> sandwich = bread(ingredients(sandwich))>> sandwich()#### outputs</''''''\> #tomatoes# --ham-- ~salad~<\______/>

        更簡單的寫法

        @bread@ingredientsdef sandwich(food="--ham--"): print food

        裝飾器的順序是很重要的

        如果我們換下順序就會發現,三明治變成了披薩。。

        @ingredients@breaddef sandwich(food="--ham--"): print food# outputs: #tomatoes# </' ' ' ' ' '\> --ham-- <\______/> ~salad~

        Decorator 的工作原理

        首先看一下這段代碼

        def deco(fn): print "I am %s!" % fn.__name__@decodef func(): pass# outputI am func!# 沒有執行func 函數 但是 deco 被執行了

        在用某個@decorator來修飾某個函數func時

        @decoratordef func(): pass

        其解釋器會解釋成下面這樣的語句:

        func = decorator(func)

        其實就是把一個函數當參數傳到另一個函數中,然后再回調

        但是值得注意的是裝飾器必須返回一個函數給func

        回到剛才的例子

        def my_decorator(func): def wrapper(): print "Before the function runs" func() print "After the function runs" return wrapperdef my_func(): print "I am a stand alone function">> my_func = my_decorator(my_func)>> my_func()#outputBefore the function runsI am a stand alone functionAfter the function runs

        my_decorator(my_func)返回了wrapper()函數,所以,my_func其實變成了wrapper的一個變量,而后面的my_func()執行其實變成了wrapper()

        比如:多個decorator

        @decorator_one@decorator_twodef func(): pass

        相當于:

        func = decorator_one(decorator_two(func))

        比如:帶參數的decorator:

        @decorator(arg1, arg2)def func(): pass# 相當于:func = decorator(arg1,arg2)(func)

        帶參數的裝飾器

        首先看一下, 如果被裝飾的方法有參數

        def a_decorator(method_to_decorate): def wrapper(self, x): x -= 3 print 'x is %s' % x method_to_decorate(self, x) return wrapperclass A(object): def __init__(self): self.b = 42 @a_decorator def number(self, x): print "b is %s" % (self.b + x)a = A()a.number(-3)# outputx is -6b is 36

        通常我們都使用更加通用的裝飾器,可以作用在任何函數或對象方法上,而不必關心其參數使用

        def a_decorator(method_to_decorate): def wrapper(*args, **kwargs): print '****** args ******' print args print kwargs method_to_decorate(*args, **kwargs) return wrapper@a_decoratordef func(): passfunc()#output****** args ******(){}@a_decoratordef func_with_args(a, b=0): pass return a + bfunc_with_args(1, b=2)#output****** args ******(1,){'b': 2}

        上邊的示例是帶參數的被裝飾函數

        現在我們看一下向裝飾器本身傳遞參數

        向裝飾器本身傳遞參數

        裝飾器必須使用函數作為參數,你不能直接傳遞參數給裝飾器本身

        如果想傳遞參數給裝飾器,可以 聲明一個用于創建裝飾器的函數

        # 我是一個創建裝飾器的函數def decorator_maker(): print "I make decorators!" def my_decorator(func): print "I am a decorator!" def wrapped(): print "I am the wrapper around the decorated function. " return func() print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator# decorator_maker()返回的是一個裝飾器new_deco = decorator_maker()#outputsI make decorators!As a decorator maker, I return a decorator# 使用裝飾器def decorated_function(): print "I am the decorated function"decorated_function = new_deco(decorated_function) decorated_function()# outputsI make decorators!As a decorator maker, I return a decoratorI am a decorator!As the decorator, I return the wrapped function.I am the wrapper around the decorated function.I am the decorated function

        使用@修飾

        decorated_function = new_deco(decorated_function)# 等價于下面的方法@new_decodef func(): print "I am the decorated function"@decorator_maker()def func(): print "I am the decorated function"

        my_decorator(裝飾器函數)是decorator_maker(裝飾器生成函數)的內部函數

        所以可以使用把參數加在decorator_maker(裝飾器生成函數)的方法像裝飾器傳遞參數

        # 我是一個創建帶參數裝飾器的函數def decorator_maker_with_arguments(darg1, darg2): print "I make decorators! And I accept arguments:", darg1, darg2 def my_decorator(func): print "I am a decorator! Somehow you passed me arguments:", darg1, darg2 def wrapped(farg1, farg2): print "I am the wrapper around the decorated function." print "I can access all the variables", darg1, darg2, farg1, farg2 return func(farg1, farg2) print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator@decorator_maker_with_arguments("deco_arg1", "deco_arg2")def decorated_function_with_arguments(function_arg1, function_arg2): print ("I am the decorated function and only knows about my arguments: {0}" " {1}".format(function_arg1, function_arg2))decorated_function_with_arguments('farg1', 'farg2')# outputsI make decorators And I accept arguments: deco_arg1 deco_arg2As a decorator maker, I return a decoratorI am a decorator Somehow you passed me arguments: deco_arg1 deco_arg2As the decorator, I return the wrapped function.I am the wrapper around the decorated function.I can access all the variables deco_arg1 deco_arg2 farg1 farg2I am the decorated function and only knows about my arguments: farg1 farg2

        這里裝飾器生成函數內部傳遞參數是閉包的特性

        使用裝飾器需要注意

        裝飾器是Python2.4的新特性

        裝飾器會降低代碼的性能

        裝飾器僅在Python代碼導入時被調用一次,之后你不能動態地改變參數.當你使用"import x",函數已經被裝飾

        使用 functools.wraps

        最后Python2.5解決了最后一個問題,它提供functools模塊,包含functools.wraps,這個函數會將被裝飾函數的名稱、模塊、文檔字符串拷貝給封裝函數

        def foo(): print "foo"print foo.__name__#outputs: foo# 但當你使用裝飾器def bar(func): def wrapper(): print "bar" return func() return wrapper@bardef foo(): print "foo"print foo.__name__#outputs: wrapper

        "functools" 可以修正這個錯誤

        import functoolsdef bar(func): # 我們所說的 "wrapper", 封裝 "func" @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper@bardef foo(): print "foo"# 得到的是原始的名稱, 而不是封裝器的名稱print foo.__name__#outputs: foo

        類裝飾器

        class myDecorator(object): def __init__(self, func): print "inside myDecorator.__init__()" self.func = func def __call__(self): self.func() print "inside myDecorator.__call__()"@myDecoratordef aFunction(): print "inside aFunction()"print "Finished decorating aFunction()"aFunction()# output:# inside myDecorator.__init__()# Finished decorating aFunction()# inside aFunction()# inside myDecorator.__call__()

        我們可以看到這個類中有兩個成員:

        一個是__init__(),這個方法是在我們給某個函數decorator時被調用,所以,需要有一個func的參數,也就是被decorator的函數。

        一個是__call__(),這個方法是在我們調用被decorator函數時被調用的

        如果decorator有參數的話,__init__() 就不能傳入func了,而fn是在__call__的時候傳入

        class myDecorator(object): def __init__(self, arg1, arg2): self.arg1 = arg2 def __call__(self, func): def wrapped(*args, **kwargs): return self.func(*args, **kwargs) return wrapped

        裝飾器示例

        Python 內置了三個用于裝飾方法的函數:property、classmethod和 staticmethod。

        另一個常見的裝飾器是 functools.wraps,它的作用是協助構建行為良好的裝飾器。

        functools.lru_cache

        functools.lru_cache 實現了內存緩存功能,它可以把耗時長的函數結果保存起來,避免傳入相同參數時重復計算。

        我們自己的實現代碼如下:

        from functools import wrapsdef memo(fn): cache = {} miss = object() @wraps(fn) def wrapper(*args): result = cache.get(args, miss) if result is miss: result = fn(*args) print "{0} has been used: {1}x".format(fn.__name__, wrapper.count) cache[args] = result return result return wrapper@memodef fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)

        統計函數執行次數的裝飾器

        def counter(func): """ 記錄并打印一個函數的執行次數 """ def wrapper(*args, **kwargs): wrapper.count = wrapper.count + 1 res = func(*args, **kwargs) print "{0} has been used: {1}x".format(func.__name__, wrapper.count) return res wrapper.count = 0 return wrapper

        裝飾器做緩存

        帶有過期時間的內存緩存

        def cache_for(duration): def deco(func): @wraps(func) def fn(*args, **kwargs): key = pickle.dumps((args, kwargs)) value, expire = func.func_dict.get(key, (None, None)) now = int(time.time()) if value is not None and expire > now: return value value = func(*args, **kwargs) func.func_dict[key] = (value, int(time.time()) + duration) return value return fn return deco

        統計代碼運行時間

        def timeit(fn): @wraps(fn) def real_fn(*args, **kwargs): if config.common['ENVIRON'] == 'PRODUCTION': return fn(*args, **kwargs) _start = time.time() #app.logger.debug('Start timeit for %s' % fn.__name__) result = fn(*args, **kwargs) _end = time.time() _last = _end - _start app.logger.debug('End timeit for %s in %s seconds.' % (fn.__name__, _last)) return result return real_fn

        預約申請免費試聽課

        填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

        上一篇:Python常用模塊資料總結和歸納
        下一篇:你應該掌握的Python3接口測試內功

        2021年Python面試題及答案匯總詳解

        python數據分析,你需要這些工具

        Python培訓講解二叉樹的三種深度

        Python培訓:如何實現窮舉搜索?

        • 掃碼領取資料

          回復關鍵字:視頻資料

          免費領取 達內課程視頻學習資料

        • 視頻學習QQ群

          添加QQ群:1143617948

          免費領取達內課程視頻學習資料

        Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

        選擇城市和中心
        黑龍江省

        吉林省

        河北省

        湖南省

        貴州省

        云南省

        廣西省

        海南省

        青青青草网站免费观看|青青青视频在线观看 超真实强奷视频在线看 百度 好搜 搜狗
        <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>