参考链接:Python 函数装饰器

  我认为python中的装饰器是一个很厉害的功能,他能瞬间提升代码的逼格,但对于我这样的小白来说,别说为所欲为的使用了,就连简单的尝试一下,却也是难于登天。经过长达半年的努力,我终于找到了大部分装饰器的介绍信息,鲁迅曾经说过,良好的开始就代表了成功的一半,在我看来,鲁迅分明还是太保守,良好的开端无疑代表你已经成功了。比如接下来我们只需要Ctrl+C+V,就可以完成装饰器的学习了,亲爱的小朋友们,你们学会了吗?

给大人看的分割线


装饰器是用来给已经定义好的函数增加功能用的,能在方法运行期间动态增加方法的功能

1)不会改变原来的代码结构。

为什么这么说呢,因为相比于另一种方法:只将函数作为变量传入另一个函数从而来减少重合,使用装饰器并不需要将函数调用改写

#定义这个函数来为类似foo()的需要去增加loging.warn()的全部函数增添这个loging.warn()的功能
#假如有许多函数和foo()一样,都需要增加loging.warn()这行代码,我们就可以定义一个函数,然后将每个需要增加功能的函数作为参数传入
#但是这样必须在用use_logging(foo)去替换原有的foo()这就改变了函数的调用,破坏了原有的代码结构
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func() def foo():
print('i am foo') use_logging(foo)

2)装饰器在不修改函数代码的前提下影响代码的功能;调用python中的函数可以作为变量去传递,基于这一特性,我们可以构建出自己的装饰器;

先来了解一些基础知识:

  1)我们可以将函数作为变量传递。要注意括号,加了括号就会运行函数。

  2)在函数的内部还可以嵌套函数

def hi(name="yasoob"):
print("现在在hi()里面运行") def greet():
return "现在在greet()里面运行" def welcome():
return "现在在welcom()里面运行"
print(greet())
print(welcome())
print("现在你返回到了hi()里面") hi()
# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
# 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如

  结果:

现在在hi()里面运行
现在在greet()里面运行
现在在welcom()里面运行
现在你返回到了hi()里面

  

 3)从函数中返回函数

def hi(name="yasoob"):
def greet():
return "now you are in the greet() function" def welcome():
return "now you are in the welcome() function" if name == "yasoob":
return greet
else:
return welcome a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500> #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
#现在试试这个 print(a())
#outputs: now you are in the greet() function

  注意下面的 hi()返回的值是greet,加上后面的括号时,就成为了greet()随即开始运行

 hi()() #这会输出 now you are in the greet() function。

  

 4)将函数作为参数传递给另外一个函数

def hi():
return "hi yasoob!" def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi()")
print(func()) doSomethingBeforeHi(hi) #注意不要加括号
#outputs:I am doing some boring work before executing hi()
# hi yasoob!

  

第一个修饰器

def a_new_decorator(a_func):
def wrapTheFunction():
print('我在a_func执行前做一些事情')
a_func()
print('我在a_func执行后做一些事情')
return wrapTheFunction #记得把这个内定义的函数返回 def a_function_requiring_decoration(): #定义一个函数,等会用修饰器来修饰
print('我是需要用修饰器修饰的函数') #使用修饰器
a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()

  结果:

我在a_func执行前做一些事情
我是需要用修饰器修饰的函数
我在a_func执行后做一些事情

 

真正的装饰器:decorator

  另一种生成被装饰函数的方法:用@

补充----------------

  装饰器,只能在函数运行前做一些事情,也能在函数运行后执行,不过要是有返回结果的就不能在其后执行了,因为有return语句但是可以将装饰器写成这样

def log(parameter1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kw):
s=func(*args,**kw)
print('函数%s()运行消耗时间%.11f'%(func.__name__,parameter1))
return s#前面执行,这里返回
return wrapper
return decorator

  

补充结束------------------

先了解一下@语句

最简单的装饰器

  如果被装饰的函数运行时不需要参数,我们可以这样定义装饰器

def metric(fn):
print('%s executed in %s ms' % (fn.__name__, 10.24))
return fn
@metric
def customize(a,b):
  print("我是被装饰的函数,运行结果%d"%a+b)
