装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼。

一、 函数名应用

函数名是什么?函数名是函数的名字,本质:变量,特殊的变量。

(1)函数名就是函数的内存地址,直接打印函数名,就是打印内存地址

  1. def func1():
  2. print(123)
  3. print(func1)         # <function func1 at 0x0000029042E02E18>

(2)函数名可以作为变量

  1. def func1():
  2. print(111)
  3.  
  4. f = func1
  5. f()           # f() 就是func1()

(3)函数名可以作为函数的参数

  1. def func1():
  2. print(111)
  3.  
  4. def func2(x):
  5. x()
  6.  
  7. func2(func1)         #func1作为func2的参数

(4)函数名可以作为函数的返回值

  1. def wrapper():
  2. def inner():
  3. print('inner')
  4. return inner
  5. f = wrapper()
  6. f()

(5)函数名可以作为容器类类型的元素

  1. 使用for循环批量执行函数
  2. def func1():
  3. print('func1')
  4. def func2():
  5. print('func2')
  6. def func3():
  7. print('func3')
  8.  
  9. l1 = [func1,func2,func3]
  10. for i in l1:
  11. i()

像上面函数名这种,叫做第一类对象。

第一类对象( first-class object)指:

  • 1.可在运行期创建
  • 2.可用作函数参数或返回值
  • 3.可存入变量的实体

*不明白?那就记住一句话,就当普通变量用

二、闭包

1、闭包函数内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数

2、闭包的作用:爬虫、装饰器

  当程序执行遇到函数执行时,会在内存空间开辟局部命名空间,当函数执行完毕,该命名空间会被销毁。但是如果这个函数内部形成闭包,则该内存空间不会随着函数执行完而消失。

3、如何判断是否是闭包:print(函数名.__closure__) 结果是cell说明是闭包,结果是None说明不是闭包。

闭包举例

  1. def wrapper():
  2. name = 'summer'
  3. def inner():
  4. print(name)
  5. inner()
  6.  
  7. wrapper() # summer

如何判断它是否是一个闭包函数呢? 内层函数名.__closure__  cell 就是=闭包

