python之循序渐进学习装饰器

python装饰器的定义:在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式称之为装饰器(Decorator)

装饰器的优点和用途:

1. 抽离出大量函数中与函数功能本身无关的的雷同代码并继续重用。
2. 使用装饰器可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限与身份验证从业务中独立出来。
3. 如果一个函数需要一个功能,且这个功能可以被使用在很多函数上,或是函数并不是自己实现,那可以写个装饰器来实现这些功能。
概况来说,装饰器的作用就是为已经存在的对象添加一些额外的功能。

在学习如何运用装饰器前我们先来学习以下几个知识点:

1.变量的作用域:

在python中,函数会创建一个自己的作用域或称之为命名空间,结合以下示例来展示函数命名空间的作用域。

示例代码1:#coding=utf-8
outerVar = "this is a global variable"
def test() :
     innerVar = "this is a Local variable"
     print ("local variables :")
     print (locals()) #locals函数返回的是函数test()内部本地作用域中的可用的变量名称的字典(变量名:值)

test()
print ("global variables :")
print (globals())  #globals函数返回的是python程序中的可用的变量名称的字典(变量名:值)

#执行结果:
local variables :
{‘innerVar‘: ‘this is a Local variable‘}
global variables :
{‘__name__‘: ‘__main__‘, ‘__doc__‘: None, ‘__package__‘: None, ‘__loader__‘: <_frozen_importlib_external.SourceFileLoader object at 0x0000015848FE87F0>,
‘__spec__‘: None, ‘__annotations__‘: {}, ‘__builtins__‘: <module ‘builtins‘ (built-in)>, ‘__file__‘: ‘test.py‘, ‘__cached__‘: None,‘outerVar‘: ‘this is a global variable‘,
‘test‘: <function test at 0x0000015848E11E18>}

2.变量解析规则:
在python的作用域规则里面,创建变量时一定会在当前作用域里创建同样的变量,但访问或修改变量时,会在当前作用域中查找该变量,如果没找到匹配的变量,就会依次向上在闭合作用域中进行查找,所以也可以在函数中直接访问全局变量。

示例代码2:
#coding=utf-8
outerVar = "this is a global variable"
def test() :
 innerVar = "this is a Local variable"
 print (outerVar) #获取全局变量outerVar值
 print (n)        #获取全局变量n的值

n = 33
test()    

#执行结果:
this is a global variable
33 

3.变量的生存空间:
   变量不仅仅是存在于一个个的命名空间中,它们还都有自己的生存周期,全局变量的生存周期是在整个程序执行期间有效,而局部变量的生存周期只在当前作用域中有效,一旦这个作用域不存在了,比如函数执行退出了,变量的生存周期就结束了。

示例代码3:
#coding=utf-8
outerVar = "this is a global variable"
def test() :
 innerVar = "this is a Local variable"
test()
print (innerVar)  #test函数执行结束后,innerVar变量已释放,再访问函数内部变量就会报NameError

执行结果:
Traceback (most recent call last):
  File "test.py", line 56, in <module>
    print (innerVar)
NameError: name ‘innerVar‘ is not defined

4.嵌套函数:
  定义:嵌套函数就是在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。

说明:在python里,函数就是对象,它也只是一些普通的值而已。也就是说你可以把函数像参数一样传递给其他的函数或者说从函数了里面返回函数。

示例代码4:
#coding=utf-8
def outer() :
 name = "python"
 def inner() :      #获取name变量值时,python解析器默认会先在函数内部查找,查找失败后,继续往上一层函数作用域查找。
     print(name)  #python解释器会优先在outer的作用域里面查找变量名为inner的变量。把作为函数标识符的变量inner作为返回值返回。 每次函数outer被调用的时候,函数inner都会被重新定义,如果它不被当做变量返回的话,每次执行过后它将不复存在。
 return inner()  
print (outer())

结果:
python
None   #inner函数默认返回值为None

嵌套函数返回函数不加()表示返回函数对象,如下示例5所示:

示例代码5:#coding=utf-8
def outer() :
 name = "python"
 def inner() :
     print( name)
 return inner   #表示返回函数对象

print (outer())

执行结果:<function outer.<locals>.inner at 0x0000027446B6A9D8>

5.函数作为变量:
  python中函数可以作为参数来传递

示例代码6:
#coding=utf-8
def add(x, y):
  return x + y
def sub(x, y):
  return x - y
def apply(func, x, y):
  return func(x, y) 

