装饰器
装饰器
一、闭包
1、说明
闭包函数必须满足两个条件:
1.函数内部定义的函数
2.包含对外部作用域而非全局作用域的引用
闭包的特点:
1.自带作用域
2.延迟计算
2、使用
z = 100 def outer(): x = 2 y = 3 def inner(): print("x = %s" % x) print("y = %s" % y) print("z = %s" % z) # 先查找局部参数,没有z,再查找全局参数。 print(inner.__closure__) # 查看使用的函数内部参数 return inner ret = outer() # 一共引用了2个局部参数x、y,下面是结果。z是全局参数。 # (<cell at 0x052A0FF0: int object at 0x569ED730>, <cell at 0x052AC090: int object at 0x569ED740>) ret() # 延迟执行 # x = 1 # y = 2 # z = 100
from urllib.request import urlopen def index(url) def get() return urlopen(url).read() return get python = index("http://www.python.org") # 返回的是get函数的地址 print(python()) # 执行get函数《并且将返回的结果打印出来 baidu = index("http://www.baidu.com") print(baidu())
二、装饰器
装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。
特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码
1、函数相关的装饰器
(1)无参装饰器
在不修改主逻辑函数的情况下,增加额外的功能
原函数
def index(): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index page") index() # welcome to index page
方法1.定义额外的函数,在函数内部引用原函数。但函数的使用方法改变了,不可取。
import time, random def index2(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index2 page") return "index2的返回值x:%s" % x def timer(*args, **kwargs): start = time.time() my_func = index2(*args, **kwargs) end = time.time() print("主逻辑耗时:%s 秒" % (end - start)) return my_func ret = timer(10) # 使用方法改变了 print(ret) # welcome to index2 page # 主逻辑耗时:1.0005226135253906 秒 # index2的返回值x:10
方法2.装饰器,函数的使用方法不变
import time, random def timer(func): def wrapper(*args,**kwargs): start = time.time() my_func = func(*args,**kwargs) end = time.time() print("主逻辑耗时:%s 秒" % (end-start)) return my_func return wrapper @timer def index1(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index1 page") return "index1的返回值x:%s" % x def index2(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index2 page") return "index2的返回值x:%s" % x ret = index1(10) # 使用装饰器 print(ret) # welcome to index1 page # 主逻辑耗时:2.0000548362731934 秒 # index1的返回值x:10 ret = timer(index2)(10) # 等价于上面index1 print(ret) # welcome to index2 page # 主逻辑耗时:1.0003700256347656 秒 # index2的返回值x:10
(2)有参装饰器
装饰器里增加参数,其实就是再无参装饰器外层再套一层函数。
import time,random def timer(*args_timer,**kwargs_timer): # 在无参装饰器外层增加一层有参函数 def outer(func): # 与上面无参装饰器一样的使用 def inner(*args_inner,**kwargs_inner): start = time.time() ret = func(*args_inner,**kwargs_inner) end = time.time() print("装饰器的参数0:%s" % args_timer[0]) # 增加的装饰器参数,根据实际情况使用 print("主逻辑耗时:%s 秒" % (end - start)) return ret print("装饰器的参数1:%s" % args_timer[1]) # 增加的装饰器参数,根据实际情况使用 return inner print("装饰器的参数2:%s" % args_timer[2]) # 增加的装饰器参数,根据实际情况使用 return outer
@timer("timer0","timer1","timer2") def index3(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index3 page") return "index3的返回值x:%s" % x
ret = index3(100) print(ret) # 装饰器的参数2:timer2 # 装饰器的参数1:timer1 # welcome to index3 page # 装饰器的参数0:timer0 # 主逻辑耗时:2.000260829925537 秒 # index3的返回值x:100 ################################################################################## def index4(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index4 page") return "index4的返回值x:%s" % x ret = timer("timer0","timer1","timer2")(index4)(100) # 相当于上面使用了有参装饰器的index3 print(ret) # 装饰器的参数2:timer2 # 装饰器的参数1:timer1 # welcome to index4 page # 装饰器的参数0:timer0 # 主逻辑耗时:2.000366449356079 秒 # index4的返回值x:100
2、类相关的装饰器
(1)装饰器是类
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object): def __init__(self, func): self._func = func def __call__(self): print('类装饰器 runing') self._func() print('类装饰器 ending') @Foo # bar = Foo(bar) def bar(): print('bar') bar() # Foo(bar)() # 结果 # 类装饰器 runing # bar # 类装饰器 ending
(2)类自带的装饰器
@property、@staticmethod、@classmethod
待补充
(3)让类作为装饰器的参数
class MyClass(object): @staticmethod def clsfunc1(): print("MyClass的func1使用(是staticmethod,不用实例化对象)") @staticmethod def clsfunc2(): print("MyClass的func2使用(是staticmethod,不用实例化对象)") def deco(cls): """使用类的staticmethod,""" def outer2(func): def outer1(*args,**kwargs): cls.clsfunc1() ret = func(*args,**kwargs) cls.clsfunc2() return ret return outer1 return outer2 @deco(MyClass) # 对函数使用 def myfunc(x): print(" myfunc() called.") return x ret = myfunc(100) print(ret) # MyClass的func1使用(是staticmethod,不用实例化对象) # myfunc() called. # MyClass的func2使用(是staticmethod,不用实例化对象) # 100 ######################################################## class Foo(object): @deco(MyClass) # 跟函数一样,可使用多个装饰器 def run(self,x): print("Foo is running") return x foo = Foo() ret = foo.run(999) print(ret) # MyClass的func1使用(是staticmethod,不用实例化对象) # Foo is running # MyClass的func2使用(是staticmethod,不用实例化对象) # 999
(4)对整个类进行装饰(后续再扩展,涉及到__init__,__new__)
def deco(func): def wrapper(*args, **kwargs): print('before') ret = func(*args, **kwargs) print('after') return ret return wrapper @deco class Person: def __new__(cls, *args, **kwargs): # 在类__init__先进行__new__ print('__new__') return args,kwargs ret = Person(123,123,x=2) print(ret) # before # __new__ # after # ((123, 123), {'x': 2})
4、使用多个装饰器
多层装饰器
def outer1(func): def wrapper(*args,**kwargs): print("无参装饰器outer1") ret = func(*args,**kwargs) return ret return wrapper def outer2(func): def wrapper(*args,**kwargs): print("无参装饰器outer2") ret = func(*args,**kwargs) return ret return wrapper def outer3(x,y,z): def wrapper2(func): def wrapper1(*args, **kwargs): print("有参装饰器outer3") print("x:{},y:{},z:{}".format(x,y,z)) ret = func(*args, **kwargs) return ret return wrapper1 return wrapper2 @outer3(1,2,3) # 3.把outer2套在里面 tmp3 = outer3(1,2,3)(tmp2) @outer2 # 2.把outer1套在里面 tmp2 = outer2(tmp1) @outer1 # 1.优先把index5套在里面 tmp1 = outer(index5) def index5(x): print("主逻辑函数index5") return "index5的返回值x:%s" % x ret = index5(100) print(ret) # 有参装饰器outer3 # x:1,y:2,z:3 # 无参装饰器outer2 # 无参装饰器outer1 # 主逻辑函数index5 # index5的返回值x:100 ############################################################## def index5(x): print("主逻辑函数index5") return "index5的返回值x:%s" % x ret = outer3(1,2,3)(outer2(outer1(index5)))(100) print(ret) # 有参装饰器outer3 # x:1,y:2,z:3 # 无参装饰器outer2 # 无参装饰器outer1 # 主逻辑函数index5 # index5的返回值x:100
4、其他(待补充)
待补充
三、示例
1、登录
def index1(): print("欢迎来到购物系统") print("消费100元") print("明天到货") return None index1() # 欢迎来到购物系统 # 消费100元 # 明天到货 ####################################################################### def auth(func): def wrapper(*args,**kwargs): user = input("用户名>>:") password = input("密码>>:") if user == "abc" and password == "123": ret = func(*args,**kwargs) return ret else: print("用户名或密码错误,请重新登录") return wrapper @auth def index2(): print("欢迎来到购物系统") print("消费100元") print("明天到货") return None index2() # 用户名>>:abc # 密码>>:123 # 欢迎来到购物系统 # 消费100元 # 明天到货 # 或 # 用户名>>:asdfsdf # 密码>>:sdfsdf # 用户名或密码错误,请重新登录