python 装饰器

一.装饰器概念与语法

python装饰器本质上就是一个嵌套函数,装饰器的返回值也是一个函数对象。简写:装饰器是以一个一函数为参数,返回值为函数的函数

语法:在需要装饰的类或者函数上面写@+函数名称。

二.装饰器的作用

 装饰器可以在不更改原功能代码情况下,可以扩展新的功能。

开放封闭原则:

1.以实现的功能,不要去修改其内部(被装饰的函数或类)代码(封闭原则)

2.对功能的扩展(装饰器的扩展)是开放的(开放原则)

三.装饰器的方法

1.函数(闭包函数)

1)函数内部嵌套函数

2)外层函数的返回值是内层函数的函数名

3)内层嵌套函数对外层有一个非全局变量的引用

2.类装饰器(__call__)

1)__call __()方法是将实例成为一个可调用对象(即callable对象),同时不影响实例的构造,但可以改变实例的内部值。

四.代码演示

1.闭包形式的装饰器

 1)闭包函数:符合上面三个条件

1 def func():
2     a=10
3     def func_01():
4         print(a)
5     return func_01

2)装饰器:装饰函数

 1 def func(fu):  #外层函数的第一个参数类型为函数
 2     def func_02():
 3         print("新功能")
 4         fu()
 5     return func_02
 6 
 7 @func   #func_1=func(func_1)
 8 def func_1():
 9     print("已经实现的功能")
10 if __name__ == "__main__":
11     func_1()

装饰器原理:

  • 当@装饰器装饰的时候,会自动调用装饰器函数,然后把被装饰的函数当作参数传入进去,==》fu = func_1
  • 装饰器的返回值,被原来被装饰的函数名接收。func_1 = func_02

调用被装饰的函数:

  • 因为被装饰的函数指向的func_02,那么此时就是直接调用func_02,func_02内部又调用了fu(fu实际上此时的指向是被装饰的函数func_01)

流程:@func就相当于func(func_1),所以就是调用func()函数,然后return func_02(返回func_02也就是相当于调用func_02()),那么会先打印print("新功能"),然后接下来再调用fu()函数(也就是func_1()函数),然后就是打印print("已经实现的功能"),然后就结束了。

3)装饰器:装饰类

 1 def func(fu):  #外层函数的第一个参数类型为函数
 2     def func_02():
 3         print("新功能")
 4         return fu()    #这里记得要加return ,要不然你下面 t=func_1() 返回就是一个None了,这里就是一个坑
 5     return func_02
 6 
 7 @func   #func_1=func(func_1)
 8 class func_1():
 9     def __init__(self):
10         print("已经实现的功能")
11 
12 t=func_1()
13 print(t)

4)装饰器:带固定参装饰器

 1 def func(fu):  #外层函数的第一个参数类型为函数
 2     def func_02(a,b):     #这里记得也要传入参数,要不然会报错
 3         print("新功能")
 4         fu(a,b)       #这里记得也要传入参数,要不然会报错
 5     return func_02
 6 
 7 @func   #func_1=func(func_1)
 8 def func_1(a,b):
 9         print("a+b",a+b)
10 
11 func_1(1,2)

5)装饰器:通用装饰器

 1 def func(fu):  #外层函数的第一个参数类型为函数
 2     def func_02(*args,**kwargs):
 3         print("新功能")
 4         fu(*args,**kwargs)
 5     return func_02
 6 
 7 @func   #func_1=func(func_1)
 8 def func_1():                      #你这里可以传入单个参数或者多个参数都可以
 9     print("a+b")
10 
11 func_1()

2.类装饰器(__call__)

1)类的装饰器写法, 不带参数(先通过构造函数__init __()传入函数;再通过__call __方法重载,并返回一个函数。)

 1 class func_02():
 2     def __init__(self, func):
 3         self.func = func
 4     def __call__(self, *args, **kwargs):
 5         print('{} is running'.format(self.func.__name__))
 6         return self.func(*args, **kwargs)
 7 
 8 @func_02
 9 def func_1():                #这里可以传入单个或者多个参数
10         print("运行成功")
11 
12 func_1()

装饰器原理:

  • 当@装饰器装饰的时候,它会先执行初始化函数内容,就相当于把被装饰的函数当作参数传入进去,==》func = func_1
  • 然后在调用__call__函数运行里面的代码后在执行func_1函数里面的代码,然后再返回去。

2)类的装饰器写法, 带参数(先通过构造函数__init __()传入装饰器参数;再通过__call __方法传入被装饰的函数,并返回一个函数)

 1 class func_02():
 2     def __init__(self, code):  
 3         self.code = code
 4     def __call__(self, func):
 5         def func_03(*args, **kwargs):
 6             print('{} is running'.format(func.__name__))
 7             print('Code: {}'.format(self.code))
 8             return func(*args, **kwargs)    # 正式调用主要处理函数
 9         return func_03