print (apply(add, 2, 1)) #add函数作为apply函数的参数传递
print (apply(sub, 2, 1)) 

执行结果:
3
1

6.闭包:

定义:如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包。

一个函数返回的函数对象,这个函数对象执行需要依赖外部函数的变量值,这个时候函数返回的实际内容如下:
    1.函数对象
    2.函数对象执行需要使用的外部变量和变量值。
简而言之:闭包就是返回一个函数和一个函数执行所需要的外部变量。

示例代码7:#coding=utf-8
def outer():
    name = "python"
    def inner() :
       print (name) #函数使用了外部的name变量
    return inner    #返回函数对象

res = outer()    #调用outer()方法,返回inner函数对象
res()            #inner函数的对象+()=调用函数inner()
print (res.func_closure)# 查看函数包含哪些外部变量
#print (res()) #注意使用print打印返回结果为name值+inner函数默认返回值None

执行结果:
python
(<cell at 0x0000000002706CD8: str object at 0x0000000002708738>,) #外部变量是一个str类型  

上例中的inner()函数就是一个闭包,它本身也是一个函数,而且还可以访问本身之外的变量。

每次函数outer被调用时,inner函数都会被重新定义,示例代码7每次返回的函数inner结果都一样,因为name没变。如下例所示,我们将函数稍微改动
一下,结果就不一样了。

示例代码8:#coding=utf-8
def outer(name) :
 def inner() :
   print (name)
 return inner

res1 = outer("python")
res2 = outer("java")
res1()
res2()

执行结果:
python
java

学习了以上6个小知识点,下面开始学习装饰器。

装饰器的定义:
  装饰器其实就是一个闭包,把一个函数当做参数后返回一个替代版函数。

 装饰器分类:

装饰器分为无参数decorator和有参数decorator
无参数decorator:生成一个新的装饰器函数
有参数decorator:装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰。
装饰器的具体定义:
1、把要装饰的方法作为输入参数;
2、在函数体内可以进行任意的操作;
3、只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数,也可以是一个新函数)

装饰器学习七步法:

第一步:最简单的函数,准备附加额外功能

# -*- coding:utf-8 -*-#最简单的函数,表示调用了两次函数
def myfunc():
  print "myfunc() called."
myfunc()
myfunc()

执行结果:
myfunc() called.
myfunc() called.

第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

# -*- coding:utf-8 -*-
‘‘‘示例2: 替换函数(装饰) ,装饰函数的参数是被装饰的函数对象,返回原函数对象。装饰的实质语句: myfunc = deco(myfunc)‘‘‘
def deco(func):
 print ("before myfunc() called.")  #在func()函数执行前附加功能,打印一句话
 func() #被执行函数
 print ("after myfunc() called.")  #在func()函数执行后附加功能,打印一句话
 return func

def myfunc():
 print ("myfunc()called.")

new_myfunc = deco(myfunc) #表示调用参数为myfunc函数对象的deco()函数,结果返回func函数对象并赋值给myfunc
new_myfunc() #表示调用myfunc函数
new_myfunc() #表示调用myfunc函数

结果:
before myfunc() called.
myfunc()called.
after myfunc() called.
myfunc()called.
myfunc()called.
解析:
1.myfunc = deco(myfunc)执行结果:
 before myfunc() called.
 myfunc()called.
 after myfunc() called.
2.第一次调用myfunc()执行结果:
  myfunc()called.
3.第二次调用myfunc()执行结果:
  myfunc()called.

从第2和第3次调用myfunc函数来看,并没有实现每次调用都返回第1次调用的效果,那么我们要实现每次调用都带有附加功能的效果,我们后面会=逐步实现。

第三步:使用@符号来装饰函数

# -*- coding:utf-8 -*-‘‘‘示例3:使用@符号装饰函数,相当于"myfunc = deco(myfunc)",但发现新函数只在第一次被调用,且原函数多调用了一次。等价于第二步程序 ‘‘‘def deco(func):
     print ("before myfunc() called.")
     func()
     print ("after myfunc() called.")
     return func

@deco
def myfunc():
     print ("myfunc()called.")

myfunc()
myfunc() 

#执行结果:
before myfunc() called.
myfunc()called.
after myfunc() called.
myfunc()called.
myfunc()called.

第四步:使用内嵌包装函数来确保每次新函数都被调用

