python中装饰器_Python中的装饰器

webchat.jpg

刚开始学习Python中的装饰器,一直不理解有什么用处,后来在知乎上看到某个回答,解决了自己的一些疑惑,在这里做个记录。

简单的装饰器

Python中的装饰器其实是一个函数,可以在不改变任何的代码的前提下为某个函数增加额外的功能。通常使用装饰器可以抽离出大量与函数功能本身无关的代码并继续重用。也可以理解成为已经存在的对象添加额外的功能。

举个列子,定义个函数:

1

2

3

4def ():

print('call foo ...')

foo()

如果现在添加一个新的需求,输出函数执行日志。我们可以定义一个print_log(func)函数:

1

2

3

4

5

6

7

8

9def print_log(func):

print("%s is running" % func.__name__)

func()

def ():

print('call foo ...')

print_log(foo)

这种方式看起来非常好,能满足我们的目的。但是这种方式破坏了原来函数的封装性,以前直接调用函数foo(),现在不得不调用print_log(foo)。当然也可以使用装饰器来达到同样的目的。

1

2

3

4

5

6

7

8

9

10

11def print_log(func):

def wrapper(*args, **kwargs):

print("%s is running" % func.__name__)

return func(*args, **kwargs)

return wrapper

def ():

print('call foo ...')

bar = print_log(foo)

bar()

print_log函数就是装饰器,它将执行的业务方法包再函数里面,我们看起来就好像foo函数被print_log函数装饰了。在函数进入和退出时,被称为一个横切面,这种编程方式被称为面向切片编程[email protected],在定义函数的时候使用,从而避免再一次赋值操作。

1

2

3

4

5

6

7

8

9

10

11def print_log(func):

def wrapper(*args, **kwargs):

print("%s is running" % func.__name__)

return func(*args, **kwargs)

return wrapper

def ():

print('call foo ...')

foo()

这样就可以省略前面的foo = print_log(foo),使用@print_log替代。如果还有其他的函数,我们可以继续使用这个装饰器,这样就增加了程序的可读性。

带参数的装饰器

装饰器还可以带参数,比如@decorator(a)。这样就可以提供更大的灵活性。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16def print_log(level):

def decorator(func):

def wrapper(*args, **kwargs):

if level == "warn":

print("%s is running" % func.__name__)

return func(*args, **kwargs)

return wrapper

return decorator

@print_log(level="warn")

def ():

print('call foo ...')

foo()

这相当于将原来的装饰器进行一层封装,并返回装饰器decorator,当我们使用@print_log(level="warn")调用的时候,将参数level传递到装饰器当中。

在执行到foo = print_log(level="warn")(foo)时,执行foo.__name__发现该函数变成wrapper,并不是foo。这是因为经过decorator装饰后的函数,返回值已经变成wrapper函数,并不是foo函数。Python内置的functools.wraps能将原函数的信息暂时保存起来。这样使得装饰器函数也有和原函数一样的信息。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18import functools

def print_log(level):

def decorator(func):

@functools.wraps(func)

def wrapper(*args, **kwargs):

if level == "warn":

print("%s is running" % func.__name__)

return func(*args, **kwargs)

return wrapper

return decorator

@print_log(level="warn")

def foo():

print('call foo ...')

foo()

内置装饰器

Python中内置很多有用的装饰器: @[email protected]@property

@property

@property装饰器就是将一个方法变成属性调用,把一个getter方法变成属性,只需要加上@property。此时,@property又创建了另外一个装饰器@score.setter,将一个setter方法变成属性赋值,这样就可以对将方法转换成属性来操作。这样就可以对参数进行检查。举个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22class Student(object):

@property

def score(self):

return self._score

@score.setter

def score(self, value):

if not isinstance(value, int):

raise ValueError('score must be an integer!')

if value < 0 or value > 100:

raise ValueError('score must between 0 ~ 100')

self._score = value

# 也可以添加只读的属性

@property

def age(self):

return 18

s = Student()

s.score = 60 # 相当于调用 s.set_score(60)

s.score # 相当于调用 s.get_score()

s.score = 1000 # ValueError: score must between 0 ~ 100!

装饰器的顺序:

1

2

3

4@a

@b

@c

def f ():

等效于:f = a(b(c(f)))

类装饰器

类装饰器比函数装饰器较灵活,

1

2

3

4

5

6

7

8

9

10

11

12

13

14class Foo(object):

def __init__(self, func):

self._func = func

def __call__(self):

print ('class decorator runing')

self._func()

print ('class decorator ending')

@Foo

def bar():

print ('bar')

bar()

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值