函数闭包与装饰器

# 统计函数的执行时间
import time
def cal(l):
    start_time=time.time()        #加入1
    res=0
    for i in l:
        time.sleep(0.1)
        res+=i
    stop_time = time.time()        #加入2
    print('函数的运行时间是%s' %(stop_time-start_time))
    return res
#如果函数是线上运行的,那修改源代码违反开放封闭式原则。因为别的可能还在调用它。

print(cal(range(100)))

def index():
    pass

def home():
    pass

一 什么是装饰器

装饰器即函数

装饰即修饰,意指为其他函数添加新功能

装饰器定义:本质就是函数,功能是为其他函数添加新功能

二 装饰器需要遵循的原则

1.不修改被装饰函数的源代码(开放封闭原则)

2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式

三 实现装饰器知识储备

装饰器=高阶函数+函数嵌套+闭包

四 高阶函数

高阶函数定义:
1.函数接收的参数是一个函数名

2.函数的返回值是一个函数名

3.满足上述条件任意一个,都可称之为高阶函数

#1、函数接收的参数是一个函数名:保证函数源代码不被修改
import time
def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):                    #test形参接收了函数名,为高阶函数。func为函数的内存地址
    # print(func)                    #打印出来的是内存地址
    start_time=time.time()
    func()                        #直接运行foo函数
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
# foo()
test(foo)

# 2、高阶函数的返回值也是一个函数名,函数的调用方式也不用修改了
def foo():
    print('from the foo')
def test(func):
    return func        

res=test(foo)        #从而func就是foo内存地址
# print(res)
res()                #函数直接就运行了

#进一步取消res,就用原来的foo名字
foo=test(foo)
# # print(res)
foo()                # 不改变函数调用方式



#########整合版本1
import time
def foo():
    time.sleep(3)
    print('来自foo')

#不修改foo源代码
#不修改foo调用方式
#多运行了一次,不合格
def timer(func):         #参数为函数名
    start_time=time.time()
    func()                #return func如果换到这里来,并没有给原函数添加任何功能。
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
    return func             #返回值为函数名
foo=timer(foo)             #赋给foo,执行了一遍
foo()                     #直接调用foo,又执行了一遍foo。只能改return
##结果:
来自foo
函数运行时间是 3.0001756482365
来自foo    #多打印了一个
#高阶函数没办法正常运行且顺利添加新功能
#1、函数接收的参数是一个函数名:保证函数源代码不被修改
import time
def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):                    #test形参接收了函数名,为高阶函数。func为函数的内存地址
    # print(func)                    #打印出来的是内存地址
    start_time=time.time()
    func()                        #直接运行foo函数
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
# foo()
test(foo)

# 2、高阶函数的返回值也是一个函数名,函数的调用方式也不用修改了
def foo():
    print('from the foo')
def test(func):
    return func        

res=test(foo)        #从而func就是foo内存地址
# print(res)
res()                #函数直接就运行了

#进一步取消res,就用原来的foo名字
foo=test(foo)
# # print(res)
foo()                # 不改变函数调用方式



#########整合版本1
import time
def foo():
    time.sleep(3)
    print('来自foo')

#不修改foo源代码
#不修改foo调用方式
#多运行了一次,不合格
def timer(func):         #参数为函数名
    start_time=time.time()
    func()                #return func如果换到这里来,并没有给原函数添加任何功能。
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
    return func             #返回值为函数名
foo=timer(foo)             #赋给foo,执行了一遍
foo()                     #直接调用foo,又执行了一遍foo。只能改return
##结果:
来自foo
函数运行时间是 3.0001756482365
来自foo    #多打印了一个
#高阶函数没办法正常运行且顺利添加新功能
#1、函数接收的参数是一个函数名:保证函数源代码不被修改
import time
def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):                    #test形参接收了函数名,为高阶函数。func为函数的内存地址
    # print(func)                    #打印出来的是内存地址
    start_time=time.time()
    func()                        #直接运行foo函数
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
# foo()
test(foo)

