python装饰器

python装饰器

 


一 什么是装饰器

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

 

 

二 装饰器需要遵循的原则

1.不能修改被修饰函数的源代码
2.不能修改被修饰函数的调用方式

 

现在有下面一个函数,如果我们要为这个函数添加一个功能:测试程序的运行时间。

import time
list=[1,2,3,4,5]
def cal(list):
    for i in list:
        time.sleep(0.5)
        print(i)
cal(list)

 

import time
list=[1,2,3,4,5]
def cal(list):
    start_time=time.time()
    for i in list:
        time.sleep(0.5)
        print(i)
    stop_time=time.time()
    print("运行时间:%s" %(stop_time-start_time))

cal(list)
1
2
3
4
5
运行时间:2.502134323120117
运行结果

总结:我们虽然实现了功能,但是我们要知道两大原则,现在我们直接修改函数的源代码,这样做是不行的。

 

三 实现装饰器知识储备

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

 

四 高阶函数

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

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

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

def foo():
    print('我的函数名作为参数传给高阶函数')
def gao_jie1(func):
    print('我就是高阶函数1,我接收的参数名是%s' %func)
    func()

def gao_jie2(func):
    print('我就是高阶函数2,我的返回值是%s' %func)
    return func

gao_jie1(foo)
gao_jie2(foo)
高阶函数示例

只满足一个条件:函数接收的参数是一个函数名

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),改变了函数的调用方式
View Code

满足两个条件

1.函数接收的参数是一个函数名
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增加任何新功能
View Code

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

 

 

五 函数嵌套

def father(name):
    print('from father %s' %name)
    def son():
        print('from son')
        def grandson():
            print('from grandson')
        grandson()
    son()

father('aaa')

 

 

六 闭包

闭包:  就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁
闭包:闭包是指内部函数调用外部函数的变量
包是指嵌套,闭是指封装
def test1():
	a=1
	def test2():
		print(a)
	test2()
test1()

  

七 无参装饰器

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

基本框架

1 #这就是一个实现一个装饰器最基本的架子
2 def timer(func):
3     def wrapper():
4         func()
5     return wrapper

 

加上参数

解决被修饰的函数是有参数的。

 

1 def timer(func):
2     def wrapper(*args,**kwargs):
3         func(*args,**kwargs)
4     return wrapper
import time
def timmer(func):#func=test
    def wrapper(name,age):
        start_time = time.time()
        res=func(name,age)  # 就是在运行test()
        stop_time = time.time()
        print("函数运行时间的是%s" % (stop_time - start_time))
        return res
    return wrapper
# @timmer 等效于 test=timmer(test)
@timmer
def test(name,age):
    time.sleep(3)
    print('test函数运行完毕')
    return "这是test的返回值"
res=test("lingling",20)
print(res)


@timmer
# @timmer 等效于 test1=timmer(test1)
def test1(name,age,gender):
    time.sleep(3)
    print('test函数运行完毕')
    return "这是test1的返回值"
res=test1("lingling",20)
print(res)
#@timmer 等效于 test=timmer(test),#在这里一定要注意,我们首先把test函数传入到timmer装饰器,我们下一步返回了wrapper函数的地址(return wrapper)。
#test变量接收到wrapper函数的地址
#test("lingling",20)传入参数,执行wrapper函数,这里test函数有参数,为name,age,所以wrapper函数也应该有两个变量name,age,而执行wrapper函数,实际是要执行test函数,此处也需要两个参数,name,age
#res变量接收到test函数执行的结果



# 1.将test函数传入timmer:timmer(test)
# 2.执行timmer函数,返回wrapper函数地址(return wrapper)
# 3.执行wrapper函数(执行wrapper函数实则是执行test函数),并接收test函数返回值:res=test("lingling",20)
参数个数不同
import time
def timmer(func):#func=test
    def wrapper(*wargs,**kwargs):
        start_time = time.time()
        res=func(*wargs,**kwargs)  # 就是在运行test()
        stop_time = time.time()
        print("函数运行时间的是%s" % (stop_time - start_time))
        return res
    return wrapper

@timmer
def test(name,age):
    time.sleep(3)
    print('test函数运行完毕')
    return "这是test的返回值"
res=test("lingling",age=20)
print(res)


