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

最新文章

  1. linux-redhat6.4驱动无线网卡rtl8188eu
  2. 闲扯json取值,联想map取值。
  3. 一SERVLET (1)
  4. 通过gdb跟踪Linux内核装载和启动可执行程序过程
  5. CentOS7.0 重置Root的密码
  6. BZOJ 1015 [JSOI2008]星球大战starwar
  7. OWIN启动项的检测
  8. Mobile Matrices
  9. thinkinginjava学习笔记09_内部类
  10. 第十一课 CSS介绍与font字体 css学习1
  11. vue-cli 打包编译 -webkit-box-orient: vertical 被删除解决办法
  12. vue自定义滚动条
  13. [Manthan, Codefest 18][Codeforces 1037F. Maximum Reduction]
  14. 搭建nginx反向代理用做内网域名转发
  15. Verilog TestBench Coding Style
  16. 35、输入框(UITextField)密码明暗文切换
  17. fortune 计算公式
  18. HDU 2199 Can you solve this equation(二分答案)
  19. Stay hungry, stay foolish. 求知若饥,虚心若愚。
  20. hdu 2412 Party at Hali-Bula 经典树形DP

热门文章

  1. cucumber的疑问解答
  2. C#与重构(入门)
  3. Ashx登录
  4. Error: connect ECONNREFUSED 127.0.0.1:8080
  5. springBoot 定时器任务
  6. &lt;转载&gt;为什么VR不可能成功?
  7. ConcurrentHashMap源码刨析(基于jdk1.7)
  8. 关于dependencies和devDependencies的理解
  9. Metasploitable渗透测试实战——Windows漏洞 MS08-067复现
  10. &lt;已解决&gt; Eclipse启动失败