# -*- coding:utf-8 -*-
‘‘‘示例4: 使用内嵌包装函数来确保每次新函数都被调用,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象‘‘‘
def deco(func):
 def _deco():
     print ("before myfunc() called.")
     func()
     print ("after myfunc() called.")
 # 不需要返回func,实际上应返回原函数的返回值
 return _deco

@deco
def myfunc():
 print ("myfunc() called.")
 return ‘ok‘

myfunc()
myfunc() 执行结果:before myfunc() called.myfunc() called.after myfunc() called.before myfunc() called.myfunc() called.after myfunc() called.

上面是实现了1个函数使用装饰器的例子,下面演示2个函数分别使用装饰器的实例:

# -*- coding:utf-8 -*-‘‘‘增加打印函数执行时间功能,分别统计两个函数的执行效率 ‘‘‘
import time
def deco(func):
 def _deco():
     start_time=time.time()
     func()
     end_time=time.time()
     print("执行总耗时:",end_time-start_time)
 return _deco

@deco
def myfunc():
 print (" myfunc() called.")
 time.sleep(1)
 return ‘true‘  

@deco
def youyfunc():
 print (" youyfunc() called.")
 time.sleep(2)
 return ‘true‘

myfunc()
print (‘*‘*20)
youyfunc() 

#结果:
#myfunc() called.
#执行总耗时: 1.0080790519714355
#********************
# youyfunc() called.
#执行总耗时: 2.0119848251342773

#执行过程解析: 执行myfunc()等价于执行deco(myfunc)()
#首先myfunc函数作为参数传递给了deco()函数,形参func被替换为实参myfunc,deco()函数开始顺序执行_deco()函数,
#先调用了myfunc()函数,开始执行myfunc函数,打印执行总耗时,最后返回_deco()函数对象。

说明:使用装饰器的函数之间变量不会互相影响,等于每次调用都会重新生成一个_deco函数。
第五步:实现对带参数的函数进行装饰

# -*- coding:utf-8 -*-‘‘‘  内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象 ‘‘‘def deco(func):
  def _deco(a, b):
         print ("before myfunc() called.")
         ret = func(a, b)
         print ("after myfunc() called. result: %s" % ret)
         return ret
  return _deco  

@deco
def myfunc(a, b):
  print ("myfunc(%s,%s) called."%(a, b))
  return a + b 

myfunc(1, 2)  #使用print (myfunc(1, 2)) 查看return ret 的结果
myfunc(3, 4)   

执行结果:
before myfunc() called.
 myfunc(1,2) called.
after myfunc() called. result: 3
before myfunc() called.
 myfunc(3,4) called.
after myfunc() called. result: 7    

第六步:对参数数量不确定的函数进行装饰

# -*- coding:utf-8 -*-
‘‘‘参数用(*args, **kwargs),自动适应变参和命名参数‘‘‘
def deco(func):
     def _deco(*args, **kwargs):
         print ("before %s called." % func.__name__)
         ret = func(*args, **kwargs)
         print (" after %s called. result: %s" % (func.__name__, ret))
         return ret
     return _deco

@deco
def myfunc1(a, b):
     print (" myfunc(%s,%s) called." % (a, b))
     return a+b

@deco
def myfunc2(a, b, c):
     print (" myfunc2(%s,%s,%s) called." % (a, b, c))
     return a+b+c

myfunc1(1, 2)
myfunc1(3, 4)
myfunc2(1, 2, 3)
myfunc2(3, 4, 5)

#结果
before myfunc1 called.
 myfunc(1,2) called.
 after myfunc1 called. result: 3
before myfunc1 called.
 myfunc(3,4) called.
 after myfunc1 called. result: 7
before myfunc2 called.
 myfunc2(1,2,3) called.
 after myfunc2 called. result: 6
before myfunc2 called.
 myfunc2(3,4,5) called.
 after myfunc2 called. result: 12

第七步:装饰器带可变参数

# -*- coding:utf-8 -*-
‘‘‘在装饰器第四步4的基础上,让装饰器带参数,和上一示例相比在外层多了一层包装。装饰函数名实际上应更有意义些‘‘‘
def deco(arg):      def _deco(func):
         def __deco():
             print ("before %s called [%s]." % (func.__name__, arg))
             func()
             print ("after %s called [%s]." % (func.__name__, arg))
         return __deco
     return _deco

@deco("mymodule1")  #装饰器参数是一个字符串,本身没有含义
def myfunc():
 print (" myfunc() called.")

@deco("mymodule2")
def myfunc2():
 print (" myfunc2() called.")

