1 引言

装饰器(Decorators)可能是Python中最难掌握的概念之一了,也是最具Pythonic特色的技巧,深入理解并应用装饰器,你会更加感慨——人生苦短,我用Python。

2 初步理解装饰器

2.1 什么是装饰器

在解释什么是装饰器之前,我们有必要回顾一下Python中的一些思想和概念。我们都知道,Python是一门面向对象的语言,Python基本思想就是一些皆对象,数据类型是对象、类是对象、类实例也是对象……对于接下来我们要说的装饰器而言,最重要的是,函数也是对象!

你没看错,函数也和数据类型等概念一样,都是对象,那么既然数据类型可以进行赋值操作,那么函数是不是也可以赋值呢?当然可以!

  1. def do_something():
  2. print('完成一些功能')
  3.  
  4. if __name__ == '__main__':
  5. do = do_something
  6. do()

输出:

完成一些功能

看,原本我们定义的函数名是do_something,但我们把函数赋值给do后,也可以通过do()调用函数。不仅如此,函数当做参数传递给其他函数:

  1. def do_something():
  2. print('正在完成功能')
  3.  
  4. def func(f):
  5. f()
  6.  
  7. if __name__ == '__main__':
  8. func(do_something)

输出:

完成一些功能

正是因为Python中函数既可以赋值给其他变量名,也可以当做参数参数进其他函数,所以,上面的代码没有任何问题。

当然,毕竟被称呼为函数,有别有变量之类的概念,所以它也有自己的特性,例如在函数内部,还可以定义函数,甚至作为返回值返回:

  1. def func():
  2. def inner_func():
  3. print('我是內部函数')
  4. return inner_func
  5.  
  6. if __name__ == '__main__':
  7. fun1 = func()
  8. fun1()

输出结果:

我是內部函数

我们来总结一下函数的这几个特性:

  • 可以赋值给其他变量;

  • 可以作为参数传递给其他函数;

  • 可以在内部定义一个函数;

  • 可以当做返回值返回。

不要疑惑我为什么要着重说明Python中函数的这几个特性,因为装饰器中正是这几个特性为基石。为什么这么说呢?从本质上来说,装饰器就是一个函数,它也具有我们上面说到的4个特性,而且充分利用了这4个特性。装饰器接受一个普通函数作为参数,并在内部定义了一个函数,在这个内部函数中实现了一些功能,并调用了传递进来的函数,最后将内部函数作为返回值返回。如果一个函数把这个步骤全走了一遍,我们就可以认为,这个函数是一个装饰器函数,或者说装饰器。

我们来动手写一个装饰器:

  1. def func(f):
  2. def inner_func():
  3. print('{}函数开始运行……'.format(f.__name__))
  4. f()
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return inner_func
  7.  
  8. def do_something():
  9. print('正在完成功能')
  10.  
  11. if __name__ == '__main__':
  12. do_something = func(do_something)
  13. do_something()

输出结果:

do_something函数开始运行……

正在完成功能

do_something函数结束运行……

在上面代码中,我们将do_something方法作为参数传递给func方法,在func方法内部我们定义了一个inner_func方法,并在这个方法中添加了函数开始执行和结束执行的提示,在func方法最后,我们将inner_func作为参数返回,更加值得一说的是,我们重新将func函数的返回值赋给了do_something,所以,最后一行我们再次调用的do_something方法已经不再是最初的do_something函数,而是func方法内定义的inner_func函数,所以最后执行do_something函数时,会有函数开始执行和结束执行的提示功能,而后面再调用do_something函数时,也都直接使用do_something()。

正如你所预料,func函数就是一个装饰器,捋一捋你会发现,func函数把我们上面说过的所有特性、步骤全实现了。

如果你在别处见过Python装饰器的使用,你可能会疑惑,我们实现的func装饰器跟你见过的装饰器不都一样,因为在实际应用中,装饰器大多是与“@”符号结合起来使用。其实“@”符号所实现的功能就是 do_something = func(do_something)这行代码的功能。来,我们尝试一下使用“@”:

  1. def func(f):
  2. def inner_func():
  3. print('{}函数开始运行……'.format(f.__name__))
  4. f()
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return inner_func
  7.  
  8. @func
  9. def do_something():
  10. print('正在完成功能')
  11.  
  12. if __name__ == '__main__':
  13. do_something()