customize(1,3) #输出
customize executed in 10.24 ms
我是被装饰的函数,运行结果4

  把@metric放到customize()函数的定义处,相当于执行了语句:

customize=metric(customize)

  由于metric()是一个decorator,返回一个函数,所以,原来的customize()函数仍然存在,只是现在同名的customize变量指向了新的函数,于是调用customize()将执行新函数,即在metric()函数中返回的wrapper中返回的customize函数。

普遍使用的装饰器的格式

  上面的只是最简单的,我们通常使用装饰器的时候都需要被装饰的函数的运行结果,当被装饰的函数需要参数时,使用上面的定义就不行了:

  

decorator实际上也是一个返回函数的函数,不过他返回的函数并不是我们要装饰的函数。
装饰器是在方法执行的过程中动态的为方法增加功能的一种方式,不知道我们能不能想到,这种方式可以通过先运行要增加的功能,然后再运行要修饰的对象函数
而两部过程应该放到一个方法体中,而装饰器返回的就是包含这两步过程的一个函数。
另外需要注意的是参数,装饰器接受的参数是要修饰的方法名,其内部的函数接受的是要修饰的方法需要的参数(用*args,**kw)来表示就好
在这个函数中,先运行计划为函数增加功能的代码,然后运行函数
最后,通过python的@语法将这个装饰器“绑定”到要修饰的方法上
def a_new_decorator(a_func):
def wrapTheFunction():
print('我在a_func执行前做一些事情')
a_func()
print('我在a_func执行后做一些事情')
return wrapTheFunction #记得把这个内定义的函数返回 #不同之处在这里
@a_new_decorator
def a_function_requiring_decoration(): #定义一个函数,等会用修饰器来修饰
print('我是需要用修饰器修饰的函数') a_function_requiring_decoration()

  把@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

  

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

解决被修饰的函数会被重命名问题的wraps函数

  使用上面会出现一个问题,获取_name_时会返回被修饰器内置函数的名字

print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction  应该返回a_function_requiring_decoration
之所以返回的是修饰器里内置的名字,是因为它会重写函数的名字和注释文档

而python提供了 functools.wraps() 来解决这个办法,以如下代码为例说明 functolls.wraps 使用方法

from functools import wraps

def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
---snip---
    return a_func
return wrapTheFunction @a_new_decorator
def a_function_requiring_decoration():
---snip-- print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration

@wraps 接受一个被修饰函数的名字,并加入了复制函数名称、注释文档、参数列表等功能,这让我们能在修饰器里访问函数被修饰之前的属性

怎样为修饰器传入参数:

  

假如装饰器也需要一些参数怎么办呢,比如我们想要输出日志的同时还要输出程序的运行时间.这需要三个函数来完成,绑定最外面的函数,其他的可以看作不变

  假如 a_function_requiring_decoration() 需要参数

def a_function_requiring_decoration(name):
--snip--

  我们需要在定义 wrapTheFunction() 时指定参数即:

def wrapTheFunction(name):
---snip---
    return a_func(name)

  当装饰器不知道要装饰的函数有多少个参数时,可以用 *args 来代替

def wrapTheFunction(*args):
---snip---
    return a_func(*args)

  当需要关键字参数时

def a_function_requiring_decoration(name, age=None, height=None):
  print("I am %s, age %s, height %s" % (name, age, height))

  可以这样

def wrapTheFunction(*args, **kwargs):
  # args是一个数组,kwargs一个字典
  logging.warn("%s is running" % func.__name__)
  return func(*args, **kwargs)

  和两层嵌套的decorator相比,3层嵌套的效果是这样的:

def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator

  用法

@log('execute')
def now():
print('2015-3-25')

  和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)

  我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

原文链接 >>>点击<<<