# 2、高阶函数的返回值也是一个函数名,函数的调用方式也不用修改了
def foo():
    print('from the foo')
def test(func):
    return func        

res=test(foo)        #从而func就是foo内存地址
# print(res)
res()                #函数直接就运行了

#进一步取消res,就用原来的foo名字
foo=test(foo)
# # print(res)
foo()                # 不改变函数调用方式



#########整合版本1
import time
def foo():
    time.sleep(3)
    print('来自foo')

#不修改foo源代码
#不修改foo调用方式
#多运行了一次,不合格
def timer(func):         #参数为函数名
    start_time=time.time()
    func()                #return func如果换到这里来,并没有给原函数添加任何功能。
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
    return func             #返回值为函数名
foo=timer(foo)             #赋给foo,执行了一遍
foo()                     #直接调用foo,又执行了一遍foo。只能改return
##结果:
来自foo
函数运行时间是 3.0001756482365
来自foo    #多打印了一个
#高阶函数没办法正常运行且顺利添加新功能
#1、函数接收的参数是一个函数名:保证函数源代码不被修改
import time
def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):                    #test形参接收了函数名,为高阶函数。func为函数的内存地址
    # print(func)                    #打印出来的是内存地址
    start_time=time.time()
    func()                        #直接运行foo函数
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
# foo()
test(foo)

# 2、高阶函数的返回值也是一个函数名,函数的调用方式也不用修改了
def foo():
    print('from the foo')
def test(func):
    return func        

res=test(foo)        #从而func就是foo内存地址
# print(res)
res()                #函数直接就运行了

#进一步取消res,就用原来的foo名字
foo=test(foo)
# # print(res)
foo()                # 不改变函数调用方式



#########整合版本1
import time
def foo():
    time.sleep(3)
    print('来自foo')

#不修改foo源代码
#不修改foo调用方式
#多运行了一次,不合格
def timer(func):         #参数为函数名
    start_time=time.time()
    func()                #return func如果换到这里来,并没有给原函数添加任何功能。
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
    return func             #返回值为函数名
foo=timer(foo)             #赋给foo,执行了一遍
foo()                     #直接调用foo,又执行了一遍foo。只能改return
##结果:
来自foo
函数运行时间是 3.0001756482365
来自foo    #多打印了一个
#高阶函数没办法正常运行且顺利添加新功能
#1、函数接收的参数是一个函数名:保证函数源代码不被修改
import time
def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):                    #test形参接收了函数名,为高阶函数。func为函数的内存地址
    # print(func)                    #打印出来的是内存地址
    start_time=time.time()
    func()                        #直接运行foo函数
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
# foo()
test(foo)

# 2、高阶函数的返回值也是一个函数名,函数的调用方式也不用修改了
def foo():
    print('from the foo')
def test(func):
    return func        

res=test(foo)        #从而func就是foo内存地址
# print(res)
res()                #函数直接就运行了

#进一步取消res,就用原来的foo名字
foo=test(foo)
# # print(res)
foo()                # 不改变函数调用方式

#########整合版本1
import time
def foo():
    time.sleep(3)
    print('来自foo')

#不修改foo源代码
#不修改foo调用方式
#多运行了一次,不合格
def timer(func):         #参数为函数名
    start_time=time.time()
    func()                
    stop_time = time.time()
    print('函数运行时间是  %s' % (stop_time-start_time))
    return func             #返回值为函数名
foo=timer(foo)             #赋给foo,执行了一遍
foo()                     #直接调用foo,又执行了一遍foo。只能改return
##结果:
来自foo
函数运行时间是 3.0001756482365
来自foo    #多打印了一个
#高阶函数没办法正常运行且顺利添加新功能

