zoukankan      html  css  js  c++  java
  • 写python中的装饰器

    python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器。

    首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下)

     1 def my_log(func):
     2     def wrapper():
     3         print('decorator works')
     4         func()
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def run(): # run=my_log(run)=wrapper 返回的是wrapper函数对象,未运行
     9     print('run')
    10 
    11 run() #run()=wrapper() 此时调用并运行了wrapper()函数
    12 
    13 # 运行结果如下
    14 # decorator works
    15 # run
     1 def my_log(func):
     2     def wrapper():
     3         print('decorator works')
     4         func()
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def add(x,y):   #add=my_log(add)=wrapper 返回的也是wrapper函数对象,未运行
     9     print(x+y)
    10 
    11 add(3,6)    
    12 #试图调用并运行wrapper()
    13 
    14 #结果出错
    15 #TypeError: wrapper() takes 0 positional arguments but 2 were given
    16 # 原因是运行add(3,6)时传入了两个参数add(3,6)=wrapper(3,6) 但wrapper()函数中并无参数接收

    修改后的正确装饰器函数为(被装饰函数仅输出,无返回值情况下)

     1 def my_log(func):
     2     def wrapper(x,y):
     3         print('decorator works')
     4         func(x,y)
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def add(x,y):   #add=my_log(add)=wrapper 返回的也是wrapper函数对象,未运行
     9     print(x+y)
    10 
    11 add(3,6)
    12 
    13 # 运行结果如下
    14 # decorator works
    15 # 9

    但并不是完善的装饰器函数(被装饰函数仅输出,无返回值情况下)

     1 def my_log(func):
     2     def wrapper(x,y):
     3         print('decorator works')
     4         func(x,y)
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def add(x,y):
     9     print(x+y)
    10 
    11 add(3,6)
    12 
    13 @my_log
    14 def run():
    15     print('run')
    16 
    17 run()
    18 
    19 # 但添加无参函数run()时又出现了错误
    20 # TypeError: wrapper() missing 2 required positional arguments: 'x' and 'y'
    21 # 因run()=wrapper()未传给wrapper所必需的两个参数

    此时需要修改装饰器中wrapper函数为非关键字可变参数和关键字可变参数来保证 无论是否有参数传入都正常运行(被装饰函数仅输出,无返回值情况下)

     1 def my_log(func):
     2     def wrapper(*args,**kwargs):
     3         print('decorator works')
     4         func(*args,**kwargs)
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def add(x,y):
     9     print(x+y)
    10 
    11 add(3,6)
    12 
    13 @my_log
    14 def run():
    15     print('run')
    16 
    17 run()
    18 
    19 # 运行结果如下
    20 # decorator works
    21 # 9
    22 # decorator works
    23 # run

    但此时装饰器函数并未完全正确(被装饰函数仅输出,无返回值情况下)

     1 def my_log(func):
     2     def wrapper(*args,**kwargs):
     3         print('decorator works')
     4         func(*args,**kwargs)
     5     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     6 
     7 @my_log
     8 def add(x,y):
     9     print(x+y)
    10 
    11 @my_log
    12 def run():
    13     print('run')
    14 
    15 print(run.__name__)
    16 print(add.__name__)
    17 
    18 # 运行结果如下
    19 # wrapper
    20 # wrapper
    21 #因run=my_log(run)=wrapper 偷偷修改了run方法和add方法的__name__属性

    需要在装饰器函数内部添加@wraps来保证被装饰函数__name__属性不被修改(被装饰函数仅输出,无返回值情况下)

     1 from functools import wraps
     2 
     3 def my_log(func):
     4     @wraps(func)
     5     def wrapper(*args,**kwargs):
     6         print('decorator works')
     7         func(*args,**kwargs)
     8     return wrapper  #返回的只是wrapper函数对象,此时并没有运行
     9 
    10 @my_log
    11 def run(): # run=my_log(run)=wrapper 返回的是wrapper函数对象,未运行
    12     print('running')
    13 
    14 run()
    15 # run()=wrapper()
    16 
    17 print("函数run的__name__是%s"%run.__name__)
    18 print('-'*30)
    19 
    20 @my_log # add=my_log(add)=wrapper 返回的也是wrapper函数对象
    21 def add(a,b):
    22     print(a+b)
    23 
    24 add(3,4)
    25 # add(3,4)=wrapper(3,4)
    26 print("函数add的__name__是%s"%add.__name__)
    27 
    28 # 运行结果如下
    29 # running
    30 # 函数run的__name__是run
    31 # ------------------------------
    32 # decorator works
    33 # 7
    34 # 函数add的__name__是add

    被装饰函数有返回值时

     1 from functools import wraps
     2 
     3 
     4 #***************错误*************************
     5 def login_required(func):
     6     @wraps(func)
     7     def wrapper(*args,**kwargs):
     8         print('hello world')
     9         func(*args,**kwargs)
    10     return wrapper
    11 
    12 @login_required
    13 def index():
    14     return 'aaaaaaaaaaa'
    15 
    16 a=index()
    17 print(a)
    18 #index=login_required()=wrapper
    19 #index()=wrapper()
    20 # def wrapper(*args,**kwargs):
    21 #     print('hello world')
    22 #     index()   此处只是调用index函数得到'aaaaaaaaaaa'   但wrapper函数并未返回该值
    23 
    24 # hello world
    25 # None      可见wrapper()调用index()函数并未返回'aaaaaaaaaaa'
    26 
    27 #**************正确********************
    28 def login_required2(func):
    29     @wraps(func)
    30     def wrapper(*args,**kwargs):
    31         print('hello world')
    32         return func(*args,**kwargs)
    33     return wrapper
    34 
    35 @login_required2
    36 def index():
    37     return 'aaaaaaaaaaa'
    38 
    39 print('-'*20)
    40 a=index()
    41 print(a)
    42 
    43 
    44 
    45 #输出结果如下
    46 #--------------------
    47 # hello world
    48 # aaaaaaaaaaa

    最后总结:

    1.装饰器中定义的函数要使用*args 和**kwargs 组合来接收任何可能被装饰函数的参数,在装饰器中的wrapper函数中执行原函数时需传入*args 和**kwargs

    2.需使用functools.wraps在装饰器中wrapper函数前将wrapper函数用@wraps包裹,防止被装饰函数__name__属性被修改

    以下来自 CS实验室 大佬喵的 https://mp.weixin.qq.com/s?timestamp=1531273713&src=3&ver=1&signature=S-vLsC7yG6GltzbaRovEtemNFSFg3Ps*AQnH1hc3E7-huMUuZbG-i3m0c-8pkEihTO1UIA9wF4Ze8tlMKitOtFb8-eDjsQhYq7KDFDtPZpcotQikbQg8DRhzdgQHArojOFIjBhPb0wAnLzZ2hGn5PXYI4HjRy5CiEpNwr1ii09Y=

     1 def red_oil(func):
     2     print("ready to paint!")
     3 
     4     def red_wall_func():
     5         print("red wall!")
     6 
     7     return red_wall_func
     8 
     9 
    10 @red_oil
    11 def wall():
    12     print("wall!")
    13 
    14 if __name__=="__main__":
    15     print("start painting!!!")

    执行结果如下:

     1 ready to paint!

    2 start painting!!! 

     1 def red_oil(func):
     2     print("ready to paint!")
     3 
     4     def red_wall_func():
     5         print("red wall!")
     6 
     7     return red_wall_func
     8 
     9 
    10 @red_oil
    11 def wall():
    12     print("wall!")
    13 
    14 if __name__=="__main__":
    15     print("start painting!!!")
    16     wall()

    执行结果如下:
     1 ready to paint!

    2 start painting!!!

    3 red wall! 

     

    装饰器在被装饰的函数被定义时立即执行,被装饰的函数在运行时才执行。

  • 相关阅读:
    Windows共享上网的详细设置
    使用树莓派实现微信远程监控
    数据结构——队列及循环队列
    springmvc web 大文件上传源代码
    springboot web 大文件上传源代码
    jsp web 大文件上传源代码
    csharp web 大文件上传源代码
    c# web 大文件上传源代码
    .net web 大文件上传源代码
    asp.net web 大文件上传源代码
  • 原文地址:https://www.cnblogs.com/xiongxueqi/p/8858402.html
Copyright © 2011-2022 走看看