Python第二十六天 python装饰器

装饰器
Python 2.4 开始提供了装饰器( decorator ),装饰器作为修改函数的一种便捷方式,为工程师编写程序提供了便利性和灵活性
装饰器本质上就是一个函数,这个函数接受其他函数作为参数,并将其以一个新的修改后的函数进行替换。

装饰器的作用
1、注入参数。为函数提供默认参数,生成新的参数等
2、记录函数的行为。可以统计函数的调用次数,缓存函数的结果,计算函数调用耗费的时间
3、预处理与后处理
4、修改调用时的上下文

函数可以赋值给另外一个变量名
函数可以嵌套
函数对象可以作为另外一个函数的参数
装饰器仅仅是利用前面的这些Python 知识加上Python 语法实现的一种高级语法

函数对象
在Python 语言中, def 语句定义了一个函数对象,并将其赋值给函数名。
也就是说,函数名其实只是一个变量,这个变量引用了这个函数对象。
因此,我们可以将函数赋值给另外一个变量名,通过这个新的变量名调用函数。

def say_hi():
print ("HI")
hello = say_hi
hello()

嵌套函数
在Python 语言中, def 语句是一个实时执行的语句,当它运行的时候会创建一个新的函数对象,并将其赋值给一个变量名
这里所说的变量名就是函数的名称。因为def 是一个语句,因此,函数定义可以出现在其他语句之中。

def outer(x , y) :
def inner () :
return x + y
return inner
f = outer(l , 2)
print(f())

在这个例子中,我们定义了一个名为outer 的函数, 并在outer 函数内部定义了inner 函数
outer 函数以返回值的形式返回inner 函数,我们将返回值保存在变量f 中,f 引用的是outer 函数内部的inner 函数
所以,当我们调用函数f 时,实际是调用的inner 函数

回调函数
回调函数是指将函数作为参数传递给另外一个函数,并在另外一个函数中进行调用。
回调函数并不是Python 语言特有的,在各个编程语言中都存在。

def greeting(f):
f() def say_hi():
print ("HI") def say_hello():
print("HELLO") greeting(say_hi)
greeting(say_hello)

装饰器
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。

def say_hi():
print("hi") def bread(f):
def wrapper(*args, **kwargs ):
print("begin call {0}".format(f.__name__))
f()
print("finish call {0}".format(f.__name__))
return wrapper say_hi_copy = bread(say_hi)
say_hi_copy()

上面这段代码的执行结果如下:
begin call say_hi
hi
finish call say_hi

 

改造为装饰器

@bread
def say_hi(username='someone'): #被装饰函数,被装饰函数的参数一定要用关键字参数,不能用位置参数,否则不能装入字典!!!!!!!
print("hi")
print(username) def bread(f): #装饰器函数
def wrapper(*args, **kwargs ):
print("begin")
f()
print("finish")
print(kwargs.get("username")) #读取被装饰函数的参数,被装饰函数的参数一定要用关键字参数,不能用位置参数,否则不能装入字典
return wrapper say_hi() 这段程序和前面的程序作用一模一样,产生的结果也相同。区别在于,前面的程序显示地调用了bread 函数来封装say_hi 函数
这段程序通过Python 的语法糖来封装say_hi函数。在Python 中, say_hi 函数定义语句前一行的"@bread"语句表示对该函数应用bread装饰器
其中,"@"是装饰器的语法,"bread"是装饰器的名称

获取正确函数属性
对于一个函数,我们可以通过name 属性得到函数的名字,通过doc 属性得到函数的帮助信息。
但是, 一个被装饰器装饰过的函数,默认情况下,我们通过doc 和name 获取属性时,得到的却是装饰器中嵌套函数的信息

标准库的functools 模块中的wraps 装饰器。wraps 装饰器的作用是,复制函数属性至被装饰的函数
使用functools.wraps 装饰器装饰wrapper 函数。通过这样简单的修改就可以获取到add 函数的正确属性

