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. springMVC(spring)+WebSocket案例(获取请求参数)

    开发环境(最低版本):spring 4.0+java7+tomcat7.0.47+sockjs 前端页面要引入: <script src="http://cdn.jsdelivr.ne ...

  2. PAT1081:Rational Sum

    1081. Rational Sum (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given N ...

  3. Elasticsearch结构化搜索_在案例中实战使用term filter来搜索数据

    1.根据用户ID.是否隐藏.帖子ID.发帖日期来搜索帖子 (1)插入一些测试帖子数据 POST /forum/article/_bulk { "index": { "_i ...

  4. Re:从零开始的领域驱动设计

    领域驱动的火爆程度不用我赘述,但是即便其如此得耳熟能详,但大多数人对其的认识,还只是停留在知道它的缩写是DDD,知道它是一种软件思想,或者知道它和微服务有千丝万缕的关系.Eric Evans对DDD的 ...

  5. 这样入门asp.net core 之 静态文件

    本文章主要说明asp.net core中静态资源处理方案: 一.静态文件服务 首先明确contentRoot和webroot这两个概念 contentRoot:web的项目文件夹,其中包含webroo ...

  6. 用react重构个人网站 3-23

    1:  :before 选择器在被选元素的内容前面插入内容 2:  float是什么?浮动在CSS中的作用 3:CSS运用在React中的两种方式 使用className属性,CSS内容就用link加 ...

  7. Linux 6.8 TFS(Taobao File System)使用文档-安装篇

    介绍  TFS(Taobao FileSystem)是一个高可扩展.高可用.高性能.面向互联网服务的分布式文件系统,其设计目标是支持海量的非结构化数据的存储:TFS使用C++语言开发,需要运行在64b ...

  8. 把封装脚本做成jar包

    前提: eclipse, selenium, maven 把二次封装过的脚本做成jar包, 这样可以在新建工程里也调用封装过的方法. 实现步骤: 1. project 右键 => maven = ...

  9. Golang中使用lua进行扩展

    前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与go ...

  10. I春秋——Misc(贝斯家族)

    不好意思呀!最近一直忙着学习python,竟然忘记更新I春秋里面的题目了(QAQ),我以后会时时刻刻提醒自己要更新 永远爱你们的! ----新宝宝 1:贝斯家族: 解题思路:我相信看见这一题都能够想到 ...