输出结果:

do_something函数开始运行……

正在完成功能

do_something函数结束运行……

之前我们知道,func函数就是一个装饰器,所以使用“@”符号时,我们只需要在被装饰的函数前面加上“@func”就表示该函数被func装饰器装饰,在需要处直接调用do_something函数即可。

2.2 为什么要用装饰器

在上面代码中,我们写了一个装饰器func,在这个装饰器中,使用装饰器的好处就已经初见端倪了。

(1)可以在不对被装饰函数做任何修改的前提下,给被装饰函数附加上一些功能。使用@func对do_something函数进行装饰时,我们没有对do_something函数的代码做什么的改变,但是被装饰后的do_something函数却多了开始运行和结束运行的功能。

(2)不改变被装饰函数的调用方式。在被装饰前,我们通过do_something()调用这个函数,被装饰后,还是通过do_something()调用这个函数。

(3)代码更加精简。在上面代码中,我们只是用@func装饰了do_something一个函数,但是如果有多个函数需要添加开始运行和结束运行的提示功能,如果不用装饰器,那么就需要对每一个函数进行修改,则工作量和需要修改的代码量……用了装饰器之后,只需要在需要添加这一功能的函数前面添加@func就可以了。

一言以盖之,装饰器可以在不改变原函数调用方式和代码情况下,为函数添加一些功能,使代码更加精简。

我们在写一个装饰器来加深一下理解。相比大家都写过代码来统计一个函数的运行时间的功能,我们使用装饰器来实现一下这个功能:

  1. import time
  2. def timmer(f):
  3. def inner_func():
  4. start_time = time.time()
  5. f()
  6. end_time = time.time()
  7. print('{}函数运行消耗时间为:{}'.format(f.__name__, end_time-start_time))
  8. return inner_func
  9.  
  10. @timmer
  11. def do_something():
  12. print('do_something函数运行……')
  13. time.sleep(1)
  14.  
  15. if __name__ == '__main__':
  16. do_something()

输出结果:

do_something函数运行……

do_something函数运行消耗时间为:1.000662088394165

在上面例子中,我们首先定义了一个计时装饰器timmer,当需要统计某个函数运行时间时,只需要在函数定义时,在前面添加一行写上@timmer即可,例如上面对do_something函数运行时间进行统计,对do_something原来要实现什么功能就继续实现这一功能,原来代码该怎样还怎样,该怎么调用还怎么调用。所以说,使用装饰器可以在不改变原函数代码和调用方式的情况下附加上其他功能。

如果你阅读到了这里,我想你对装饰器已经有了初步的理解。接下来,我们继续聊一聊更加复杂的装饰器。

3 深入理解装饰器

3.1 被装饰的函数带返回值

我们上面写的两个装饰器所装饰的do_something函数是没有返回值的,但大多数函数可都是有返回值的。针对有返回值的函数,装饰器该怎么写呢?

  1. def func(f):
  2. def inner_func():
  3. print('{}函数开始运行……'.format(f.__name__))
  4. ret = f()
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return ret # 这里返回值
  7. return inner_func
  8.  
  9. @func
  10. def do_something():
  11. print('正在完成功能')
  12. return '我是返回值'
  13.  
  14. if __name__ == '__main__':
  15. ret = do_something()
  16. print(ret)

输出结果:

do_something函数开始运行……

正在完成功能

do_something函数结束运行……

我是返回值

我们知道,被装饰后的do_something函数其实不再是最初的do_something函数,而是装饰器内部定义的inner_func函数,所以,被装饰的函数的返回值只需要通过装饰器内部定义的inner_func函数返回返回即可即可。有点绕,不过对着上面的代码应该好理解。

3.2 被装饰函数带参数

