Python装饰器详解

装饰器

定义:本质就是函数,(装饰其他函数)就是为其他函数添加额外的功能; 遵循的原则:1、不能修改其被装饰函数的源代码    2、不能修改其被装饰函数的调用方式   实现装饰器需要用到的知识点:
  1. 函数即"变量"
  2. 高阶函数
  3. 嵌套函数
如果你对装饰器实现的如上三个知识点理解了的话,那你就完全可以理解装饰器了,图示如下: [attach]1343[/attach] 下面我们就把装饰器的知识分解一个一个来理解。   1、函数即"变量" 让我们来看一下几个例子,来说明函数即变量 函数的错误调用案例,跟变量一样,还没有定义就调用:
# 错误调用方式一:
def foo():
    print('in the foo!')
    bar()

foo()
# 输出
NameError: name 'bar' is not defined  (因为在调用foo函数执行的时候函数体内又调用了bar函数,但是bar函数并没有定义)

# 错误调用方式二:
def foo():
    print('in the foo!')
    bar()

foo()

def bar():
    print('in the bar!')

# 输出
NameError: name 'bar' is not defined (同上面,不同的是虽然你在下面定义了bar函数,但是在调用之后才定义的.)
从上面的例子可以看出,函数的定义和调用,其实是跟变量是一样的,变量在没有定义之前你对变量进行操作也是会报变量没有被定义的错误。   正确的调用方式如下:
# 错误调用方式二:
def foo():
    print('in the foo!')
    bar()

def bar():
    print('in the bar!')

foo()

# 输出
in the foo!
in the bar!
具体说明我们可以结合定义变量图示说明,我们把整个内存看作是一个大楼,把变量看做是门牌号,而变量真正存在的值看做是房间里面的东西,房间就相当于是大内存中的一小块内存块,图示如下: [attach]1344[/attach] 如图所示我们可以看出函数def下定义的函数名就相当于变量的变量名,而函数体就相当于变量的值;所以函数的执行跟调用变量是一样的,只有在你执行之前所有的函数已经声明,占用了内存地址才可以执行,否则在你执行之前未定义,而会报错。   函数和变量一样,如果你已经定义好了,就会从内存中划分出一块内存空间存储进行存储,那什么时候回收这个内存空间呢?1、当你整个程序执行完成后,你所定义的变量或者函数占用的内存空间就会被释放  2、就是你利用del()内置函数的删除方法,把变量名和函数名删除,然后Python的内存回收机制发现这块内存空间没有被引用了,就把这块没有被引用的内存空间回收了。   2、高阶函数 高阶函数定义:
  1. 把一个函数名当做实参传给另外一个函数当做形参
  2. 返回值(return)中包含函数名
只要满足以上其中一个条件就称为高阶函数。   首先我们看一个高阶函数的例子:
def foo(func):
    print('in the foo!')
    print(func)
    func()


def bar():
    print('in the bar!')

foo(bar)

结果:
in the foo!
   # 打印了函数bar的内存地址
in the bar!
如上是一个符号传递函数名的一个高阶函数的例子。   下面我们利用高阶函数来做一个不改变被装饰函数代码,为被装饰函数添加一个新功能,我们这里添加一个程序运行时间的功能。   把一个函数名当做实参传给另外一个函数(没有改变函数的代码,添加了一个功能,但是运行方法改变了)
import time
def foo(func):
    print('in the foo!')
    start_time = time.time()
    func()
    stop_time = time.time()
    print("func run time is %s" % (stop_time - start_time))


def bar():
    time.sleep(2)
    print('in the bar!')

foo(bar)

# 结果如下:
in the foo!
in the bar!
func run time is 2.002309799194336
如上代码把bar函数当做一个实参,传给了foo函数当做形参,然后在里面运行了bar函数。   返回值中包含函数(虽然没有改变函数运行方式,但是好像功能没有加上)
import time
def foo(func):
    print(func)
    return func


def bar():
    time.sleep(2)
    print('in the bar!')

bar = foo(bar)
bar()

# 结果为:

in the bar!
好像都并没有什么卵用,但是你应该发现了,我可以做到不改变函数调用方式和增加功能,但是单独利用高阶函数,这两个条件同事满足是做不到的,因为还需要结合嵌套函数才行。   3、嵌套函数 定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)   函数嵌套示范:
def foo():
    print('in the foo')
    def bar():
        print('in the bar')

    bar()

foo()
这就是一个最简单的一个嵌套函数,一个函数下,再定义另外一个函数。   局部作用域和全局作用域的访问顺序:
x = 0
def grandpa():
    x = 1
    def dad():
        x = 2
        def son():
            x = 3
            print(x)   # 打印出来为3
        son()
    dad()
grandpa()

print(x)   # 这个还是0
结果是 3, 0,过程如图所示: [attach]1345[/attach]   闭包: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure
def counter(start_num=0):
    count=[start_num]
    def incr():
        count[0]+=1
        return count[0]
    return incr

print(counter())
print(counter()())
print(counter()())

