漫天飞雪

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

装饰器(闭包函数的一种应用)

1. 什么是装饰器
器:指的是具备某一功能的工具
装饰:指的是为被装饰器对象添加新功能

装饰器就是用来为被装饰器对象添加新功能的工具

需要注意的是:装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象


2. 为何要用装饰器
因为在实际开发环境中,是不允许对原代码进行操作,而且不能破坏原有的调用关系,根据实际要求还需要增添新功能
开发需要遵循的一个原则
开放封闭原则:封闭指的是对修改封闭,对扩展开放,就是保持原有功能并新增新的功能

装饰器的实现必须遵循两大原则:
1. 不修改被装饰对象的源代码,被操作的函数的源代码是不能进行修改
2. 不修改被装饰器对象的调用方式

装饰器的目标:就是在遵循1和2原则的前提下为被装饰对象添加上新功能

3. 怎么用装饰器

无参装饰器
  装饰器不需要参数传入
有参装饰器
  装饰器需要参数传入

# 装饰器模板
def outter(func):
  def wrapper(*args,**kwargs):
    #在调用函数前加功能
    res=func(*args,**kwargs) #调用被装饰的\也就是最原始的那个函数
    #在调用函数后加功能
    return res
  return wrapper

注意:
1.需要注意的是装饰器最多有三层
2.解释装饰器时@语法的时候是”自下而上运行“
3.执行装饰器时装饰器内的那个wrapper函数时的是”自上而下“

语法糖
什么是语法糖?
使用了”@”语法糖后,就不需要额外代码来给”被装饰函数”重新赋值了,
其实”@装饰器函数”的本质就是”被装饰函数 = 装饰器函数(被装饰函数#原始的被装饰函数的内存地址)”。
为什么用语法糖?
使用语法糖让代码简化,不需要重复在调用装饰器时,进行重复的赋值操作。

 


一、 无参装饰器及其升级版
1 开放封闭原则
软件一旦上线后,就应该遵循开放封闭原则,即对修改源代码是封闭的,对功能的扩展是开放的。也就是说我们必须找到一种解决方案:能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能。

总结原则如下:
1、不修改源代码
2、不修改调用方式
目的:在遵循1和2原则的基础上扩展新功能

2、什么是装饰器?
器指的工具,装饰指的是为被装饰对象添加新功能。装饰器即在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能。

装饰器与被装饰的对象均可以是任意可调用的对象
装饰器=》函数
被装饰的对象=》函数

3、给函数加上了功能,但改变了原代码
import time

def index():
  start_time=time.time() #time.time()调的是1970年至现在一共过了多少秒
  time.sleep(3)
  print('welcome to index page')
  stop_time=time.time()
  print('run time is %s' %(stop_time-start_time))

index()

4. 没有把常用功能抽象成函数,需要反复输入
import time

def index():
  time.sleep(3)
  print('welcome to index page')

def home(name):
  time.sleep(5)
  print('welcome %s to home page' %name)

  start_time=time.time()
  index()
  stop_time = time.time()
  print('run time is %s' % (stop_time - start_time))

  start_time=time.time()
  home('egon')
  stop_time = time.time()
  print('run time is %s' % (stop_time - start_time))

5、修改了原函数的调用方式,即无法用index()的形式直接调用
import time

def index():
  time.sleep(3)
  print('welcome to index page')

def home(name):
  time.sleep(5)
  print('welcome %s to home page' %name)

def wrapper(func): #func=index
  start_time=time.time()
  func() #index()
  stop_time = time.time()
  print('run time is %s' % (stop_time - start_time))

wrapper(index) # 修改了原函数的调用方式

6、 初步解决了问题,但没有考虑到原参数带返回值,或带参数的情况
import time

def index():
  time.sleep(3)
  print('welcome to index page')

def outter(func): #func=最原始的index
  # func=最原始的index
  def wrapper():
    start_time=time.time()
    func()
    stop_time=time.time()
    print(stop_time-start_time)
  return wrapper

index=outter(index) # 新的index=wrapper
index() #wrapper()

7、 考虑了原参数带返回值或参数的问题,但每次都要加上index=timmer(index)
import time

def index():
  time.sleep(1)
  print('welcome to index page')
  return 122

def home(name):
  time.sleep(2)
  print('welcome %s to home page' %name)

#==============装饰器
def timmer(func):
  #func=最原始的home
  def wrapper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs) #调用最原始的home
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return wrapper