10 
11 @func_02(code="123")    #传入参数,可以单个或者多个
12 def func_1():
13     print("运行成功")
14 
15 func_1()

3.内置装饰器

1)@property:将一个方法变为属性调用。
未添加装饰器@property时,函数类型是一个方法:<class 'method'>
添加装饰器@property时,函数类型是返回值的类型:如,<class 'str'>
2)property对象的setter方法:表示给属性添加设置功能,即可修改属性值。
若未添加设置属性,就设置新值,则会引发错误AttributeError: can't set attribute。
3)property对象的deleter方法:表示给属性添加删除功能
若添加删除属性,就删除属性则会引发错误AttributeError: can't delete attribute
 1 """@property装饰器"""
 2 class Test_1:
 3     def __init__(self, name):
 4         self.name = name
 5 
 6     @property               # 将函数由方法变为属性
 7     def get_name(self):
 8         return self.name
 9 
10     @get_name.setter            # 添加设置属性
11     def get_name(self, value):
12         if not isinstance(value, str):
13             raise TypeError("参数应为字符串类型,但实际是%s类型" % type(value))
14         else:
15             self.name = value
16 
17     @get_name.deleter           # 添加删除属性
18     def get_name(self):
19         del self.name
20 
21 test1 = Test_1("long")
22 # 获取get_name类型
23 print(type(test1.get_name))      # 结果: <class 'str'>
24 # 获取get_name属性值
25 print(test1.get_name)            # 结果:long
26 # 给get_name属性设置新值:添加设置属性需使用装饰器@property的setter函数;
27 test1.get_name = "赋新值"
28 print(test1.get_name)           # 结果:赋新值
29 # 删除get_name属性:删除属性需使用装饰器@property的deleter函数;
30 del test1.get_name
31 print(test1.get_name)           # 结果:报错(AttributeError: 'Test1' object has no attribute '_Test1__name'),表示删除属性成功

4.第三方库装饰器

 1 from functools import wraps
 2 
 3 def decorate(func):
 4     print('running decorate', func)
 5     print('running decorate name:', func.__name__)
 6     #@wraps(func)     #标记代码
 7     def decorate_inner():
 8         print('running decorate_inner function', decorate_inner)
 9         print("你是:",decorate_inner.__name__)
10         return func()
11     return decorate_inner
12 
13 @decorate
14 def func_1():
15     print('running func_1', func_1)
16     print('你是:', func_1.__name__,"函数")
17 
18 if __name__ == '__main__':
19     func_1()

结果:

无启用@wraps(func):

说明:当你没有启用@wraps(func)时候,那么被装饰的函数名称已经被更改了。

启用@wraps(func):

 

说明:当你启用@wraps(func)时候,那么被装饰的函数名称没有被更改。

五.面试问题

1.请列举装饰器两种实现方式                    --------> 闭包函数、类中定义的__call__方法

2.装饰器可以装饰类吗                               --------> 可以

3.类可以当装饰器吗                                   --------> 可以

4.如何实现通用装饰器                               ---------> 传参使用不定长参数

5.可以同时被多个装饰器装饰吗                ---------> 可以,优先运行离装饰函数或类近的装饰器,以此往外执行。

六.应用场景

1.可以再外层函数加上时间计算函数,计算函数运行时间。

 1 import time
 2 def func_1(func):
 3     def func_2():
 4         start=time.time()
 5         func()
 6         time.sleep(1)
 7         end=time.time()-start
 8         print("运行时间是:",end)
 9     return func_2
10 
11 @func_1
12 def func_3():
13     print("运行结束")
14 
15 func_3()

2.计算函数运行次数。

3.可以用在框架的路由参数上。

4.插入日志,作为函数的运行日志。

5.事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败。

6.缓存,实现缓存处理。

7.权限的校验,在函数外层套上权限验证的代码,实现权限校验。

8.登录校验

 1 def login(func):  #外层函数的第一个参数类型为函数
 2     def func_02():
 3         name=input("请输入用户名:")
 4         pwd=input("请输入密码:")
 5         if name=="admin" and pwd==1:
 6             func()
 7         else:
 8             print("用户名或密码错误,请重新登录")
 9     return func_02
10 
11 @login   #func_1=login(func_1)
12 def func_1():
13         print("登录成功,欢迎您")
14 
15 func_1()

 

posted @ 2019-08-28 15:08  珠海-路飞  阅读(147)  评论(0编辑  收藏  举报