理解 python 裝飾器

變量

name = 'world'
    x = 3

變量是表明某個值的名字python

函數

def hello(name):
        return 'hello' + name


    hello('word)
    hello word

函數經過def關鍵字、函數名和可選的參數列表定義。git

是能夠調用的,它執行某種行爲而且返回一個值。設計模式

函數內部也能夠定義函數數組

def outer():
        x = 1
        def inner():
            print x # 1
            inner()

    >> outer()
    1

做用域&生存週期

def func(x):
        print 'x is', x
        print locals()

    func(50)
    

    >> x is 50
    {'x': 50}
    

    >> print x
    Traceback (most recent call last):
    NameError: name 'x' is not defined

函數會建立一個新的做用域(命名空間)。緩存

函數的命名空間隨着函數調用開始而開始,結束而銷燬閉包

g = 'global variable'
    
>> print globals()
{'g': 'global variable',
 '__builtins__': <module '__builtin__' (built-in)>,
 '__file__': 'test.py',
 '__package__': None,
 'func': <function func at 0x10472d8c0>,
 '__name__': '__main__',
 '__doc__': None}

def foo():
    g = 'test'
     print 'g is', g
     print locals()
     print globals()

>> foo()
g is test
{'g': 'test'}   
{'g': 'global variable',
 '__builtins__': <module '__builtin__' (built-in)>,
 '__file__': 'test.py',
 '__package__': None,
 'func': <function func at 0x10472d8c0>,
 '__name__': '__main__',
 '__doc__': None}

>> print g
global variable

在函數內部遇到變量的時候會有如今本身的命名空間裏找app

猜一下段代碼會執行的結果是什麼ide

g = '我已經定義了'

def foo():
    print g
    g = '我從新定義了'
    print g

答案函數

函數參數

函數有兩種參數性能

  1. 位置參數
  2. 命名參數
def foo(x, y=0):
        return x - y

*args and **kwargs? [duplicate]

python 中一切都是對象

函數和python中其餘同樣都是對象

In [7]: class A(object):
   ...:     pass


In [8]: A
Out[8]: __main__.A
In [9]: type(A)
Out[9]: type
In [10]: def foo():
   ....:     pass


In [11]: type(foo)
Out[11]: function
    
In [12]: A.__class__
Out[12]: type
    
In [13]: foo.__class__
Out[13]: function
    
In [14]: a = 1
In [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 + y
        
def apply(func):
    return func

>> a = apply(add)
>> type(a)
<type 'function'>
    
>> a(1, 2)
>> 3

閉包

def make_adder(a):
    def adder(b):
        return b + a
    return adder
    
add = make_adder(5)

>> add
# output
<function adder at 0x108166140>

>> add(3)
# output
8
  • adder 就是一個閉包
  • 也能夠說 make_adder 指向一個閉包
  • 或者說 make_add 是閉包的工廠函數

閉包能夠認爲是一個內層函數(adder),由一個變量指代,而這個變量相對於外層包含它的函數而言,是本地變量

嵌套定義在非全局做用域裏面的函數可以記住它在被定義的時候它所處的封閉命名空間

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

裝飾器

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

def my_decorator(func):
    def wrapper():
        print "Before the function runs"
        func()
        print "After the function runs"
    return wrapper
    
def my_func():
    print "I am a stand alone function"
    

>> my_func()

# output
I am a stand alone function

# 而後,咱們在這裏裝飾這個函數
# 將函數傳遞給裝飾器,裝飾器將動態地將其包裝在任何想執行的代碼中,而後返回一個新的函數

>> my_func = my_decorator(my_func)
>> my_func()
#output
Before the function runs
I am a stand alone function
After the function runs

# 也能夠這麼寫

@ my_decorator
def my_func():
    print "I am a stand alone function"
 
>> my_func()
#output
Before the function runs
I am a stand alone function
After the function runs

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

多個裝飾器

裝飾器能夠嵌套使用

def bread(func):
    def wrapper():
        print "</''''''\>"
        func()
        print "<\______/>"
    return wrapper

def ingredients(func):
    def wrapper():
        print "#tomatoes#"
        func()
        print "~salad~"
    return wrapper

def sandwich(food="--ham--"):
    print food

#outputs:

#嵌套兩個裝飾器
>> sandwich = bread(ingredients(sandwich))
>> sandwich()

# outputs:
</''''''\>
 #tomatoes#
 --ham--
 ~salad~
<\______/>

更簡單的寫法

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

裝飾器的順序是很重要的

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


# outputs:

 #tomatoes#
 </' ' ' ' ' '\>
   --ham--
 <\______/>
 ~salad~

Decorator 的本質

首先看一下這段代碼

def deco(fn):
    print "I am %s!" % fn.__name__
 
@deco
def func():
    pass
    
# output
I am func!

# 沒有執行func 函數 可是 deco 被執行了

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

@decorator
def func():
    pass

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

func = decorator(func)

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

回到剛纔的例子

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

my_decorator(my_func)返回了wrapper()函數,因此,my_func其實變成了wrapper的一個變量,然後面的my_func()執行其實變成了wrapper()

好比:多個decorator

@decorator_one
@decorator_two
def 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 wrapper


class 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)

# output
x is -6
b 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_decorator
def func():
    pass
   
func()
#output
****** args ******
()
{}

@a_decorator
def func_with_args(a, b=0):
    pass
    return a + b

func_with_args(1, b=2)

#output
****** args ******
(1,)
{'b': 2}

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

如今咱們看一下向裝飾器自己傳遞參數

向裝飾器自己傳遞參數

再看一下這段代碼

def deco(fn):
    print "I am %s!" % fn.__name__
 
@deco
def func():
    pass
    
# output
I am func!

沒有執行func 函數 可是 deco 被執行了

在用某個@deco來修飾某個函數func時
其解釋器會解釋成下面這樣的語句:

func = deco(func)

裝飾器必須使用函數做爲參數,你不能直接傳遞參數給裝飾器自己

若是想傳遞參數給裝飾器,能夠 聲明一個用於建立裝飾器的函數

# 我是一個建立裝飾器的函數
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()

#outputs
I 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()

# outputs
I make decorators!
As a decorator maker, I return a decorator
I 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_deco
def 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')

# outputs

I make decorators! And I accept arguments: deco_arg1 deco_arg2
As a decorator maker, I return a decorator
I am a decorator! Somehow you passed me arguments: deco_arg1 deco_arg2
As 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 farg2
I am the decorated function and only knows about my arguments: farg1 farg2

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

使用裝飾器須要注意

  • 裝飾器是Python2.4的新特性
  • 裝飾器會下降代碼的性能
  • 裝飾器僅在Python代碼導入時被調用一次,以後你不能動態地改變參數.當你使用"import x",函數已經被裝飾

最後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

@bar
def foo():
    print "foo"

print foo.__name__
#outputs: wrapper

"functools" 能夠修正這個錯誤

import functools

def bar(func):
    # 咱們所說的 "wrapper", 封裝 "func"
    @functools.wraps(func)
    def wrapper():
        print "bar"
        return func()
    return wrapper

@bar
def 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__()"
 
@myDecorator
def aFunction():
    print "inside aFunction()"
 
print "Finished decorating aFunction()"
 
aFunction()
 
# output:
# inside myDecorator.__init__()
# Finished decorating aFunction()
# inside aFunction()
# inside myDecorator.__call__()

咱們能夠看到這個類中有兩個成員:

  1. 一個是__init__(),這個方法是在咱們給某個函數decorator時被調用,因此,須要有一個func的參數,也就是被decorator的函數。
  2. 一個是__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

裝飾器示例

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
  • 裝飾器作緩存
from functools import wraps
def 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
 
@memo
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

15言和知性中用到的緩存

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
相關文章
相關標籤/搜索