对于装饰器,我们要深刻理解一件事:以上面的装饰器func和被装饰函数do_something为例,被装饰后的do_something函数已经不再是原来的do_something函数,而是装饰器内部的inner_func函数。这句话我已经在上文中我已经不止提过一次,因为真的很重要。如果被装饰的函数有参数(加入参数为name),我们还是通过do_something(name)的方式传递传输,不过,既然我们最终调用的时候,通过do_something实质调用的inner_func函数,那么在定义装饰器是,定义的inner_func函数时也需要接受参数。

  1. def func(f):
  2. def inner_func(name):
  3. print('{}函数开始运行……'.format(f.__name__))
  4. ret = f(name)
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return ret
  7. return inner_func
  8.  
  9. @func
  10. def do_something(name):
  11. print('你好,{}!'.format(name))
  12. return '我是返回值'
  13.  
  14. if __name__ == '__main__':
  15. ret = do_something('姚明')
  16. print(ret)

输出结果:

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

我是返回值

一个装饰器可用于装饰千千万万个函数,则千千万万个函数参数情况可能各不相同,有的没有参数,有的可能多个参数,甚至还有关键字参数,对于这参数情况不同的函数,我们不可能为每个函数都写一个func装饰器,那怎么办呢?

Python中提供了*args, **kwargs这种机制来接受任意位置的位置参数和关键字参数,参数前面带*表示接受任意个数位置参数,接收到的所有位置参数存储在变量名为args的元组中,带**表示接受任意个数的关键字参数,接收到的所有关键字参数以字典的形式参数在变量名为kwargs的字典中。

当我们知道只有位置参数,但不知道有多少个位置参数是func装饰器可以这么写:

  1. def func(f):
  2. def inner_func(*name):
  3. print('{}函数开始运行……'.format(f.__name__))
  4. ret = f(*name)
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return ret
  7. return inner_func
  8.  
  9. @func
  10. def do_something(name):
  11. print('你好,{}!'.format(name))
  12.  
  13. @func
  14. def do_something_2(name_1, name_2):
  15. print('你好,{}!'.format(name_1))
  16. print('你好,{}!'.format(name_2))
  17.  
  18. @func
  19. def do_something_3(*name):
  20. for n in name:
  21. print('你好,{}!'.format(n))
  22.  
  23. if __name__ == '__main__':
  24. do_something('姚明')
  25. print('-------------------------------')
  26. do_something_2('姚大明', '姚小明')
  27. print('-------------------------------')
  28. do_something_3('姚一明', '姚二明', '姚三明', '姚四明')

输出结果:

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

-------------------------------

do_something_2函数开始运行……

你好,姚大明!

你好,姚小明!

do_something_2函数结束运行……

-------------------------------

do_something_3函数开始运行……

你好,姚一明!

你好,姚二明!

你好,姚三明!

你好,姚四明!

do_something_3函数结束运行……

上面例子定义func装饰器时,我们用*name来接受任意个数的位置参数,可别以为只能用*args,args只是一个变量名,只不过约定俗成,用的多一些,实际开发时你爱取啥名就用啥名,对于这个知识点不再多说,毕竟本篇主角是装饰器。我们继续装饰器内容!

当我们知道只有关键字参数,却不知道参数个数时,可以func装饰器这么写:

  1. def func(f):
  2. def inner_func(**name):
  3. print('{}函数开始运行……'.format(f.__name__))
  4. ret = f(**name)
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return ret
  7. return inner_func
  8.  
  9. @func
  10. def do_something(name='无名氏'):
  11. print('你好,{}!'.format(name))
  12.  
  13. @func
  14. def do_something_2(name_1='无名氏', name_2='无名氏'):
  15. print('你好,{}!'.format(name_1))
  16. print('你好,{}!'.format(name_2))
  17.  
  18. @func
  19. def do_something_3(**name):
  20. for n in name.keys():
  21. print('你好,{}!'.format(name[n]))
  22.  
  23. if __name__ == '__main__':
  24. do_something(name='姚明')
  25. print('-------------------------------')
  26. do_something_2(name_1='姚大明', name_2='姚小明')
  27. print('-------------------------------')
  28. do_something_3(name_1='姚一明', name_2='姚二明', name_3='姚三明', name_4='姚四明')

输出结果:

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

-------------------------------

do_something_2函数开始运行……

你好,姚大明!

你好,姚小明!

do_something_2函数结束运行……

-------------------------------

do_something_3函数开始运行……

你好,姚一明!

你好,姚二明!