python 进阶:修饰器的介绍的更多相关文章

  1. python中用修饰器进行异常日志记录

    当脚本中需要进行的的相同的异常操作很多的时候,可以用修饰器来简化代码.比如我需要记录抛出的异常: 在log_exception.py文件中, import functools import loggi ...

  2. python函数修饰器(decorator)

    python语言本身具有丰富的功能和表达语法,其中修饰器是一个非常有用的功能.在设计模式中,decorator能够在无需直接使用子类的方式来动态地修正一个函数,类或者类的方法的功能.当你希望在不修改函 ...

  3. python 类修饰器

    1. 修改类函数. 场景: 如果要给一个类的所有方法加上计时,并打印出来.demo如下: # -*- coding:utf-8 -*- import time def time_it(fn): &qu ...

  4. python decorator 修饰器

    decorator 就是给函数加一层皮,好用! 1 from time import ctime 2 3 def deco(func): 4 def wrappedFunc(*args, **kwar ...

  5. Python 函数修饰器

    # 一.用函数修饰函数 #!/usr/bin/python3 def decorate_func(func): def call(*args, **kwargs): print('you have c ...

  6. Python多版本管理器-pyenv 介绍及部署记录

    一. pyenv简单介绍 在日常运维中, 经常遇到这样的情况: 系统自带的Python是2.x,而业务部署需要Python 3.x 环境, 此时需要在系统中安装多个Python版本,但又不能影响系统自 ...

  7. Python学习-修饰器 - itemgetter的妙用

    下面这篇对装饰器讲的很好,懂了. http://python.jobbole.com/85056/ <简单 12 步理解 Python 装饰器> 使用装饰器非常简单(见步骤10),但是写装 ...

  8. python 之修饰器

    from functools import update_wrapper def debug(func): def wrapper(): print "[DEBUG]: enter {}() ...

  9. python 通用 修饰器

    import functools def log(option): def dec(func): def swapper(*arg, **karg): functools.update_wrapper ...

随机推荐

  1. WPF 基础

    关于布局的规则 控件的布局应该由容器来决定,而不是通过自身使用margin之类的东西来控制位置 避免为控件定义明确的尺寸,但应该限定一个可接受的最大及最小尺寸 不要将界面元素设置成与屏幕坐标相关 容器 ...

  2. (转载)PopuWindow和软键盘共存时的设置

    PopuWindow和软键盘共存时的设置 收藏 artshell 发表于 2年前 阅读 1499 收藏 10 点赞 2 评论 0 腾讯云上实验室 1小时搭建人工智能应用 让技术更容易入门>> ...

  3. 【Java】新建的web项目的jsp页面报错的解决方法

    一.错误信息 当用Eclipse新建web项目后,在里面建个jsp页面可能出现如下图的错误,jsp文件有一个红叉: 主要原因是:依赖tomcat类库 解决方案:项目右击—>build path ...

  4. python内置的一些模块

    logging模块: 默认情况下,logging将日志打印到屏幕,日志级别为WARNING:日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO & ...

  5. 【LNOI2014】【BZOJ3626】NOIp2018模拟(三) LCA

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设$dep[i]$表示点i的深度,$lca(i,j)$表示i与j的最近公共祖 ...

  6. caioj 1154 同余方程(模版)

    求x的最小正整数解,使得ax=b(mod m) 那么显然ax - b = m * y ax - my = b 那么就套入Ax+By = K的不定方程中,然后用exgcd求解即可 但这道题求最大正整数解 ...

  7. HDU——T 2444 The Accomodation of Students

    http://acm.hdu.edu.cn/showproblem.php?pid=2444 Time Limit: 5000/1000 MS (Java/Others)    Memory Limi ...

  8. Qt之QImageReader

    简述 QImageReader类为从文件或设备读取图像提供了一个独立的接口. 读取图像最常用的方法是通过构造QImage和QPixmap,或通过调用QImage::load()和QPixmap::lo ...

  9. GridView中使用CheckBox

    asp.net中checkbox是向server提交时才干触发事件 把该控件的autopostback设置为true.才干响应事件  protected void Checkbox_CheckedCh ...

  10. jsp页面中自定义标签的小演示

    在实习期遇到公司的pg自定义标签了,同事要我自己自学一下 自定义标签是用户定义的JSP语言元素.当JSP页面包含一个自定义标签时将被转化为servlet.JSP标签扩展可以让你创建新的标签并且可以直接 ...