@timmer
def test1(name,age,gender):
    time.sleep(3)
    print('test1函数运行完毕')
    return "这是test1的返回值"
res=test1("lingling",20,'male')
print(res)

#解决wrapper不固定接收参数的问题
#如果细心可能会想,这里我们把wrapper参数写死了,你只能传入两个参数,如果有不同的函数需要这个装饰器,那我们不得重新修改装饰器,这真是一个大问题,我们来解决这个问题
#所以我们在wrapper函数中应该接收到test函数的所有参数:*wargs,**kwargs
# *wargs:接收所有位置参数
# **kwargs:接收键值对类型的参数
#我们将wrapper函数与内部func函数的参数设置*wargs,**kwargs就可以接收所有传进来的参数

#第二个问题,func接收参数

# 使用*wargs,**kwargs怎么接收参数
# 就使用*wargs,**kwargs怎么传递参数
# 我们可以把*wargs,**kwargs看成一个整体,接收所有的参数
解决接收不同个数的参数
def test2(name,age,gender): #test2(*('alex',18,'male'),**{})
    print(name)
    print(age)
    print(gender)
    
def test1(*args,**kwargs):
    test2(*args,**kwargs) #args=('alex',18,'male')  kwargs={}
test1('alex',18,'male')


def test3(*args,**kwargs):
    print(args)
    print(kwargs)
#
test3(1,2,3,4,name="linlin")
test3(*(1,2,3,4),**{'name':'linlin'})
args与kwargs的补充

 

加上功能

在装饰器的框架基础上添加被测试函数所需要的功能。
import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
    return wrapper
import time
def timmer(func):#func=test
    def wrapper():
        start_time = time.time()
        func()  # 就是在运行test()
        stop_time = time.time()
        print("函数运行时间的是%s" % (stop_time - start_time))
    return wrapper
# @timmer 等效于 test=timmer(test)
@timmer
def test():
    time.sleep(3)
    print('test函数运行完毕')
test()
import time
def timmer(func):#func=test
    def wrapper():
        start_time = time.time()
        func()  # 就是在运行test()
        stop_time = time.time()
        print("函数运行时间的是%s" % (stop_time - start_time))
    return wrapper


def test():
    time.sleep(3)
    print('test函数运行完毕')
test=timmer(test)#返回的是wrapper的地址
test()#执行的是wrapper()
原型

 

加上返回值

解决在测试程序里有返回值的情况。
import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
        return res
    return wrapper
import time
def timmer(func):#func=test
    def wrapper():
        start_time = time.time()
        res=func()  # 就是在运行test()
        stop_time = time.time()
        print("函数运行时间的是%s" % (stop_time - start_time))
        return res
    return wrapper
# @timmer 等效于 test=timmer(test)
@timmer
def test():
    time.sleep(3)
    print('test函数运行完毕')
    return "这是test的返回值"
res=test()
print(res)

 

语法糖@

如果我们需要使用装饰器,只需要在被测试的函数前加上@装饰器函数名
@timmer 等效于 test=timmer(test)
@timer  #@timer就等同于cal=timer(cal)
def cal(array):
    res=0
    for i in array:
        res+=i
    return res

cal(range(10))

 

补充

a,b,c=(1,2,3)
print(a)
print(b)
print(c)
a,b,c='hel'
print(a)
print(b)
print(c)

#如果我们不使用索引,如何去列表的第一个值与最后一个值
list=[10,4,2,7,2,8,3,8,3,6]
a,*d,c=list#这里*代表中间所有的值,*后面的代表为一个变量d接收中间的所有值
print(a)
print(c)
print(d)

#使用索引取第一个和最后一个值
a,c=list[0],list[-1]
print(a)
print(c)

#在python中交换两个值
f1=1
f2=2
f1,f2=f2,f1
print(f1,f2)
解压序列

 

 

装饰器应用示例

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_deco(func):
    def wrapper(*args,**kwargs):
        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('用户名或者密码错误,重新登录')

    return wrapper

@auth_deco
def index():
    print('欢迎来到主页面')

@auth_deco
def home():
    print('这里是你家')

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

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

print(user_list)
# index()
print(user_list)
home()

无参装饰器
无参装饰器
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 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/linhaifeng/articles/6140395.html

你可能感兴趣的:(python装饰器)