你好,姚三明!

你好,姚四明!

do_something_3函数结束运行……

事实上,大多数情况下,我们对被装饰函数是一无所知的——我们不知道有多少个位置参数、多少个关键字参数,甚至对有没有位置参数、关键字参数都不知道,这时候,我们就只能*args和**kwargs齐上阵了:

  1. def func(f):
  2. def inner_func(*name1, **name2):
  3. print('{}函数开始运行……'.format(f.__name__))
  4. ret = f(*name1, **name2)
  5. print('{}函数结束运行……'.format(f.__name__))
  6. return ret
  7. return inner_func
  8.  
  9. @func
  10. def do_something(name):
  11. print('你好,{}!'.format(name))
  12.  
  13. @func
  14. def do_something_2(name_1, name_2='无名氏'):
  15. print('你好,{}!'.format(name_1))
  16. print('你好,{}!'.format(name_2))
  17.  
  18. @func
  19. def do_something_3(*name1, **name2):
  20. for n in name1:
  21. print('你好,{}!'.format(n))
  22. for n in name2.keys():
  23. print('你好,{}!'.format(name2[n]))
  24.  
  25. if __name__ == '__main__':
  26. do_something(name='姚明')
  27. print('-------------------------------')
  28. do_something_2(name_1='姚大明', name_2='姚小明')
  29. print('-------------------------------')
  30. do_something_3('姚一明', '姚二明', '姚三明', name_4='姚四明')

输出结果:

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

-------------------------------

do_something_2函数开始运行……

你好,姚大明!

你好,姚小明!

do_something_2函数结束运行……

-------------------------------

do_something_3函数开始运行……

你好,姚一明!

你好,姚二明!

你好,姚三明!

你好,姚四明!

do_something_3函数结束运行……

3.3 装饰器本身带参数

我们上面写的装饰器都没有参数,或者说只有一个自带参数,也就是被装饰函数f。其实,装饰器也是可以有其他参数的,这样的装饰器更加灵活。我们通过实例来说明:现在我们要对上面的func装饰器进行改进,需要做到灵活控制装饰器是用中文输出还是用英文输出,代码如下。

  1. def language(lang='中文'): # 这里带参数
  2. def func(f): # 往里嵌套了一层
  3. def inner_func(*name1, **name2):
  4. if lang=='中文':
  5. print('{}函数开始运行……'.format(f.__name__))
  6. else:
  7. print('The function of {} starts runging…'.format(f.__name__))
  8. ret = f(*name1, **name2)
  9. if lang=='中文':
  10. print('{}函数结束运行……'.format(f.__name__))
  11. else:
  12. print('The function of {} ends runging…'.format(f.__name__))
  13. return ret
  14. return inner_func
  15. return func
  16.  
  17. @language('中文')
  18. def do_something(name):
  19. print('你好,{}!'.format(name))
  20.  
  21. @language('English')
  22. def do_something_2(name):
  23. print('你好,{}!'.format(name))
  24.  
  25. if __name__ == '__main__':
  26. do_something(name='姚明')
  27. print('-------------------------')
  28. do_something_2(name='姚明')

输出如下:

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

-------------------------

The function of do_something_2 starts runging…

你好,姚明!

The function of do_something_2 ends runging…

可以看到,通过装饰器带参数的方式,我们只需要在定义被装饰函数时,指定装饰器参数,就可以灵活控制每个被装饰函数提示的语言。

当然,必须承认,装饰器带参数后,看起来更加复杂,需要多嵌套一层函数,由最外层的函数接受参数,里层函数才是真正的装饰器。使用装饰器时,会首先运行带参数的最外层函数,返回装饰器,这一步Python会自动帮我们完成。所以,带参数的装饰器甚至可以这么使用:

  1. h = language('中文')
  2. @h
  3. def do_something(name):
  4. print('你好,{}!'.format(name))

3.4 多层装饰器