#没有修改被修饰函数的源代码,也没有修改被修饰函数的调用方式,但是也没有为被修饰函数添加新功能
def timer(func):
start_time=time.time()
return func #return func如果换到这里来,并没有给原函数添加任何功能。
stop_time = time.time()
print('函数运行时间是 %s' % (stop_time-start_time)

foo=timer(foo)
foo()


#高阶函数应用1:把函数当做参数传给高阶函数
import time
def foo():
    print('from the foo')

def timmer(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print('函数%s 运行时间是%s' %(func,stop_time-start_time))
timmer(foo)
#总结:我们确实为函数foo增加了foo运行时间的功能,但是foo原来的执行方式是foo(),现在我们需要调用高阶函数timmer(foo),改变了函数的调用方式
#高阶函数应用2:把函数名当做参数传给高阶函数,高阶函数直接返回函数名
import time
def foo():
    print('from the foo')

def timmer(func):
    start_time=time.time()
    return func
    stop_time=time.time()
    print('函数%s 运行时间是%s' %(func,stop_time-start_time))
foo=timmer(foo)
foo()
#总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能

高阶函数总结
1.函数接收的参数是一个函数名
  作用:在不修改函数源代码的前提下,为函数添加新功能,
  不足:会改变函数的调用方式
2.函数的返回值是一个函数名
  作用:不修改函数的调用方式
  不足:不能添加新功能

五 函数嵌套

# 函数嵌套:在一个函数中重新定义一个新的函数
def foo():
    print('from foo')
    def test():
        pass

def father(auth_type):
    # print('from father %s' %name)
    def son():
        # name='linhaifeng_1'
        # print('我的爸爸是%s' %name)
        def grandson():
            print('我的爷爷是%s' %auth_type)    #在上上级找,在最外层传,这里也能收到
        grandson()            #运行函数
    # print(locals())        #打印当前层的局部变量:name,son函数也是
    son()
# father('linhaifeng')
father('filedb')
六 闭包
 1 '''
 2 闭包:在一个作用域里放入定义变量,相当于打了一个包
 3 '''
 4 def father(name):
 5     def son():
 6         # name='alex'
 7         print('我爸爸是 [%s]' %name)
 8         def grandson():
 9             # name='wupeiqi'
10             print('我爷爷是 [%s]' %name)
11         grandson()
12     son()
13 
14 father('林海峰')
#装饰器实现
import
time def timmer(func): #func=test def wrapper(): # print(func) start_time=time.time() func() #就是在运行test() stop_time = time.time() print('运行时间是%s' %(stop_time-start_time)) return wrapper @timmer #test=timmer(test) def test(): time.sleep(3) print('test函数运行完毕') test() res=timmer(test) #返回的是wrapper的地址 res() #执行的是wrapper() test=timmer(test) #返回的是wrapper的地址 test() #执行的是wrapper() @timmer 就相当于 test=timmer(test)

七 无参装饰器

#补充知识:用解压的方式取出一个列表的最开头和结尾
#比索引略微简洁一些,有的时候适用
#例1:
a,b,c='hel'
>>>a
'h'
>>>b
'e'
>>>c
'l'
#例2:a表示列表第一个,c表示最后一个
l=[0,2,3,445,67,8,9,92,2,1]
>>>a.*_.c=l       #下划线表示中间所有元素的变量名,可以随便起,*m也可以
>>>a    
'0'
>>>c
'1'        
#例3:取出头尾两个
>>>a,b.*_.c,d=l 
#对比索引:a,d=l[0],l[-1]

无参装饰器=高级函数+函数嵌套

基本框架

1 #这就是一个实现一个装饰器最基本的架子
2 def timer(func):      #func就相当于你输入的test函数
3     def wrapper():
4      print(func)   #函数嵌套 4 func() 5 return wrapper

加上参数

1 def timer(func):
2     def wrapper(*args,**kwargs):
3         func(*args,**kwargs)
4     return wrapper

加上功能

1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         func(*args,**kwargs)
6         stop_time=time.time()
7         print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
8     return wrapper

加上返回值

1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         res=func(*args,**kwargs)
6         stop_time=time.time()
7         print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
8         return res
9     return wrapper

使用装饰器

1 def cal(array):
2     res=0
3     for i in array:
4         res+=i
5     return res
6 
7 cal=timer(cal)
8 cal(range(10))

语法糖@

1 @timer  #@timer就等同于cal=timer(cal)
2 def cal(array):
3     res=0
4     for i in array:
5         res+=i
6     return res
7 
8 cal(range(10))

八 装饰器应用示例

# 京东登录(无参数)
# 利用用户列表,for循环来遍历该字典
user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}    #记录下当前用户的状态

#加上认证功能的装饰器函数:参数、返回值
#登录一次之后,不用每次都再输。应该记录下登录一次的状态(用一个全局变量来实现)
def auth_func(func):            #函数名
        def wrapper(*args,**kwargs):
          if auth_type == 'filedb':        
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('用户名:').strip()
                passwd=input('密码:').strip()
                for user_dic in user_list:        #遍历列表中所有记录是否与输入的名字密码匹配
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username']=username    #记录下登录的状态:拿的就是全局变量的引用
                        current_dic['login']=True
                        res = func(*args, **kwargs)            #验证成功了才可以直接运行函数
                        return res                            #wrapper函数直接结束,不必再加break语句了
                else:
                    print('用户名或者密码错误')        #此else与for连用,不是循环一次后的if连用。列表中用户名和密码全for循环完毕了return wrapper
    return auth_func

@auth
def index():                        #已经输入正确用户名及密码了
    print('欢迎来到京东主页')

@authdef home(name):
    print('欢迎回家%s' %name)

@authdef shopping_car(name):
    print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

print('before-->',current_dic)
index()
print('after--->',current_dic)
home('产品经理')
shopping_car('产品经理')
复制代码
user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]