index=timmer(index) # 新的index=wrapper
home=timmer(home) #新的home=wrapper
# ==========================================
index()
home('egon')

无参装饰器模板
def outer(func):
  def inner(*args,**kwargs):
    res=func(*args,**kwargs)
    return res
  return inner

8、运用装饰器语法糖 @timmer #index=timmer(index)

@timmer必须放在原函数的正上方,

timmer的函数体要放在@timmer之前

如果要用装饰器加@。。。,如果不用注释掉@


import time
def timmer(func):
  def wrapper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs)
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return wrapper

@timmer #index=timmer(index)
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122

@timmer # home=timmer(home)
def home(name):
  time.sleep(2)
  print('welcome %s to home page' %name)

index()
home('egon')

9、认证装饰器实现(另一个例子)
import time
current_user={
      'username':None,
# 'login_time':None
      }

def auth(func):
  # func=index
  def wrapper(*args,**kwargs):
    if current_user['username']:
      print('已经登陆过了')
      res=func(*args,**kwargs)
      return res

  uname=input('用户名>>: ').strip()
  pwd=input('密码>>: ').strip()
  if uname == 'egon' and pwd == '123':
    print('登陆成功')
    current_user['username']=uname
    res=func(*args,**kwargs)
    return res
  else:
    print('用户名或密码错误')
  return wrapper

@auth #index=auth(index)
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122

@auth
def home(name):
  time.sleep(2)
  print('welcome %s to home page' %name)

index()
home('egon')

10、叠加多个装饰器,上面的装饰器作用为:下方装饰器+原函数
import time
current_user={
'username':None,
# 'login_time':None
}

def auth(func):
  # func=index
  def wrapper(*args,**kwargs):
    if current_user['username']:
      print('已经登陆过了')
      res=func(*args,**kwargs)
    return res

    uname=input('用户名>>: ').strip()
    pwd=input('密码>>: ').strip()
    if uname == 'egon' and pwd == '123':
      print('登陆成功')
      current_user['username']=uname
      res=func(*args,**kwargs)
      return res
    else:
      print('用户名或密码错误')
  return wrapper

def timmer(func):
  def wrapper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs)
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return wrapper

@timmer # timmer 统计的是auth+index的执行时间
@auth
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122

index()


二、 有参数的装饰器 #再包上一层,函数的参数可以为一个,也可为多个
import time
current_user={
'username':None,
# 'login_time':None
}

def auth(engine):
  # engine='file'
  def auth2(func):
  # func=index
    def wrapper(*args,**kwargs):
      if engine == 'file':
        if current_user['username']:
          print('已经登陆过了')
          res=func(*args,**kwargs)
          return res

        uname=input('用户名>>: ').strip()
        pwd=input('密码>>: ').strip()
        if uname == 'egon' and pwd == '123':
          print('登陆成功')
          current_user['username']=uname
          res=func(*args,**kwargs)
          return res
        else:
          print('用户名或密码错误')
      elif engine == 'mysql':
        print('基于MyQL的认证')
      elif engine == 'ldap':
        print('基于LDAP的认证')
     return wrapper
  return auth2

@auth('ldap') #@auth2 #index=auth2(index) #index=wrapper
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122

index() # wrapper()

 装饰器补充functools功能模块
因为Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性
会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写
一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。
具体实现形式,是导入from functiontools import wraps,在函数内调用时就能实现属性一直性。
实例:
不加wraps情况
from functools import wraps
def my_decorator(func):

  # @wraps(func)
  def wrapper(*args, **kwargs):
    '''decorator'''
    print('Calling decorated function...')
    return func(*args, **kwargs)
  return wrapper

@my_decorator
def example():
  """Docstring"""
  print('Called example function')

  print(example.__name__, example.__doc__)
输出结果:
wrapper decorator

加wraps情况
from functools import wraps
def my_decorator(func):
  @wraps(func)
  def wrapper(*args, **kwargs):
    '''decorator'''
    print('Calling decorated function...')
    return func(*args, **kwargs)

  return wrapper

@my_decorator
def example():
  """Docstring"""
  print('Called example function')

  print(example.__name__, example.__doc__)
输出结果:
example Docstring

内容总结:
装饰器(闭包函数的一种应用)
  无参装饰器
  有参装饰器


posted on 2018-12-26 19:58  漫天飞雪世情难却  阅读(93)  评论(0编辑  收藏  举报