装饰器也是可以多层嵌套使用的,也就是说,一个函数可以通过是被多个装饰器所装饰,执行顺序是从下到上的优先顺序加载装饰:

  1. # -*- coding: utf-8 -*-
  2. import time
  3.  
  4. print(1)
  5. def func(f):
  6. print(2)
  7. def inner_func(*name1, **name2):
  8. print('{}函数开始运行……'.format(f.__name__))
  9. f(*name1, **name2)
  10. print('{}函数结束运行……'.format(f.__name__))
  11. print(3)
  12. return inner_func
  13.  
  14. print(4)
  15. def timmer(f):
  16. print(5)
  17. def inner_timmer(*args, **kwargs):
  18. print('开始计时……')
  19. start_time = time.time()
  20. f(*args, **kwargs)
  21. end_time = time.time()
  22. print('开始结束……')
  23. time_cost = end_time - start_time
  24. print('{}函数运行时长为:{}秒'.format(f.__name__, time_cost))
  25. print(6)
  26. return inner_timmer
  27.  
  28. print(7)
  29. @func
  30. @timmer
  31. def do_something(name):
  32. time.sleep(1)
  33. print('你好,{}!'.format(name))
  34.  
  35. print(8)
  36. def do_something_2(name):
  37. time.sleep(1)
  38. print('你好,{}!'.format(name))
  39.  
  40. if __name__ == '__main__':
  41. print(9)
  42. do_something(name='姚明')
  43. print('-------------------------')
  44. func(timmer(do_something_2))(name='姚明') # 执行效果与上面使用了@符号的do_something一样

输出结果:

1

4

7

5

6

2

3

8

9

inner_timmer函数开始运行……

开始计时……

你好,姚明!

开始结束……

do_something函数运行时长为:1.0004358291625977秒

inner_timmer函数结束运行……

-------------------------

5

6

2

3

inner_timmer函数开始运行……

开始计时……

你好,姚明!

开始结束……

do_something_2函数运行时长为:1.000028133392334秒

inner_timmer函数结束运行……

在上面代码中,我们同时用func和timmer两个装饰器来装饰do_something,从运行结果中可以看出,两个装饰器都发挥了作用。同时,为了方便大家理解,我们使用不带@符号的来使用两个装饰器装饰,两者运行结果是一样的,结合代码中的输出标记,我们来分析一下装饰器的执行过程:开始运行后,1->4->7这几个过程我相信大家都是可以理解的,到了位置7后,遇到了@符号标识的装饰器,而且是多层的,两个@装饰器相当于func(timmer(do_something_2)),所以是先执行timmer函数获取返回值作为参数传递给func,所以有了7之后是5->6,timmer函数返回值是inner_timmer函数,这时候就相当于func(inner_timmer),所以程序退出timmer函数后进入func函数,就有了2->3,从func函数返回后,继续向下执行遇到位置8,然后就进入了主函数运行,所以是8->9,此时的函数是被装饰过的,本质已经是func函数返回的inner_func函数了,所以最终在主函数中执行do_something时执行的是inner_func方法,所以先输出了func装饰器的函数开始提示,然后才是timmer装饰器的计时开始提示。

嗯,有点复杂!

3.5 类装饰器

通过上面的介绍,我想你已经知道,@符号是装饰器的一个表示,或者说一个装饰器语法糖,当使用@时,例如@A,这种语法糖会自动将被装饰函数f作为参数传递给A函数,然后将A函数的返回值重新f给f这个变量名,这就是@语法糖帮我们做的事情,概括来说就是f=A(f)()。

我们现在发散一下思维,假设A如果是一个类会怎么样呢?我们知道当A是一个类时,A()表示调用A类的构造函数__init__实例化一个A类对象,那么A(f)就表示将函数f作为参数传递给A类的构造方法__init__来构造一个A类实例对象,如果使用了@符号,那么这种语法机制就还会在A(f)后面加一个括号变成A(f)(),这是什么鬼?执行一个类实例对象?不过话要说回来,如果A(f)()这种结构要是没有问题,能够成功执行,是不是就意味着Python中类也可以成为装饰器了呢?确实如此。我们先看看通过A()()执行一个类实例对象会怎么样:

  1. class A(object):
  2. def __init__(self):
  3. print('实例化一个A类对象')
  4. def __call__(self, *args, **kwargs):
  5. print('__call__方法被调用……')
  6.  
  7. if __name__ == '__main__':
  8. A()()

输出结果:

实例化一个A类对象

__call__方法被调用……