current_user={'username':None,'login':False}
def auth(auth_type='file'):
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if auth_type == 'file':
                if current_user['username'] and current_user['login']:
                    res=func(*args,**kwargs)
                    return res
                username=input('用户名: ').strip()
                passwd=input('密码: ').strip()

                for index,user_dic in enumerate(user_list):
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_user['username']=username
                        current_user['login']=True
                        res=func(*args,**kwargs)
                        return res
                        break
                else:
                    print('用户名或者密码错误,重新登录')
            elif auth_type == 'ldap':
                print('巴拉巴拉小魔仙')
                res=func(*args,**kwargs)
                return res
        return wrapper
    return auth_deco


#auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file')
#就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
@auth(auth_type='ldap')
def index():
    print('欢迎来到主页面')

@auth(auth_type='ldap')
def home():
    print('这里是你家')

def shopping_car():
    print('查看购物车啊亲')

def order():
    print('查看订单啊亲')

# print(user_list)
index()
# print(user_list)
home()
复制代码

 装饰器运行流程

import time
def timmer(func): #func=test1   第二步
# wrapper中参数数目不要写死,用列表装
    def wrapper(*args,**kwargs): #第五步  #test('linhaifeng',age=18)  元祖args=('linhaifeng')  kwargs={'age':18}
        start_time=time.time()     #第六步 
        res=func(*args,**kwargs) #第七步,接着去找test函数定义去了   就是在运行test()         func(*('linhaifeng'),**{'age':18})
       #wrapper中怎么接收的就怎么传给func
        stop_time = time.time()     #第十二步
        print('运行时间是%s' %(stop_time-start_time))        #第十三步
        return res            ##第十四步  加入返回值,结束wrapper
    return wrapper            #第三步

@timmer #test=timmer(test)    #第一步:运行这个执行timmer函数,直接返回wrapper地址给test,然后到test()调用
def test(name,age):            #第八步:对应wrapper中形参表也要加
    time.sleep(3)            #第九步
    print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))        #第十步
    return '这是test的返回值'    #第十一步
test()                    #第四步,直接运行wrapper函数
# 京东登录:auth函数中带参数:数据库的认证类型来源
# 带参数的少用,不带参数的更加常用
# 利用用户列表,for循环来遍历该字典
# 缺点1、认证来源:用户列表应该从数据库里取出来。写死了数据库
#(可以将user_list写到文件当中便于增删改。注意提取文件内容时都是得到字符串,用eval剥离出来转化为字典)
user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}    #记录下当前用户的状态