from __future__ import print_function
import time
import functools def benchmark(func):
@functools.wraps(func)
def wrapper(*args , **kwargs):
t = time.time()
res = func(*args , ** kwargs)
print(func.__name__, time.time() - t)
return res
return wrapper @benchmark
def add (a, b):
'''Calculate the sum of two numbers'''
return a + b print(add.__name__)
print(add.__doc__)

inspect 模块
inspect 模块提供了许多有用的函数来获取活跃对象的信息。其中, getcallargs 函数用来获取函数的参数信息
getcallargs 会返回一个字典,该字典保存了函数的所有参数,包括关键字参数和位置参数。
也就是说, getcallargs 能够根据函数的定义和传递给函数的参数,推测出哪一个值传递给函数的哪一个参数。
getcallargs 推测出这些信息以后,以一个字典的形式返回给我们所有的参数和取值。

import functools
import inspect
def check_is_admin(func):
@functools.wraps(func)
def wrapper(* args, * *kwargs):
func_args = inspect.getcallargs(func , *args , ** kwargs)
if func_args.get('username')! = 'admin' :
raise Exception("This user is not allowed to put/get elem")
return f( * args, * *kwargs )
return wrapper

给装饰器传递参数
在Python 的装饰器语法中,内层函数的参数是被装饰的函数参数,外层函数的参数是被装饰的函数。
那么,如果装饰器本身也有参数应该怎么办呢?在Python 的装饰器语法中, 如果装饰器本身也有参数,则需要再嵌套一层函数。
这也是为什么读者看到的Python装饰器有时候是两层嵌套, 有时候是三层嵌套的原因

下面是一个带参数的装饰器。在这个装饰器的实现中,最外层的函数是装饰器的名称。
这个装饰器的作用是将被装饰的函数执行多次。具体执行的次数由装饰器的参数指定

from __future__ import print_function

def times(length=1):
def bread(func):
def wrapper(* args ,* *kwargs):
for i in range(length):
func(*args, **kwargs) # func代表被装饰函数sandwich(),所以被装饰函数有什么参数这里就要写什么,例如Django里面每个view函数都有request参数,装饰view函数就要写成func(request, *args, **kwargs)
return wrapper
return bread @times(5) #5传入到length,执行5次sandwich
def sandwich(name):
print (name) sandwich('hello')

装饰器的先后顺序

例子中,never_cache就要先于login_required被调用。

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'

使用method_decorator有时会导致TypeError异常,因为参数传递的原因。当然,一般人碰不到的,碰得到的人都不一般,都能自己查Django官方文档掌握问题原因。

参考

http://www.liujiangblog.com/blog/37/


def record_functime():
'''
函数执行时间统计装饰器
:return:
'''
def stopwatch(callback):
@functools.wraps(callback)
def wrapper(*args, **kwargs):
start = time.time()
body = callback(*args, **kwargs)
end = time.time()
timelog = 'function name:'+callback.__name__ +'; execute time:' +str(start) +'; elapsed time:'+str(end - start)
log_helper.info(timelog)
return body
return wrapper
return stopwatch