看到没,通过A()()执行一个类实例对象时,执行的是A类内部的__call__方法。那么如果用A用作装饰器时,@A返回的就是A类内部定义的__call__方法,相当于函数装饰器func内的inner_func。来,我们感受一下类装饰器:

  1. class A(object):
  2. def __init__(self, f):
  3. print('实例化一个A类对象……')
  4. self.f = f
  5. def __call__(self, *args, **kwargs):
  6. print('{}函数开始运行……'.format(self.f.__name__))
  7. self.f(*args, **kwargs)
  8. print('{}函数结束运行……'.format(self.f.__name__))
  9.  
  10. @A
  11. def do_something(name):
  12. print('你好,{}!'.format(name))
  13. if __name__ == '__main__':
  14. do_something('姚明')

输出结果:

实例化一个A类对象……

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

如果类装饰器带参数呢?这时候,类装饰器的参数也可定是通过@A(t)的形式传递,这时候,因为@语法糖会自动加括号的原因,结构就编程这样A(t)(),A(t)是类实例对象,A(t)()就是__call__方法,所以,@语法糖会把被装饰函数f作为参数传递给__call__方法,被装饰函数的参数需要在__call__内部定义一个函数来接受。也就是话说,定义类装饰器时,装饰器的参数通过__init__构造方法接收,被装饰函数的作为参数被__call__方法接收,。(这段子很绕,有点烧脑,没点儿Python基础还真不好理解,语言表达能力优先感觉要枯竭了)

  1. import time
  2. class A(object):
  3. def __init__(self, t):
  4. print('实例化一个A类对象……')
  5. self.t = t
  6. def __call__(self, f):
  7. def inner_A(*args, **kwargs):
  8. print('延迟{}秒后开始执行……'.format(self.t))
  9. time.sleep(self.t)
  10. print('{}函数开始运行……'.format(f.__name__))
  11. f(*args, **kwargs)
  12. print('{}函数结束运行……'.format(f.__name__))
  13. return inner_A
  14.  
  15. @A(1)
  16. def do_something(name):
  17. print('你好,{}!'.format(name))
  18.  
  19. if __name__ == '__main__':
  20. do_something('姚明')

输出结果:

实例化一个A类对象……

延迟1秒后开始执行……

do_something函数开始运行……

你好,姚明!

do_something函数结束运行……

无论是函数装饰器还是类装饰器,原理上是一样的,区别在于如果A是函数,A()是直接调用函数,而A是类时,A()是实例化,通过A()()是调用A类的__call__方法。

4 Python中内置的装饰器

4.1 @property,@setter,@deleter

@property,@setter,@deleter这三个装饰器提供了更加友好的方式来获取、设置或删除类中的属性。@property装饰器所装饰的函数可以像访问属性一样调用函数,注意,@property装饰器必须先于@setter,@deleter使用,且三者说装饰的函数必须同名。

  1. class A(object):
  2. def __init__(self, v):
  3. print('实例化一个A类对象……')
  4. self.__value = v
  5.  
  6. @property
  7. def value(self):
  8. print('取值时被调用')
  9. return self.__value
  10.  
  11. @value.setter
  12. def value(self, value):
  13. print('赋值时被调用')
  14. self.__value = value
  15.  
  16. @value.deleter
  17. def value(self):
  18. print('删除值时被调用……')
  19. del self.__value
  20.  
  21. if __name__ == '__main__':
  22. a = A(123)
  23. print('-------------')
  24. print('__value的值为:{}'.format(a.value))
  25. print('-------------')
  26. a.value = 234
  27. print('__value的值为:{}'.format(a.value))
  28. print('--------------')
  29. del a.value
  30. print('__value的值为:{}'.format(a.value))

输出为:

Traceback (most recent call last):

实例化一个A类对象……

-------------

取值时被调用

File "E:/WorkProjectCode/study_pymysql/study_secorators/test2.py", line 33, in <module>

__value的值为:123

-------------

print('__value的值为:{}'.format(a.value))

赋值时被调用

取值时被调用

__value的值为:234

File "E:/WorkProjectCode/study_pymysql/study_secorators/test2.py", line 11, in value

--------------

return self.__value