#加上认证功能的装饰器函数:参数、返回值
#登录一次之后,不用每次都再输。应该记录下登录一次的状态(用一个全局变量来实现)
def auth(auth_type='filedb'):
    def auth_func(func):            #函数名
        def wrapper(*args,**kwargs):
            print('认证类型是',auth_type)        
            if auth_type == 'filedb':        
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('用户名:').strip()
                passwd=input('密码:').strip()
                for user_dic in user_list:        #遍历列表中所有记录是否与输入的名字密码匹配
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username']=username    #记录下登录的状态:拿的就是全局变量的引用
                        current_dic['login']=True
                        res = func(*args, **kwargs)            #验证成功了才可以直接运行函数
                        return res                            #wrapper函数直接结束,不必再加break语句了
                else:
                    print('用户名或者密码错误')        #此else与for连用,不是循环一次后的if连用。列表中用户名和密码全for循环完毕了
            elif auth_type == 'ldap':
                print('鬼才特么会玩')
                res = func(*args, **kwargs)
                return res
            else:
                print('鬼才知道你用的什么认证方式')
                res = func(*args, **kwargs)
                return res

        return wrapper
    return auth_func

    #对auth_func添加参数,加括号相当于直接运行auth函数,因此在这里向无参数形式当中加入return auth_func
@auth(auth_type='filedb') 
# auth函数的返回值:auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type  
# --->index=auth_func(index)这个指向的返回值还是wrapper,继续执行wrapper
def index():                        #已经输入正确用户名及密码了
    print('欢迎来到京东主页')

@auth(auth_type='ldap')
def home(name):                    
    print('欢迎回家%s' %name)

@auth(auth_type='sssssss')
def shopping_car(name):
    print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

print('before-->',current_dic)
index()
print('after--->',current_dic)
home('产品经理')
shopping_car('产品经理')

九 超时装饰器

复制代码
import sys,threading,time


class KThread(threading.Thread):

    """A subclass of threading.Thread, with a kill()

    method.



    Come from:

    Kill a thread in Python:

    http://mail.python.org/pipermail/python-list/2004-May/260937.html

    """

    def __init__(self, *args, **kwargs):

        threading.Thread.__init__(self, *args, **kwargs)

        self.killed = False



    def start(self):

        """Start the thread."""

        self.__run_backup = self.run

        self.run = self.__run      # Force the Thread to install our trace.

        threading.Thread.start(self)



    def __run(self):

        """Hacked run function, which installs the

        trace."""

        sys.settrace(self.globaltrace)

        self.__run_backup()

        self.run = self.__run_backup



    def globaltrace(self, frame, why, arg):

        if why == 'call':

          return self.localtrace

        else:

          return None



    def localtrace(self, frame, why, arg):

        if self.killed:

          if why == 'line':

            raise SystemExit()

        return self.localtrace



    def kill(self):

        self.killed = True



class Timeout(Exception):

    """function run timeout"""



def timeout(seconds):

    """超时装饰器,指定超时时间

    若被装饰的方法在指定的时间内未返回,则抛出Timeout异常"""

    def timeout_decorator(func):

        """真正的装饰器"""



        def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):

            result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))



        def _(*args, **kwargs):

            result = []

            new_kwargs = { # create new args for _new_func, because we want to get the func return val to result list

                'oldfunc': func,

                'result': result,

                'oldfunc_args': args,

                'oldfunc_kwargs': kwargs

            }

            thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)

            thd.start()

            thd.join(seconds)

            alive = thd.isAlive()

            thd.kill() # kill the child thread

            if alive:

                raise Timeout(u'function run too long, timeout %d seconds.' % seconds)

            else:

                return result[0]

        _.__name__ = func.__name__

        _.__doc__ = func.__doc__

        return _

    return timeout_decorator


@timeout(5)

def method_timeout(seconds, text):

    print('start', seconds, text)

    time.sleep(seconds)

    print('finish', seconds, text)

    return seconds


method_timeout(6,'asdfasdfasdfas')
原文地址:https://www.cnblogs.com/Josie-chen/p/8707322.html