装饰器模式可以以透明的方式动态的将功能添加到一个对象之中

可使用的场景:

可以用到装饰器的地方有很多,简单的举例如以下场景

  • 引入日志
  • 同击函数运行时间
  • 执行函数前预备处理
  • 执行函数后清理功能
  • 权限校验等场景
  • 缓存

例子

1
2
3
4
5
6
7
8
9
10
11
def func(func):
def print1():
print('装饰器进来了')
func()
return print1

@func
def gointo():
print("gogogog")

gointo()

等价于

1
2
3
4
5
6
7
8
9
10
11
def func(func):
def print1():
print('这是装饰器里面')
func()
return print1

def gointo():
print("gogogog")

gointo = func(gointo)
gointo()

使用装饰器装饰无返回值、无参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def func(func):
def print1():
print('装饰器进来了')
func()
return print1

@func
def gointo():
print("gogogog")

gointo()

>>>这是装饰器里面
>>>gogogog

使用装饰器装饰无返回值、有参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
def func(func):
def print1(num):
print('这是装饰器里面')
func(num)
return print1
@func
def gointo(num):
print("{}".format(num))

gointo(100)
>>>这是装饰器里面
>>>100

使用装饰器装饰不定长参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def func(func):
def print1(num,*args,**kwargs):
print('这是装饰器里面')
func(num,*args,**kwargs)
return print1
@func
def gointo(num,*args,**kwargs):
print("{}".format(num))
print("{}".format(args))
print("{}".format(kwargs))

gointo(100,241,113,421,mama='lala')

>>>100 #第一位num固定接收,后面只要不是
>>>(241, 113, 'sfsa', 421) #元组接收第二位以后不是字典格式的数据
>>>{'mama': 'lala'} #字典

通用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def func(func):
def print1(num,*args,**kwargs):
print('这是装饰器里面')
return func(num,*args,**kwargs) #注意这里也要return
return print1
@func
def gointo(num,*args,**kwargs):
print("{}".format(num))
print("{}".format(args))
print("{}".format(kwargs))
return "ok"

s = gointo(100,241,113,'sfsa',421,mama='lala')
print(s)
>>>100
>>>(241, 113, 'sfsa', 421)
>>>{'mama': 'lala'}
>>>ok

使用多个装饰器装饰一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def func1(func):
print('装饰器1') #第三步
def print1(num,*args,**kwargs):
print('这是装饰器里面1') #第四步
return func(num*2,*args,**kwargs)
return print1 #第三步后进行

def func2(func):
print('装饰器2') #第二步
def print1(num,*args,**kwargs):
print('这是装饰器里面2') #第五步
return func(num*3,*args,**kwargs)
return print1 #第二步后进行

@func1 #个人看法gointo = fun2(func1(gointo)),如有错误,麻烦指正
@func2
def gointo(num,*args,**kwargs):
print("{}".format(num))
print("{}".format(args))
print("{}".format(kwargs))
return "ok"

s = gointo(100,241,113,'sfsa',421,mama='lala')
print(s) #第一步
装饰器2
装饰器1
这是装饰器里面1
这是装饰器里面2
>>>600 #
>>>(241, 113, 'sfsa', 421)
>>>{'mama': 'lala'}
>>>ok
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def biggest():
def middle(mid):
def small(*args,**kwargs):
level = args[0]
if level == 1:
print("---一级警戒---")
elif level == 2:
print("---二级警戒---")
return mid()
return small #先定义好对象返回给函数
return middle #先定义好对象返回给函数

@biggest() #run1=biggest(run1)
def run1():
print("---run1---")
return "ok"
@biggest()
def run2():
print("---run2---")
return "ok"

run1(1) #传值给*arg arg=()
run2(2)
>>>---一级警戒---
>>>---run1---
>>>---二级警戒---
>>>---run2---

拓展:

funcyools

函数被装饰后函数名和属性已经改变,使用functools.wraps()来保留原有的函数属性和名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#不用functools.wraps()
import functools
def run(i):
def wrapper():
return i()
return wrapper

@run
def test():
print('lala')
return 'ok'
if __name__ == "__main__":
funcname = test()
print(funcname)
print("函数名称:{}".format(test.__name__))

>>>lala
>>>ok
>>>函数名称:wrapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#添加后
import functools
def run(i):
@functools.wraps(i)
def wrapper():
return i()
return wrapper

@run
def test():
print('lala')
return 'ok'
if __name__ == "__main__":
funcname = test()
print(funcname)
print("函数名称:{}".format(test.__name__))
#可以发现保留了原来的函数名
>>>lala
>>>ok
>>>函数名称:test

@staticmethod和@classmethod

在我们想使用某个类的方法时,一般需要实例化一个对象再通过对象调用方法,使用了@staticmethod和@classmethod之后,就省去了实例化生成对象的过程。有利于代码整洁、执行效率更高(类似c++里面的静态方法)。

@staticmethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Single:
__instance = None
def __init__(self):
print("__init__被执行啦")

#静态方法
@staticmethod
def getinstance():
return '这是静态方法里面'

#使用classmethod需要参数
# @classmethod
# def getinstance(cls):
# return '这是静态方法里面'

s = Single() #1
print(s.getinstance())
print('------')
print(Single.getinstance()) #2
#从1可以看到s对象调用了__init__方法,而2直接调用了类里面的方法,没有经过实例化的过程
#同时也可以知道@staticmethod不需要self参数和cls参数
>>>__init__被执行啦
>>>这是静态方法里面
>>>------
>>>这是静态方法里面

@classmethod

与@staticmethod不同,该装饰器需要cls类参数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#先实例化对象,然后再使用类里的方法
class LOL(object):
def __init__(self, name):
self.name = name

def nuokesasi(self):
print(self.name, '诺手')
return 'ok1'

@classmethod
def demaxiya(cls):
print('盖伦')
return 'ok2'

palyer = LOL('选择英雄:')
print(palyer.nuokesasi(), palyer.demaxiya())
>>>选择英雄: 诺手
>>>盖伦
>>>ok1 ok2

使用@classmethod来装饰类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class LOL():
def __init__(self, name):
self.name = name

def nuokesasi(self):
print(self.name, '诺手')

@classmethod
def demaxiya(cls):
print('盖伦')

LOL.demaxiya()
LOL.nuokesasi()

#demaxiya函数被装饰后可以直接调用
#报错是因为没有进行实例化,没有self参数传入
>>>盖伦
>>>Traceback (most recent call last):
>>> File "f:/pythonpachong/笔记/单例设计模式/测试代码.py", line 196, in <module>
>>> LOL.nuokesasi()
>>>TypeError: nuokesasi() missing 1 required positional argument: 'self'

@property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LOL(object):
def __init__(self, name):
self.__name = name

@property
def demaxiya(self):
return self.__name

@demaxiya.setter #经过property修饰的方法,想要修改其变量只能通过此方式
def demaxiya(self, value):
self.__name = value

test = LOL(name='诺手')
print(test.demaxiya)
test.demaxiya = '德玛'
print(test.demaxiya)
>>>诺手
>>>德玛