1.

  1. def wrapper():
  2. name = 'summer'
  3. def inner():
  4. print(name)
  5. inner()
  6. print(inner.__closure__)
  7.  
  8. wrapper()
  1. 执行输出:
  2. summer
  3. (<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

2.

  1. name = 'summer'
  2. def wrapper():
  3. def inner():
  4. print(name)
  5. inner()
  6. print(inner.__closure__)
  7.  
  8. wrapper()
  1. 结果输出:
  2. summer
  3. None

返回值为None 表示它不是闭包,因为name是一个全局变量,如果函数调用了外层变量而非全局变量,那么它就是闭包。

3.

  1. name = 'summer'
  2. def wrapper2():
  3. name1 = 'spring'
  4. def inner():
  5. print(name)
  6. print(name1)
  7. inner()
  8. print(inner.__closure__)
  9.  
  10. wrapper2()
  1. 结果输出:
  2. summer
  3. spring
  4. (<cell at 0x030B7310: str object at 0x03043680>,)

只要引用了外层变量至少一次,非全局的,它就是闭包

4:判断下面的函数,是一个闭包吗?******

  1. name = 'summer'
  2. def wraaper2(n):        #相当于n = 'summer'
  3.   def inner():
  4. print(n)
  5. inner()
  6. print(inner.__closure__)
  7.  
  8. wraaper2(name)
  1. 结果输出:
  2. summer
  3. (<cell at 0x03867350: str object at 0x037F3680>,)

它也是一个闭包. 虽然wraaper2传了一个全局变量,但是在函数wraaper2内部,inner引用了外层变量,相当于在函数inner外层定义了 n = 'summer',所以inner是一个闭包函数

闭包的好处当函数开始执行时,如果遇到了闭包,他有一个机制,他会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失。

举一个例子:爬3次,内存开了3次,很占用内存

  1. from urllib.request import urlopen
  2. content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
  3. content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
  4. content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')

把它封装成闭包

  1. from urllib.request import urlopen
  2.  
  3. def index():
  4. url = "https://www.cnblogs.com/"
  5. def get():
  6. return urlopen(url).read()
  7. return get        #return的是get,就是一个函数名
  8.  
  9. cnblog = index()
  10. print(cnblog) # <function index.<locals>.get at 0x02F46978>
  11. content = cnblog()
  12. print(content) # 页面源码

这个例子,只有第一遍,是从网站抓取的。之后的执行,直接从内存中加载,节省内存空间

三、装饰器

1 装饰器初识

装饰器本质就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

  1. import time
  2. def timmer(f):       
  3. def inner():
  4. start_time = time.time()
  5. f()     
  6. end_time = time.time()
  7. print('此函数的执行时间为{}'.format(end_time - start_time))
  8. return inner   
  9.  
  10. def func1():   
  11. print('in func1')       
  12. time.sleep(1)       
  13.  
  14. func1 = timmer(func1)       
  15. print(func1)
  16. func1()           # 这里的func1是全新的func1,就是上面的赋值,此时相当于执行 inner函数
  1. 输出结果:
  2. <function timmer.<locals>.inner at 0x03822DF8>
  3. in func1
  4. 此函数的执行时间为1.0003533363342285

代码从上至下执行

语法糖:想测试谁,前面加@装饰器函数,即可。写装饰器,约定俗成,函数名为wrapper

  1. def wrapper(func):
  2. def inner(*args,**kwargs):
  3. '''被装饰函数之前'''
  4. ret = func(*args,**kwargs)
  5. '''被装饰函数之后'''
  6. return ret
  7. return inner
  8.  
  9. @wrapper
  10. def func(*args,**kwargs):
  11. print(args,kwargs)
  12. return 666
  13.  
  14. print(func())
  1. 输出结果:
  2. () {}
  3. 666

装饰器利用return制造了一个假象,func()执行,其实是执行inner()func()把原来的func()给覆盖了

2. 装饰器传参

1:上面装饰器的例子,func1,要传2个参数a,b

  1. import time
  2. def timmer(f):
  3. def inner(a,b):
  4. start_time = time.time()
  5. f(a,b)
  6. end_time = time.time()
  7. print('此函数的执行时间为{}'.format(end_time - start_time))
  8. return inner
  9.  
  10. @timmer
  11. def func1(a,b):
  12. print('in func1 {}{}'.format(a,b))
  13. time.sleep(1) # 模拟程序逻辑
  14.  
  15. func1(1,2)
  1. 执行输出:
  2. in func1 12
  3. 此函数的执行时间为1.0006024837493896

2:如果有多个参数呢?改成动态参数

  1. import time
  2. def timmer(f):
  3. def inner(*args,**kwargs):
  4. start_time = time.time()
  5. f(*args,**kwargs)
  6. end_time = time.time()
  7. print('此函数的执行时间为{}'.format(end_time - start_time))
  8. return inner
  9.  
  10. @timmer
  11. def func1(*args,**kwargs):
  12. print('in func1 {}{}'.format(args,kwargs))
  13. time.sleep(1) # 模拟程序逻辑
  14.  
  15. func1(1,2,a='',b=4)
  1. 执行输出:
  2. in func1 (1, 2){'b': 4, 'a': '3'}
  3. 此函数的执行时间为1.000101089477539

函数的执行时,*打散

函数的定义时,*聚合。

  1. from functools import wraps
  2. def wrapper(f): # f = func1
  3. def inner(*args,**kwargs):       #聚合,args (1,2,3)
  4. '''执行函数之前的相关操作'''
  5. ret = f(*args,**kwargs)      # 打散 1,2,3
  6. '''执行函数之后的相关操作'''
  7. return ret
  8. return inner
  9.  
  10. @wrapper # func1 = wrapper(func1) func1 = inner
  11. def func1(*args):       #args (1,2,3) 聚合
  12. print(666)
  13. return args
  14.  
  15. print(func1(*[1,2,3]))
  1. 执行输出:
  2. 666
  3. (1, 2, 3)

3*****

  1. import time #1.加载模块
  2.  
  3. def timmer(*args,**kwargs): #2.加载变量 5.接收参数True,2,3
  4.  
  5. def wrapper(f): #6.加载变量 8.f = func1
  6.  
  7. print(args, kwargs) #9.接收timmer函数的值True,2,3
  8.  
  9. def inner(*args,**kwargs): #10.加载变量. 13.执行函数inner
  10. if flag: #14 flag = True
  11. start_time = time.time() #15 获取当前时间
  12. ret = f(*args,**kwargs) #16 执行func1
  13. time.sleep(0.3) #19 等待0.3秒
  14. end_time = time.time() #20 获取当前时间
  15. print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值
  16. else:
  17. ret = f(*args, **kwargs)
  18.  
  19. return ret #22 返回给函数调用者func1()
  20. return inner #11 返回给函数调用者wrapper
  21. return wrapper #7.返回给函数调用timmer(flag,2,3)
  22.  
  23. flag = True #3 加载变量
  24. @timmer(flag,2,3) # 4.执行函数timmer(flag,2,3) 17.执行函数func1 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
  25. def func1(*args,**kwargs):
  26. return 666 #18 返回给函数调用者f(*args,**kwargs)
  27.  
  28. print(func1()) #12 执行函数

写装饰器,一般嵌套3层就可以了

3.多个装饰器,装饰一个函数

  1. def wrapper1(func): # func == f函数名
  2. def inner1():
  3. print('wrapper1 ,before func') #
  4. func()
  5. print('wrapper1 ,after func') #
  6. return inner1
  7.  
  8. def wrapper2(func): # func == inner1
  9. def inner2():
  10. print('wrapper2 ,before func') #
  11. func()
  12. print('wrapper2 ,after func') #
  13. return inner2
  14.  
  15. @wrapper2 # f = wrapper2(f) 里面的f==inner1 外面的f == inner2
  16. @wrapper1 # f = wrapper1(f) 里面的f==函数名f 外面的f == inner1
  17.  
  18. def f(): #
  19. print('in f')
  20.  
  21. f() # inner2()
  1. 执行输出:
  2. wrapper2 ,before func
  3. wrapper1 ,before func
  4. in f
  5. wrapper1 ,after func
  6. wrapper2 ,after func

哪个离函数近,哪个先计算最底下的先执行

执行顺序如下图:

多个装饰器,都是按照上图的顺序来的

4. 装饰器的__name____doc___

__name__:函数名

__doc___:函数的解释

普通函数

  1. def func1():
  2. """
  3. 此函数是完成登陆的功能,参数分别是...作用。
  4. return: 返回值是登陆成功与否(True,False)
  5. """
  6. print(666)
  7.  
  8. func1()
  9. print(func1.__name__) #获取函数名
  10. print(func1.__doc__) #获取函数名注释说明
  11.  
  12. 执行输出:
  13. 666
  14. func1
  15. 此函数是完成登陆的功能,参数分别是...作用。
  16. return: 返回值是登陆成功与否(TrueFalse

这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了

带装饰器的函数

  1. def wrapper(f): # f = func1
  2.  
  3. def inner(*args,**kwargs): #聚合, args (1,2,3)
  4. '''执行函数之前的相关操作'''
  5. ret = f(*args,**kwargs) # 打散 1,2,3
  6. '''执行函数之后的相关操作'''
  7. return ret
  8. return inner
  9.  
  10. @wrapper
  11. def func1():
  12. """
  13. 此函数是完成登陆的功能,参数分别是...作用。
  14. return: 返回值是登陆成功与否(True,False)
  15. """
  16. print(666)
  17. return True
  18.  
  19. func1()
  20. print(func1.__name__)
  21. print(func1.__doc__)
  1. 执行输出:
  2. 666
  3. inner
  4. 执行函数之前的相关操作

函数装饰之后,相当于执行了inner函数,所以输出inner

为了解决这个问题,需要调用一个模块wraps

wraps将 被修饰的函数(wrapped) 的一些属性值赋值给修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉

  1. from functools import wraps
  2.  
  3. def wrapper(f): # f = func1
  4. @wraps(f) #f是被装饰的函数
  5. def inner(*args,**kwargs): #聚合args (1,2,3)
  6. '''执行函数之前的相关操作'''
  7. ret = f(*args,**kwargs) # 打散 1,2,3
  8. '''执行函数之后的相关操作'''
  9. return ret
  10. return inner
  11.  
  12. @wrapper
  13. def func1():
  14. """
  15. 此函数是完成登陆的功能,参数分别是...作用。
  16. return: 返回值是登陆成功与否(True,False)
  17. """
  18. print(666)
  19. return True
  20.  
  21. func1()
  22. print(func1.__name__)
  23. print(func1.__doc__)
  1. 执行输出:
  2. 666
  3. func1
  4. 此函数是完成登陆的功能,参数分别是...作用。
  5. return: 返回值是登陆成功与否(TrueFalse
  6.  
  7. 千心万苦写了的博客,如果您觉得本篇文章有帮助到您,请麻烦右下角点赞呦!算是给我的鼓励!我会继续加油, 争取给大家分享到有用的知识!相互学习,一起进步!谢谢啦~

for循环用腻了,试试列表生成式。的更多相关文章

  1. Python高级特性(切片,迭代,列表生成式,生成器,迭代器)

    掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...

  2. Python:Base3(函数,切片,迭代,列表生成式)

    1.Python之调用函数: Python内置了很多有用的函数,我们可以直接调用. 要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数 abs,它接收一个参数. 可以直接从Python的官方 ...

  3. python基础——列表生成式

    python基础——列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 举个例子,要生成list [1, 2, 3, 4 ...

  4. Python学习笔记6(列表生成式)

    1.生成列表 要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],我们可以用range(1, 11): >>> range(1, 11) [1, 2, 3 ...

  5. Python 列表生成式、生成器、迭代器

    列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么 ...

  6. python基础:列表生成式和生成器

    列表生成式(List Comprehension) 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 举个例子,要生成 list ...

  7. python之列表生成式

    列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 1,比如:要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, ...

  8. Python自学笔记-列表生成式(来自廖雪峰的官网Python3)

    感觉廖雪峰的官网http://www.liaoxuefeng.com/里面的教程不错,所以学习一下,把需要复习的摘抄一下. 以下内容主要为了自己复习用,详细内容请登录廖雪峰的官网查看. 列表生成式 列 ...

  9. Python编程核心内容之二——切片、迭代和列表生成式

    Python版本:3.6.2  操作系统:Windows  作者:SmallWZQ 最近太忙啦.很多事情需要自己处理,感觉时间不够用啊~~~~今后,博客更新时间可能会慢下来,哈哈,正所谓"人 ...

随机推荐

  1. lightoj 1140 - How Many Zeroes?(数位dp)

    Jimmy writes down the decimal representations of all natural numbers between and including m and n, ...

  2. lightoj 1248-G - Dice (III) (概率dp)

    题意:给你n个面的骰子,问扔出所有面的期望次数. 虽然这题挺简单的但还是要提一下.这题题目给出了解法. E(m)表示得到m个不同面的期望次数. E(m+1)=[((n-m)/n)*E(m)+1]+(m ...

  3. Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么?

    相信大家已经对 kafka 的基本概念已经有一定的了解了,下面直接来分析一下 ISR 和 AR 的概念. ISR and AR 简单来说,分区中的所有副本统称为 AR (Assigned Replic ...

  4. 《Hive编程指南》读书笔记 | 一文看懂Hive的数据类型和文件格式

    Hive支持关系型数据库中的大多数基本数据类型,同时也支持关系型数据库中很少出现的3种集合数据类型. 和大多数数据库相比,Hive具有一个独特的功能,那就是其对于数据在文件中的编码方式具有非常大的灵活 ...

  5. 【Offer】[17] 【打印1到最大的n位数】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入数字n,按顺序打印出从1最大的n位十进制数.比如输入3,则打印出1.2.3一直到最大的3位数即999. 思路分析 要考虑到大数问题, ...

  6. zabbix监控nginx脚本

    ~]# cd /etc/zabbix/scripts/ scripts]# ls nginx_status.sh scripts]# cat nginx_status.sh ############# ...

  7. spring cloud config使用mysql存储配置文件

    spring cloud config使用mysql存储配置文件 1.结构图 2.pom.xml: <?xml version="1.0" encoding="UT ...

  8. KafkaProducer源码分析

    Kafka常用术语 Broker:Kafka的服务端即Kafka实例,Kafka集群由一个或多个Broker组成,主要负责接收和处理客户端的请求 Topic:主题,Kafka承载消息的逻辑容器,每条发 ...

  9. Unity3D_05_理解Unity的新GUI系统(UGUI)

    理解Unity的新GUI系统(UGUI) Unity GUI 链接:UnityEngine.UI系统基础类架构图  Unity GUI 链接:UnityEngine Event & Event ...

  10. b146: NOIP2004 1.不高兴的津津

    题目: 津津上初中了.妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班.另外每周妈妈还会送她去学习朗诵.舞蹈和钢琴.但是津津如果一天上课超过八个小时就会不高兴,而且 ...