Python第二十六天 python装饰器的更多相关文章

  1. 孤荷凌寒自学python第二十六天python的time模块的相关方法

    孤荷凌寒自学python第二十六天python的time模块的相关方法 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 要使用time模块的相关方法,必须在文件顶端引用: import tim ...

  2. python第二十六课——装饰器

    装饰器是闭包的一种使用场景: python中的装饰器在定义上需要传入一个函数对象, 在此函数执行之前或者之后都可以追加其它的操作, 这样做的好处是,在不改变源码(原本业务逻辑的)同时,进行功能的扩展: ...

  3. 初学 Python(十五)——装饰器

    初学 Python(十五)--装饰器 初学 Python,主要整理一些学习到的知识点,这次是生成器. #-*- coding:utf-8 -*- import functools def curren ...

  4. 孤荷凌寒自学python第二十九天python的datetime.time模块

     孤荷凌寒自学python第二十九天python的datetime.time模块 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) datetime.time模块是专门用来表示纯时间部分的类. ...

  5. 孤荷凌寒自学python第十六天python的迭代对象

    孤荷凌寒自学python第十六天python的迭代对象 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 迭代也就是循环. python中的迭代对象有相关的如下几个术语: A容器 contrai ...

  6. python 装饰器 第十步:装饰器来装饰器一个类

    第十步:装饰器来装饰一个类 def kuozhan(cls): print(cls) #声明一个类并且返回 def newHuman(): # 扩展类的功能1 cls.cloth = '漂亮的小裙子' ...

  7. 简学Python第四章__装饰器、迭代器、列表生成式

    Python第四章__装饰器.迭代器 欢迎加入Linux_Python学习群  群号:478616847 目录: 列表生成式 生成器 迭代器 单层装饰器(无参) 多层装饰器(有参) 冒泡算法 代码开发 ...

  8. Python第二十四天 binascii模块

    Python第二十四天 binascii模块 binascii用来进行进制和字符串之间的转换 import binascii s = 'abcde' h = binascii.b2a_hex(s) # ...

  9. 十一. Python基础(11)—补充: 作用域 & 装饰器

    十一. Python基础(11)-补充: 作用域 & 装饰器 1 ● Python的作用域补遗 在C/C++等语言中, if语句等控制结构(control structure)会产生新的作用域 ...

随机推荐

  1. NS3系列——eclipse + NS3环境搭建

    1. 安装NS3 (1)在 ubuntu12.04 中安装 ns3.20,首先要安装一下各种依赖软件: sudo apt-get install gcc g++ pythonsudo apt-get ...

  2. 关于TCP/IP,必知必会的十个经典问题[转]

    关于TCP/IP,必知必会的十个问题 原创 2018-01-25 Ruheng 技术特工队   本文整理了一些TCP/IP协议簇中需要必知必会的十大问题,既是面试高频问题,又是程序员必备基础素养. 一 ...

  3. C语言实现计算双基回文数详解

    双基回文数的定义: 如果一个正整数n至少在两个不同的进位制(二进制<=进制=<十进制)b1和b2下都是回文数,则称n是双基回文数. 根据定义,简单的说就是在二进制到十进制之间(包括十进制和 ...

  4. pycharm跨目录调用文件

    笔者今天在一个测试qq群中遇到解决跨目录中调用文件的问题.现在来详细说一下. 以下图目录为例: 1.同目录下直接调用掉用.test2.py和test3.py在同一个目录test2下. 所以在test3 ...

  5. 深浅拷贝,原生和JQuery方法实现

    7-17: 1:e.target.parentNode.remove();成功,查询一下JS原生的remove方法 2:复习JS DOM的原生操作方法,比如innerHTML(),insertBefo ...

  6. 一个js小游戏----总结

    花了大概一天左右的功夫实现了一个js小游戏的基本功能,类似于“雷电”那样的小游戏,实现了随即怪物发生器,碰撞检测,运动等等都实现了,下一个功能是子弹轨迹,还有其他一些扩展功能,没有用库,也没有用web ...

  7. Spring MVC温故而知新 – 请求映射RequestMapping

    RequestMapping注解说明 @RequestMapping注解的作用将Web请求映射到特定处理程序类和/或处理程序方法,这个注解可以用于类或者方法上,并通过属性value指定请求路径.用在C ...

  8. UnicodeEncodeError: 'ascii' codec can't encode character...的解决方法

    在python2.7下,因为想从数据库中读出来分类名进行写入到文件,提示 Traceback (most recent call last): File "test.py", li ...

  9. bzoj 2820 莫比乌斯反演

    搞了一整个晚自习,只是看懂了dalao们的博客,目前感觉没有思路-.还是要多切题 next day: 刚才又推了一遍,发现顺过来了,hahaha #include<cstdio> #inc ...

  10. Spring事务(一) Markdown 版

    事务 事务的特性(ACID) 原子性(Atomicity) 原子性要求事务所包含的全部操作是一个不可分割的整体,这些操作要么全部提交成功,要么只要其中一个操作失败,就全部"成仁" ...