c=counter()
print(c())
print(c())
没看懂,先记录一下。   4、装饰器 讲了装饰器 = 高阶函数 + 嵌套函数,还是添加一个打印程序执行时间功能,例子如下:
import time
def timer(func):
    print("in the timer")
    def foo():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("fun run time is %s" % (stop_time - start_time))
    return foo


def bar():
    time.sleep(2)
    print('in the bar')

bar = timer(bar)
bar()

# 结果

in the timer
in the bar
fun run time is 2.004901885986328
如上代码所示,我们为了给bar函数增加一个计算他运行时间的功能,我们既没有改变他的运行方式bar(),也没有改变他的内部代码,功能,也实现了,这就是利用高阶函数 + 嵌套函数完成的一个简易版的装饰器,在python中为了简化方法,装饰函数可以直接利用@符号把装饰函数应用到被装饰函数的上,如下所示:
import time
def timer(func):
    print("in the timer")
    def foo():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("fun run time is %s" % (stop_time - start_time))
    return foo

@timer     # @timer 相当于  bar = timer(bar)
def bar():
    time.sleep(2)
    print('in the bar')

# bar = timer(bar)
bar()

# 结果如下:
in the timer
in the bar
fun run time is 2.000408887863159
如上可以看出来,结果是一样的,简易版的装饰器我们可以做出来了,那下面我们在深入一下,做一个高级版本的装饰器,那我们在熟悉如下几个知识点。   1、函数参数固定
def decor(func):
    def wrapper(n):
        print('starting')
        func(n)
        print('stopping')
    return wrapper


def test(n):
    print('in the test arg is %s' % n)

decor(test)('lucky')
分解过程: [attach]1346[/attach] 其实就是 decor(test)('lucky')   的过程你可以理解为  a = decor(test) = wrapper  然后在调用  a('lucky') = wrapper('lucky') 。   2、函数参数不固定
def decor(func):
    def wrapper(*args, **kwargs):
        print('starting')
        func(*args, **kwargs)
        print('stopping')
    return wrapper


def test(n, x=1):
    print('in the test n is %s, x is %s' % (n, x))

decor(test)('alex', x=2)

# 结果如下:
starting
in the test n is alex, x is 2
stopping
3、无参装饰器
import time
def decorator(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            func(*args, **kwargs)
            stop = time.time()
            print('run time is %s ' % (stop - start))
            print('timeout')
        return wrapper

@decorator
def test(list_test):
    for i in list_test:
        time.sleep(0.1)
        print('-' * 20, i)
        
#decorator(test)(range(10))
test(range(10))
4、带参数的装饰器
import time
def timer(timeout=0):
    def decorator(func):
        def wrapper(*args, **kwargs):
            start=time.time()
            func(*args, **kwargs)
            stop=time.time()
            print('run time is %s ' %(stop-start))
            print(timeout)
        return wrapper
    return decorator

@timer(2)
def test(list_test):
    for i in list_test:
        time.sleep(0.1)
        print('-'*20, i)

# timer(timeout=2)(test)(range(10))   timer(timeout=2) = decorator   timer(timeout=2)(test) = decorator(test) = wrapper
# timer(timeout=2)(test)(range(10)) = decorator(test)(range(10)) = wrapper(range(10))
test(range(10))
高级版装饰器:
user, passwd = 'lucky', 'abc123'

def auth(auth_type):
    print("auth func:", auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32mUser has passwed authentication\033[0m")
                    res = func(*args, **kwargs)  # from home
                    print("---after authenticaion")
                    return res
                else:
                    exit("\033[31mInvalid username or passwd\033[0m")
            elif auth_type == "ldap":
                print("\033[33m不会ldap,搞毛线啊!\033[0m")
        return wrapper
    return outer_wrapper

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs page")
    
@auth(auth_type="local")  # home = wrapper()
def home():
    print("welcome to home page")
    return "from home"

bbs()
home()
装饰器的应用: 装饰器功能:函数超时则终止
# -*- coding: utf-8 -*-  
from threading import Thread  
import time  
   
class TimeoutException(Exception):  
    pass  
   
ThreadStop = Thread._Thread__stop#获取私有函数  
   
def timelimited(timeout):  
    def decorator(function):  
        def decorator2(*args,**kwargs):  
            class TimeLimited(Thread):  
                def __init__(self,_error= None,):  
                    Thread.__init__(self)  
                    self._error =  _error  
                       
                def run(self):  
                    try:  
                        self.result = function(*args,**kwargs)  
                    except Exception,e:  
                        self._error =e  
   
                def _stop(self):  
                    if self.isAlive():  
                        ThreadStop(self)  
   
            t = TimeLimited()  
            t.start()  
            t.join(timeout)  
        
            if isinstance(t._error,TimeoutException):  
                t._stop()  
                raise TimeoutException('timeout for %s' % (repr(function)))  
   
            if t.isAlive():  
                t._stop()  
                raise TimeoutException('timeout for %s' % (repr(function)))  
   
            if t._error is None:  
                return t.result  
   
        return decorator2  
    return decorator  
  
@timelimited(2)  
def fn_1(secs):  
    time.sleep(secs)  
    return 'Finished'  
       
if __name__ == "__main__":  
    print fn_1(4)

0 个评论

要回复文章请先登录注册