python裝飾器的作用


  常見裝飾器;內置裝飾器;類裝飾器、函數裝飾器、帶參數的函數裝飾器

  裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能

  一、函數裝飾器:

def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

def bar():
    print('i am bar')
bar
= use_logging(bar) bar()

輸出:
WARNING:root:bar is running
i am bar

  二、帶參數的函數裝飾器:

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            return func(*args)
        return wrapper
    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()

輸出:
WARNING:root:foo is running
i am foo

  三、類裝飾器:

  再來看看類裝飾器,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的 __call__方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法

  

class Foo(object):
    def __init__(self, func):
        self._func = func
    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()

輸出:
class decorator runing
bar
class decorator ending

  四、functools.wraps

  使用裝飾器極大地復用了代碼,但是他有一個缺點就是原函數的元信息不見了,比如函數的docstring、__name__、參數列表,先看例子:

  裝飾器

def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

  函數

@logged
def f(x):
   """does some math"""
   return x + x * x

  該函數完成等價於:

def f(x):
    """does some math"""
    return x + x * x
f = logged(f)

  不難發現,函數f被with_logging取代了,當然它的docstring,__name__就是變成了with_logging函數的信息了。

  print f.__name__ # prints 'with_logging'   print f.__doc__ # prints None 

  這個問題就比較嚴重的,好在我們有functools.wraps,wraps本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器函數中,這使得裝飾器函數也有和原函數一樣的元信息了。

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
    """does some math"""
    return x + x * x

print f.__name__  # prints 'f'
print f.__doc__   # prints 'does some math'
五、內置裝飾器

 @property 的用法參見:把類方法變成屬性,可以通過類直接調用

   https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820062641f3bcc60a4b164f8d91df476445697b9e000

 http://www.cnblogs.com/superxuezhazha/p/5793450.html

 因為Python支持高階函數,在函數式編程中我們介紹了裝飾器函數,可以用裝飾器函數把 get/set 方法“裝飾”成屬性調用:

 

class Student(object): 
    def __init__(self, name, score): 
        self.name = name 
        self.__score = score 
    @property 
    def score(self): 
        return self.__score 
    @score.setter 
    def score(self, score): 
        if score < 0 or score > 100: 
            raise ValueError('invalid score') 
        self.__score = score 
注意: 第一個score(self)是get方法,用@property裝飾,第二個score(self, score)是set方法,用@score.setter裝飾,@score.setter是前一個@property裝飾后的副產品。 現在,就可以像使用屬性一樣設置score了:
>>> s = Student('Bob', 59)
>>> s.score = 60 
>>> print s.score 
60

 @staticmethod :靜態方法

   @classmethod  :  類方法

 參考:http://www.cnblogs.com/taceywong/p/5813166.html

  

 Python其實有3類方法:

  • 靜態方法(staticmethod)
  • 類方法(classmethod)
  • 實例方法(instance method)

   看一下下面的示例代碼:

def foo(x):
    print "executing foo(%s)" %(x)

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)" %(self,x)
    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)" %(cls,x)
    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" %x

a = A()

  在示例代碼中,先理解下函數里面的selfcls。這個self和cls是對類或者實例的綁定,對於一般的函數來說我們可以這么調用foo(x),這個函數就是最常用的,它的工作和任何東西(類、實例)無關。對於實例方法,我們知道在類里每次定義方法的時候都需要綁定這個實例,就是foo(self,x),為什么要這么做呢?因為實例方法的調用離不開實例,我們需要把實例自己傳給函數,調用的時候是這樣的a.foo(x)(其實是foo(a,x)。類方法一樣,只不過它傳遞的是類而不是實例,A.class_foo(x)。注意這里的self和cls可以替換別的參數,但是python的約定是這兩個,盡量不要更改。

  對於靜態方法其實和普通的方法一樣,不需要對誰進行綁定,唯一的區別是調用時候需要使用a.static_foo(x)A.static_foo()來調用。

\ 實例方法 類方法 靜態方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.clas_foo(x) A.static_foo(x)
 
>>> a=A()
>>> a.foo(3)
executing foo(<__main__.A object at 0x108117790>,3)
>>> a.class_foo(3)
executing class_foo(<class '__main__.A'>,3)
>>> A.class_foo(3)
executing class_foo(<class '__main__.A'>,3)
>>> a.static_foo(3)
executing static_foo(3)
>>> A.static_foo(3)
executing static_foo(3)
>>> A.foo(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with A instance as first argument (got int instance instead)
>>>

 

參考:

1、https://www.zhihu.com/question/26930016

2、http://python.jobbole.com/85056/

3、http://pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/

4、http://30daydo.com/article/89


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM