笔记-python-装饰器

笔记-python-装饰器

 

 

1.  装饰器

 装饰器的实质是返回的函数对象的函数,其次返回的函数对象是可以调用的,搞清楚这两点后,装饰器是很容易理解的。

1.1.  相关概念理解

首先,要理解在Python中,函数也是一种对象

def foo(x):

    print(x)

 

print(type(foo))

>>><class 'function'>

 

查看函数拥有的方法:

dir(foo)

>>>

['__call__',

 '__class__',

 '__closure__',

 '__code__',

 '__defaults__',

 '__delattr__',

……]

 

因为函数是对象,所以函数可以作为参数传入另一个函数:

def bar(f, x):

x += 1

f(x)

 

bar(foo, 4) 5

 

1.2.  返回函数的函数

修饰函数是这样的一种函数,它接受一个函数作为输入,通常输出也是一个函数:

示例:

def loud(f):

    def new_func(*args, **kw):

        print('calling with', args, kw)

        rtn = f(*args, **kw)

        print('return value is', rtn)

        return rtn

    return new_func

 

loudlen = loud(len)

lisa = [10,20,30]

loudlen(lisa)

 

 

>>> loudlen

<function loud.<locals>.new_func at 0x000000894F4A2E18>

>>> len

<built-in function len>

 

解释:

在python中变量其实都是指针,对象、包括函数也是,可以相互赋值,下面也是合法的语句,在这个基础上理解容易得多;

loudlen1 = loudlen

loudlen1([10,20,30])

 

1.3.  使用 @

@是一个语法糖,与其说是修饰函数倒不如说是引用、调用它修饰的函数。

下面的一段代码,里面两个函数,没有被调用,也会有输出结果:

# 原理

def test(f): 

    print("before ...")

    f() 

print("after ...")

 

@test 

def func(): 

    print("func was called"      )

 

直接运行,输出结果:

before ... 

func was called 

after ... 

 

上面代码只定义了两个函数: test和func。没有地方调用它们。如果没有“@test”,运行应该是没有任何输出的。

 

但是,Python解释器读到函数修饰符“@”的时候,后面步骤会是这样了:

1. 去调用 test函数,test函数的入口参数就是那个叫“func”的函数;

2. test函数被执行,入口参数的(也就是func函数)会被调用(执行);

 

换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。

再来看一个例子:

def test(func):

    func()

    print("call test")

 

def test1(f):

    f()

    print( "call test1")

 

def main():

    @test

    def fun():

        print("call fun")

        @test1

        def fun1():

            print( "call fun1")

 

main()

输出结果:

call fun 

call fun1 

call test1 

call test 

 

仔细体会调用顺序。

需要注意的:

1. 函数先定义,再修饰它;反之会编译器不认识;

2. 修饰符“@”后面必须是之前定义的某一个函数;

 

 

1.4.  多重装饰

 

def first(func):

    print('%s() was post to first()'%func.__name__)

    def _first(*args,**kwargs):

        print('call the function %s() in _first().'%func.__name__)

        return func(*args, **kwargs)

    return _first

 

def second(func):

    print('%s() was post second()'%func.__name__)

    def _second(*args, **kwargs):

        print('call the function %s() in _second.'%func.__name__)

        return func(*args, **kwargs)

    return _second

 

@first

@second

def test():

return 'hello'

输出:

test() was post second()

_second() was post to first()

>>> test()

call the function _second() in _first().

call the function test() in _second.

'hello'

>>> test

<function first.<locals>._first at 0x000000A71BCBE488>

 

有意思的是在视觉效果上装饰器解释时是由内向外的,而执行时是由外至内的。

上面的代码实质上相当于下面的代码:

>>> def test():

    return 'hello world'

 

>>> test=second(test)

test() was post to second()

>>> test

<function _second at 0x000000000316D3C8>

>>> test=first(test)

_second() was post to first()

>>> test

<function _first at 0x000000000316D358>

>>> test()

Call the function _second() in _first().

Call the function test() in _second().

'hello world'

 

1.5.  带参数的装饰器

上面都是无参数装饰,下面是一个有参数装饰的例子:

# 装饰器带参数

def log(text):

    def decorator(func):

        def wrapper(*args,**kw):

            print("%s %s():" %(text, func.__name__))

            print(args,kw)

            return func(*args, **kw)

        return wrapper

    return decorator

 

@log("execute")

def now(*args,**kwargs):

    print("time.ctime()")

 

now(1,2,3)

输出:

execute now():

(1, 2, 3) {}

time.ctime()

 

上面的@log(“execute“)等价于

now = log(“execute”)(now)

 

>>> now

<function log.<locals>.decorator.<locals>.wrapper at 0x000000B1AA97E400>

 

 

2.  附录:

面向切面编程AOP (Aspect Oriented Programming):

 

AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

 

posted @ 2018-09-16 18:26  木林森__𣛧  阅读(150)  评论(0)    收藏  举报