python装饰器,细致讲解

1.无参装饰器

1.储备知识

  1. *args,**kwargs

    def index(x,y):
       print(x,y)

    def wrapper(*args,**kwargs):#接收参数
       func(*args,**kwargs)#将参数打散
  2. 名称空间与作用域:名称空间的“嵌套”是在函数定义阶段,即检测语法的时候确定的

  3. 函数对象:

    • 可以把函数当做参数传入

    • 可以把函数当做返回值返回

  4. 函数的嵌套定义

    def outter(func):
       def wrapper():
           pass
       return wrapper
  5. 闭包函数

    • 函数B在函数A内部,并且函数B调用外层函数作用域的变量,那么函数B就是闭包函数

    • def funcA():
         x=111
         def funcB():
             x
         return funcB
  6. 传递函数的方式

    1. 通过参数的形式为函数传值

    2. 通过闭包的方式为函数传值

2.装饰器

  1. 什么是装饰器

    • 定义一个函数,该函数在不修改原函数代码并且不改变调用方式的前提现,为被装饰的对象增加额外的功能

  2. 为何要用装饰器

    • 开放封闭原则

      • 开放:指的是对拓展功能是开放的

      • 封闭:指的是对修改源代码是封闭的

3.得到装饰器思路

需求:在不修改index函数源代码以及调用方式的情况下为其添加统计时间的功能

def index(x,y):
   time.sleep(3)
   print('index %s %s'%(x,y))
index(111,222)
index(111,y=222)
index(x=111,y=222)

方案一:直接在index函数内加统计时间的代码

import time
def index(x,y):
   start = time.time()
   time.sleep(3)
   print('index %s %s'%(x,y))
   stop = time.time()
   print(stop-start)
   
index(111,333)

问题:没有修改代码的调用方式,但是修改了源代码,方案一失败

 

方案二:在函数调用的时候添加统计时间的代码

def index(x,y):
   time.sleep(3)
   print('index %s %s'%(x,y))
   
start = time.time()
index(111,222)
stop = time.time()
print(stop-start)

start = time.time()
index(333,444)
stop = time.time()
print(stop-start)

start = time.time()
index(555,666)
stop = time.time()
print(stop-start)

问题:没有修改index的源代码,也没有修改调用方式,并且加上了新功能,但是代码冗余。方案二失败

 

方案三:将方案二中冗余的代码部分写成函数

def index(x,y):
   time.sleep(3)
   print('index %s %s'%(x,y))
   
def swapper():    
   start = time.time()
   index(111,222)
   stop = time.time()
   print(stop-start)
   
swapper()

问题:解决了代码冗余问题,但是函数的调用方式发生改变。方案三失败

方案三优化一:将index的参数写活

def index(x, y):
   time.sleep(3)
   print('index %s %s' % (x, y))
   
def swapper(*args,**kwargs):
   start = time.time()
   index(*args,**kwargs)
   stop = time.time()
   print(stop - start)

swapper(2222,54544)

方案三优化二:在优化一的基础上把被装饰的对象写活,原来只能装饰index

def index(x,y,z):
   time.sleep(3)
   print('index %s %s %s' %(x,y,z))

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


def outter(func):
   # func = index的内存地址
   def wrapper(*args,**kwargs):
       start=time.time()
       func(*args,**kwargs) # index的内存地址()
       stop=time.time()
       print(stop - start)
   return wrapper

index=outter(index) # index=wrapper的内存地址
home=outter(home) # home=wrapper的内存地址

方案三优化三:给wrapper添加返回值,达到以假乱真

def index(x,y,z):
   time.sleep(3)
   print('index %s %s %s' %(x,y,z))

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

def outter(func):
   def wrapper(*args,**kwargs):
       start=time.time()
       res=func(*args,**kwargs)
       stop=time.time()
       print(stop - start)
       return res
   return wrapper
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
# home=outter(home)
#
#
# res=home('egon') # res=wrapper('egon')
# print('返回值--》',res)

4.语法糖

import time

装饰器
def timmer(func):
   def wrapper(*args,**kwargs):
       start=time.time()
       res=func(*args,**kwargs)
       stop=time.time()
       print(stop - start)
       return res
   return wrapper


# 在被装饰对象正上方的单独一行写@装饰器名字
# @timmer # index=timmer(index)
def index(x,y,z):
   time.sleep(3)
   print('index %s %s %s' %(x,y,z))

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


index(x=1,y=2,z=3)
home('egon')

2.有参装饰器

1.知识储备

  • 由于语法糖@的限制,outter函数只能有一个参数,并且该参数只能接受被装饰对象的内存地址

def outter(func):
   # func = 被装饰函数的内存地址
   def wrapper(*args,**kwargs):
       res = func(*args,**kwargs)
       return res
   return wrapper

@outter #index=outter(index) ==> index=wrapper
def index(x,y):
   print(x,y)
  • index被装饰后(偷梁换柱之后)

    • index的参数什么样子,wrapper的参数就应该什么样子

    • index的返回值什么样子,wrapper的返回值就应该什么样子

    • index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps

 

2.有参装饰器

def auth(db_type):
   def deco(func):
       def wrapper(*args, **kwargs):
           name = input('your name>>>: ').strip()
           pwd = input('your password>>>: ').strip()

           if db_type == 'file':
               print('基于文件的验证')
               if name == 'egon' and pwd == '123':
                   res = func(*args, **kwargs)  # index(1,2)
                   return res
               else:
                   print('user or password error')
           elif db_type == 'mysql':
               print('基于mysql的验证')
           elif db_type == 'ldap':
               print('基于ldap的验证')
           else:
               print('不支持该db_type')
       return wrapper
   return deco
@auth(db_type='file')  # @deco # index=deco(index) # index=wrapper
def index(x, y):
   print('index->>%s:%s' % (x, y))

@auth(db_type='mysql')  # @deco # home=deco(home) # home=wrapper
def home(name):
   print('home->>%s' % name)
   
@auth(db_type='ldap')  # 账号密码的来源是ldap
def transfer():
   print('transfer')

3.有参装饰器模板

def 有参装饰器(x,y,z):
   def outter(func):
       def wrapper(*args, **kwargs):
           res = func(*args, **kwargs)
           return res
       return wrapper
   return outter

@有参装饰器(1,y=2,z=3)
def 被装饰对象():
   pass

 

posted @ 2020-11-01 00:38  安兹  阅读(130)  评论(0编辑  收藏  举报