python装饰器

概述:

如果彻底弄懂装饰器的问题,建议您耐心读完整篇博客,相信您一定会有收获

装饰器是一个可以调用的对象,其参数是另外一个函数的函数名,函数装饰器可能会处理被装饰的函数,然后将其返回,或者将其替代为另外一个函数或者可调用的对象。

@decorate
def func():
	print "func"

上面是函数装饰器最简单的用法,其作用等同于下面的写法

def func():
	print "func"
func = decorate(target)

以下问题的答案有助于弄清楚并且掌握装饰器:

  1. python装饰器的一般语法及装饰器何时执行
    • 对于最简单的装饰器来讲,装饰器的形式是函数的嵌套,即内外两层函数
    • 外部函数的参数是被装饰函数的函数名,返回值为内部函数的函数名
    • 内部函数接受参数为被装饰函数的参数,返回值为被装饰函数的返回值
    • 函数装饰器在导入模块时立即执行
    # 最典型的函数装饰器形式
    >>>def decorater(func):
        	def inner(*args, **kwargs):
    		     return func()
    	    return inner
    
    # 装饰器的使用
    >>>@decorater
       def func():
    	    print("running func")
    
    >>>func()
    running func
    >>>func
    <function decotater/<local>.inner at 0x1041543832>
    
  2. python如何判断变量的作用域
    首先应该知道,函数中的变量分为L、E、G、B,其中E是Enclosing function locals,即嵌套函数内的变量,可以称之为*变量;B代表Buildin,Python内置模块的名字空间函数名称等,比如dict、len()等;下面重点讨论一下L:local局部变量和G:global全局变量
    在下面的三个示例中,变量a就是局部变量L,b则不一定
# 先看一个没有问题的
>>>def func(a):
	   print(a)
	   print(b)
>>>func(1)
1
# 看一个有问题的
b = 2
>>>def func(a):
	   print(a)
	   print(b)
	   b =3
>>>func(1)
Traceback (most recent call last):
  File "/Users/wangjunjie/Desktop/py_folder/singlepatchdemo.py", line 6, in <module>
    func(1)
  File "/Users/wangjunjie/Desktop/py_folder/singlepatchdemo.py", line 4, in func
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment
# 看一个有问题的
b = 2
>>>def func(a):
	   global b	 # 将b声明为全局变量,此时为G
	   print(a)
	   print(b)
	   b =3
>>>func(1)
# 为什么会这样呢?
那是因为python在编译函数时候,由于存在函数内部赋值,将b判断为局部变量,那由于函数内部变量赋值在print后面,所有报错
# 如何解决上面的问题?
b = 2
>>>def func(a):
	   global b
	   print(a)
	   print(b)
	   b =3
>>>func(1)
1
2
  1. 闭包是什么?
    *是这么写的:在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量(即我们上面的E变量),则可能产生闭包
    总结以下,形成闭包的三要素:
    ~函数的嵌套
    ~内部函数引用外部E变量
    ~外部函数返回内部函数
  2. nonlocal是什么,有什么作用?
    其实下面的问题我们上面已经遇到过了,我们在执行count+=1时候,隐式的将count变成了局部变量,此时count没有初始值
#先看一个问题
>>>def average_func():
	   count = 0
	   total = 0
	   def average_inner(num):
		   count += 1
		   total += num
		   return total / count
>>>func = average_func()
>>>func(5)
Trackback (most recent call last)
  ···
UnboundLocalError: Local cariable "count referenced before addignment
#怎么解决这个问题呢?
>>>def average_func():
	   count = 0
	   total = 0
	   def average_inner(num):
	       nonlocal count, total    # 此时通过nonlocal将变量声明为*变量
		   count += 1
		   total += num
		   return total / count

功能完善的装饰器

理解以上的内容都是为了能够为了更好的理解装饰器,下面我们首先定义一个功能完整的装饰器,然后再对这个装饰器进行解释
完整装饰器的功能:1.实现函数的功能拓展;2.被装饰的函数可以有参数;3.装饰器可以有参数

#我们定义一个可以接受参数的装饰器,来实现不同形式的输出

import time
def get_run_time(flag):
    """如果flag值为1 print输出的结果为整数;其他则输出浮点数类型
    装饰器工厂函数的功能  
        1. 接收额外参数-->内部代码使用  
        2. 产生装饰器对象
    """
    def get_time(*args, **kwargs):
        #         打包参数 args = (99,) kwargs={}
        func = args[0]
        def inner(*args, **kwargs):
            begin = time.time()
            #   解包参数  (99)   暂存返回值 在最后返回
            ret = func(*args, **kwargs)
            end = time.time()
            if flag == 1:
                print("函数执行花费%d秒" % int(end-begin))
            else:
                print("函数执行花费%f秒" % (end-begin))
            return ret
        return inner
    return get_time

下面针对上面的装饰器进行说明:
python装饰器

使用functools.wraps完善你的装饰器

仔细分析下面的示例的输出结果,就能理解该函数的用途
python装饰器

叠加装饰器

@d1
@d2
def f():
	print("f")
# 等价于
def f():
	print("f")
f = d1(d2(f))

安利几个有用的装饰器函数

  • python中内置的:property、classmethod、staticmethod
  • 标准库中:缓存装饰器 functools.lru_cache、单分派泛函数 functools.singledispatch