myfunc()   #调用过程等价于:deco("mymodule1")()()-->_deco()()-->__deco()
myfunc2()

#解析三组闭包:
1. deco("mymodule1")()()+arg-->返回_deco函数对象
2. _deco()()+arg+func -->返回__deco函数对象
3. __deco()+arg+func  --返回函数最终执行结果

执行结果:
before myfunc called [mymodule1].
 myfunc() called.
after myfunc called [mymodule1].
before myfunc2 called [mymodule2].
 myfunc2() called.
after myfunc2 called [mymodule2].

装饰器顺序:
当同时对一个函数使用多个不同的装饰器进行装饰时,这个时候装饰器的顺序就很重要了。
代码示例:

@A
@B
@C
def f():
   pass
等价于:
f = A(B(C(f)))

原文地址:https://www.cnblogs.com/ssj0723/p/10408271.html

时间: 2024-10-14 16:48:31

python之循序渐进学习装饰器的相关文章

Python学习---装饰器/迭代器/生成器的学习【all】

Python学习---装饰器的学习1210 Python学习---生成器的学习1210 Python学习---迭代器学习1210 原文地址:https://www.cnblogs.com/ftl1012/p/9484145.html

python高级编程之装饰器02

#装饰器02 #参数检查 #主要是用在接收或者返回函数,在特定上下文执行时可能有用 #例如:有一个函数通过XML-RPC调用,python将不能和静态类语言中一样直接提供它的完整签名,当XML-RPC客户要求函数签名时,就需要这样的能力 """ xml-rpc相关学习:http://zh.wikipedia.org/wiki/XML-RPC """ #装饰器能提供这种签名类型,并确保输入输出与此有关,如下 from itertools impor

Python - 函数进阶之装饰器

本章内容 高阶函数 装饰器 前言 接着上一篇函数进行整理.. 一.高阶函数 高阶函数就是将一个函数以参数的形式传入另一个函数 1 def main_func(func): 2 # 定义一个主函数,并设置一个参数func 3 4 return func 5 # 返回func的值,因为我需要传入一个函数,即返回func的返回值 6 7 def func(): 8 # 定义一个函数作为参数传入主函数 9 10 return "Lyon" 11 # 返回 "Lyon" 给f

Python 函数式编程、装饰器以及一些相关概念简介

Python 中的 Decorator(装饰器) 是对一个函数或者方法的封装,从而使其可以完成一些与自身功能无关的工作. 预备知识 一切皆对象 在 Python 中,所有的一切都被视为对象,任何的变量.函数.类等都是 object 的子类.因此除了变量之外,函数和类等也可以被指向和传递. >>> def foo(): ... pass ... >>> def Foo(): ... pass ... >>> v = foo >>> v

Python之闭包and装饰器

闭包和装饰器是Python中非常重要的一种语法格式,在日常工作中应用非常广泛. 首先,我先为大家简单的接受一下闭包的概念. 闭包:闭包是在函数嵌套的基础上,内层函数使用到外层函数的变量,且外层函数返回内层函数的引用的一种语法格式. 闭包的基本格式,代码实现: def outer(): num = 0 def inner(): # 使用外部函数的变量 print(num) return inner # 返回inner函数在内存空间中的地址 # 将outer函数的返回值赋值给变量f,也就是说将f指向

python基础三大器之装饰器

闭包 由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包. 给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求. 例如:整个历史中的某个商品的平均收盘价.什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格. 比如大众推出了一款新车:小白轿车. 第一天价格为:100000元,平均收盘价:100000元 第二天价格为:110000元,平均收盘价:(100000 +

python高级编程之装饰器04

from __future__ import with_statement # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #with和contextlib #对于要确保即使发生一个错误时也能运行一些清理代码而言,try...finally语句很有用,对以下场景,如: """ 关闭一个文件, 释放一个锁 创建一个临时代码补丁 在特殊环境中运行受保护代码 ----------- with语句覆盖

Python(四)装饰器、迭代器&生成器、re正则表达式、字符串格式化

本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 先定义一个基本的装饰器: ########## 基本装饰器 ########## def orter(func):    #定义装饰器     de

python高级编程之装饰器01

# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #装饰器01 #特点是:使得函数和方法封装(接收一个函数并返回增强版本一个函数) #语法:原始场景可以将方法 在定义首部将其定义为类方法或者静态方法,在未使用装饰器之前,语法如下: class WhatFort(object): def it(cls): print 'work with %s:'%cls it=classmethod(it) def uncommo