删除值时被调用……

取值时被调用

AttributeError: 'A' object has no attribute '_A__value'

运行产生异常,因为最后访问了已经删除了的元素。

4.2 @classmethod

在一个类中,如果一个方法被@classmethod所装饰,就代表该方法与类绑定,而不是与实例对象绑定,第一个参数cls由Python机制自动传递,表示类本身。

  1. class A(object):
  2. @classmethod
  3. def f(cls):
  4. print('当前类名为:{}'.format(cls.__name__))
  5.  
  6. if __name__ == '__main__':
  7. A.f()

输出结果:

当前类名为:A

4.3 @staticmethod

被@staticmethod所装饰的方法为静态方法,静态方法一般使用场景就是和类相关的操作,但是又不会依赖和改变类、实例的状态,比如一些工具方法。

  1. import time
  2. class A(object):
  3. @staticmethod
  4. def f():
  5. time_now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
  6. return time_now
  7.  
  8. if __name__ == '__main__':
  9. print(A.f())
  10. print(A().f())

输出:

2019-08-16 19:29:32

2019-08-16 19:29:32

Python装饰器完全解读的更多相关文章

  1. 关于python装饰器

    关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...

  2. python装饰器通俗易懂的解释!

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...

  3. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  4. python 装饰器修改调整函数参数

    简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...

  5. python 装饰器学习(decorator)

    最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...

  6. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  7. 关于python装饰器(Decorators)最底层理解的一句话

    一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...

  8. Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...

  9. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

随机推荐

  1. JavaScript权威指南第六版(阅读笔记)

    前言: 对于软件行业学习是无止境的,因为知识更替非常快,能够快速稳固掌握一门新技术是一个程序员应该具备的基本素质. 了解一门语言,了解它的概念非常重要,但是一些优秀的设计思想需要细心和大量实践才能慢慢 ...

  2. Java 新特性总结——简单实用

    lambda表达式 简介 lambda 表达式的语法 变量作用域 函数式接口 内置函数式接口 默认方法 Stream(流) 创建 stream Filter(过滤) Sorted(排序) Map(映射 ...

  3. 【译】.NET Core 3.0 发布自包含单体可执行程序

    .NET Core 提供的发布应用程序选项 self-contained 是共享应用程序的好方法,因为应用程序的发布目录包含所有组件.运行时和框架.您只需要告诉使用者应用程序的入口 exe 文件,就可 ...

  4. [原创]wireshark&xterm安装、配置和使用

    --wireshark && xterm--一.安装wireshark: #apt-get install wireshark二.启动wireshark: #wireshark 或者 ...

  5. 深入了解数据校验:Bean Validation 2.0(JSR380)

    每篇一句 > 吾皇一日不退役,尔等都是臣子 对Spring感兴趣可扫码加入wx群:`Java高工.架构师3群`(文末有二维码) 前言 前几篇文章在讲Spring的数据绑定的时候,多次提到过数据校 ...

  6. [leetcode] 5. Longest Palindromic Substring (Medium)

    原题链接 找到并返回最长回路子串 思路: 解法一: 最简单的双重遍历,判断s[i]到s[j]是不是回串. Runtime: 610 ms, faster than 6.39% of Java 慢的不行 ...

  7. python课堂整理5---元组

    一.元组   Tuple tu = (111, 22, 33, "alex", (11,22), [(33, 44)], True, ) 元组元素不可被修改,不能被增加或删除 一般 ...

  8. Anacodna之conda的使用

    yum install -y bunzip2 wget https://repo.continuum.io/archive/Anaconda2-5.0.1-Linux-x86_64.sh chmod ...

  9. mysql整数类型int后面的长度有什么意义

    int 的 SQL-92 同义字为 integer.SQL-92 是数据库的一个标准. int类型的存储大小为4个字节 unsigned(无符号) int 存储范围是 2^4*8 int(M) 中的M ...

  10. 用ECharts绘制Prometheus图表,实现类似Grafana的自定义Dashboard

      大家一般都是用Grafana自定义Dashboard来监控Prometheus数据的,作者这次尝试用ECharts来绘制Prometheus数据图表,一方面可以减少依赖,另一方面可以将监控界面灵活 ...