代码改变世界

Python装饰器

2018-04-24 20:18  钱先生  阅读(157)  评论(0编辑  收藏  举报

装饰器

  • 本质是函数
  • 功能: 装饰其他函数,即为其他函数添加附加功能
  • 装饰器需要遵循的原则: 
    • 不能修改被装饰的函数
      • 例: 现在有三个函数 , 各自有其功能. 如果需要给这三个函数都添加一个日志功能. 或这三个函数都已经在线上运行了, 现在需要再新增一些功能. 可以采取的方法:
        • 修改源代码, 每个函数都添加这些功能 (风险非常大)
    • 不能修改被装饰的函数的调用方式
    • 装饰器对被装饰的函数是完全透明的. 即装饰器完全不影响原函数.

    

 1 import time
 2 def timmer(func):   # 装饰器
 3     def warpper(*args, **kwargs):
 4         start_time = time.time()
 5         func()
 6         stop_time = time.time()
 7         print('the func run time is %s' %(stop_time-start_time))
 8     return warpper
 9 
10 @timmer
11 def test1():
12     time.sleep(3)
13     print('test1')
14 
15 
16 test1()

 

 

实现装饰器知识储备:

  • 函数即"变量"
  •  1 def bar():
     2     print('bar')
     3 
     4 
     5 def foo():
     6     print('foo')
     7     bar()
     8 
     9 
    10 foo()
    11 
    12 
    13 print("=========================")
    14 
    15 # 测试函数中引用的函数可以是定义在测试函数前面, 也可以是定义在测试函数后面的. 但一定是在调用函数语句的前面.
    16 
    17 
    18 def foo1():
    19     print('foo1')
    20     bar1()
    21 
    22 
    23 def bar1():
    24     print('bar1')
    25 
    26 
    27 foo1()
    View Code
  • 高阶函数
    • a: 把一个函数名当做实参传给另外一个函数 (在不修改被装饰函数情况下为其添加功能)
      •  1 print("=========== 返回函数内存地址 ===========")
         2 
         3 
         4 def bar():
         5     print('bar')
         6 
         7 
         8 def test1(func):
         9     print(func)
        10 
        11 
        12 test1(bar)  # 返回一段内存地址
        13 
        14 
        15 print("=========== 返回函数执行结果 ===========")
        16 
        17 
        18 # 若想返回函数的执行结果而不是函数的内存地址, 需要这样更改
        19 def bar():
        20     print('bar')
        21 
        22 
        23 def test1(func):
        24     print(func)
        25     func()
        26 
        27 
        28 test1(bar)  # 返回bar
        29 
        30 
        31 print("===========  高阶函数进阶版  ===========")
        32 
        33 
        34 import time
        35 
        36 
        37 def bar():
        38     print('bar')
        39 
        40 
        41 def test1(func):  # 实现了装饰器的功能. 运行bar()函数的同时附加了计时功能. 但是改变了函数调用方式,所以这种方法不能称为装饰器.
        42     start_time = time.time()
        43     func()  # run bar
        44     stop_time = time.time()
        45     print('the func run time is %s' % (stop_time-start_time))
        46 
        47 
        48 test1(bar)
        View Code
    • 返回值中包含函数名 (不修改函数的调用方式)
      •  1 import time
         2 
         3 
         4 def bar():
         5     time.sleep(3)
         6     print('bar')
         7 
         8 
         9 def test2(func):
        10     print(func)  # 打印函数func的内存地址
        11     return func  # 返回函数func的内存地址
        12 
        13 
        14 '''
        15 test2(bar) 和 test2(bar())的区别:
        16 test2(bar) : 
        17     把函数bar()的内存地址传给函数test2
        18 
        19 test2(bar()) : (不符合高阶函数的定义)
        20     把函数bar()的运行结果传给函数test2
        21 '''
        22 t = test2(bar)
        23 print(t)
        24 
        25 t()  # 代表运行函数bar, 等同bar()
        26 
        27 
        28 print("==========================")
        29 
        30 # 下面这种写法不会报错, 也不会陷入死循环
        31 bar = test2(bar)  # 用变量bar接收函数test2()的运行结果(实际接收到的是函数bar的内存地址. 所以变量bar后面加()就可以调用函数bar())
        32 bar()  # run bar
        View Code
  • 嵌套函数
    •  1 #  函数嵌套: 在一个函数的函数体内去声明一个新的函数.
       2 
       3 
       4 def foo():
       5     print('foo')
       6 
       7     def bar():
       8         print('bar')
       9 
      10     bar()
      11 
      12 
      13 foo()
      14 
      15 
      16 print("=========== 局部作用域和全局作用域的访问顺序 ===========")
      17 x = 0
      18 def grandpa():
      19     x = 1
      20     def dad():
      21         x=2
      22         def son():
      23             x=3
      24             print(x)
      25         son()
      26     dad()  # 如果不调用dad()函数, 相当于定义了一个变量, 却没有用这个变量.
      27 grandpa()  # 返回3
      View Code
  • 高阶函数+嵌套函数 ==>装饰器

 

通用版装饰器

  1 #!/usr/bin/python
  2 # -*- coding: utf-8 -*-
  3 import time
  4 
  5 print("============== 不成功的装饰器,只有高阶函数没有嵌套 ==============")
  6 def deco(func):
  7     start_time = time.time()
  8     return func
  9     stop_time = time.time()
 10     print('the func run time is %s' % (stop_time-start_time))
 11 
 12 
 13 def test1():
 14     time.sleep(3)
 15     print('in the test1')
 16 
 17 
 18 def test2():
 19     time.sleep(3)
 20     print('in the test2')
 21 
 22 
 23 test1 = deco(test1)  # 获取函数返回值,将test1的内存地址传给变量test1.
 24 print(test1)
 25 test2 = deco(test2)
 26 print(test2)
 27 
 28 '''
 29 这样这条语句的返回值是in the test1, 装饰器没有发挥作用. 因为deco()函数中return func 后面的语句遇到return并没有执行.
 30 '''
 31 
 32 
 33 print("============  成功的装饰器, 高阶函数+嵌套函数(方法1) ============")
 34 
 35 
 36 def timer(func):
 37     def deco():  # 相当于定义了一个变量, 这个变量是个函数
 38         start_time = time.time()
 39         func()
 40         stop_time = time.time()
 41         print('the func run time is %s' % (stop_time-start_time))
 42     return deco  # 返回函数deco的内存地址
 43 
 44 
 45 def test1():
 46     time.sleep(3)
 47     print('in the test1')
 48 
 49 
 50 def test2():
 51     time.sleep(3)
 52     print('in the test2')
 53 
 54 
 55 test1 = timer(test1)  # 将函数deco的内存地址赋值给变量test1
 56 test1()  # 执行deco
 57 test2 = timer(test2)
 58 test2()
 59 
 60 
 61 print("============  成功的装饰器, 高阶函数+嵌套函数(方法2) ============")
 62 
 63 
 64 def timer(func):
 65     def deco():  # 相当于定义了一个变量, 这个变量是个函数
 66         start_time = time.time()
 67         func()
 68         stop_time = time.time()
 69         print('the func run time is %s' % (stop_time-start_time))
 70     return deco  # 返回函数deco的内存地址
 71 
 72 
 73 @timer  # 这个注释其实完成了一个动作: test1 = timer(test1)
 74 def test1():
 75     time.sleep(3)
 76     print('in the test1')
 77 
 78 
 79 @timer   # 这个注释其实完成了一个动作:test2 = timer(test2)
 80 def test2():
 81     time.sleep(3)
 82     print('in the test2')
 83 
 84 
 85 test1()  # 执行deco
 86 test2()  # 执行deco
 87 
 88 '''
 89 注: 上述方法, 若是test2(arg1)函数中有参数, 则会出错.
 90     在deco里传参(def deco(arg1))可以解决这个问题, 但是test1又会出错. 
 91 '''
 92 
 93 
 94 print("============  非固定参数装饰器, 满足有参和无参的函数 (通用装饰器) ============")
 95 def timer(func):
 96     def deco(*args, **kwargs):
 97         start_time = time.time()
 98         func(*args, **kwargs)
 99         stop_time = time.time()
100         print('the func run time is %s' % (stop_time - start_time))
101 
102     return deco  # 返回函数deco的内存地址
103 
104 
105 @timer  # 这个注释其实完成了一个动作: test1 = timer(test1)
106 def test1():
107     time.sleep(3)
108     print('in the test1')
109 
110 
111 @timer  # 这个注释其实完成了一个动作:test2 = timer(test2)
112 def test2(arg1):
113     time.sleep(3)
114     print('in the test2')
115 
116 
117 test1()  # 执行deco
118 test2("abc")  # 执行deco

 

完善版装饰器

  • 处理函数的返回结果
  1 #!/usr/bin/python
  2 # -*- coding: utf-8 -*-
  3 print("=========== 装饰器改变了函数的返回结果. 并没有返回函数自身的返回结果 ===========")
  4 user, pwd = 'alex','abc123'
  5 def auth(func):
  6     def wrapper(*args, **kwargs):
  7         username = input("username: ")
  8         password = input("password: ")
  9 
 10         if user == username and password == pwd:
 11             print("\033[32;1mUser has passed authentication\033[0m")
 12             func(*args, **kwargs)  # 执行之后有一个Func的返回值,但装饰器并没有处理这个返回值
 13         else:
 14             exit("\033[32;1mInvalid username or password \033[0m")
 15     return wrapper
 16 
 17 
 18 def index():
 19     print("welcome to index page")
 20     return ("from index")
 21 @auth
 22 def home():
 23     print("welcome to home page")
 24     return("from home")
 25 @auth
 26 def bbs():
 27     print("welcome to bbs page")
 28     return("from bbs")
 29 
 30 
 31 index()
 32 home()
 33 print(home)
 34 bbs()
 35 
 36 
 37 print("=========== 完善版装饰器, 返回函数自身的返回结果(方法1)===========")
 38 user, pwd = 'alex','abc123'
 39 def auth(func):
 40     def wrapper(*args, **kwargs):
 41         username = input("username: ")
 42         password = input("password: ")
 43 
 44         if user == username and password == pwd:
 45             print("\033[32;1mUser has passed authentication\033[0m")
 46             return func(*args, **kwargs)
 47         else:
 48             exit("\033[32;1mInvalid username or password \033[0m")
 49     return wrapper
 50 
 51 
 52 def index():
 53     print("welcome to index page")
 54     return ("from index")
 55 @auth
 56 def home():
 57     print("welcome to home page")
 58     return("from home")
 59 @auth
 60 def bbs():
 61     print("welcome to bbs page")
 62     return("from bbs")
 63 
 64 
 65 index()
 66 home()
 67 print(home)
 68 bbs()
 69 
 70 
 71 print("=========== 完善版装饰器, 返回函数自身的返回结果(方法2)===========")
 72 user, pwd = 'alex','abc123'
 73 def auth(func):
 74     def wrapper(*args, **kwargs):
 75         username = input("username: ")
 76         password = input("password: ")
 77 
 78         if user == username and password == pwd:
 79             print("\033[32;1mUser has passed authentication\033[0m")
 80             res = func(*args, **kwargs)
 81             print("----- after authentication -----")
 82             return res
 83         else:
 84             exit("\033[32;1mInvalid username or password \033[0m")
 85     return wrapper
 86 
 87 
 88 def index():
 89     print("welcome to index page")
 90     return ("from index")
 91 @auth
 92 def home():
 93     print("welcome to home page")
 94     return("from home")
 95 @auth
 96 def bbs():
 97     print("welcome to bbs page")
 98     return("from bbs")
 99 
100 
101 index()
102 home()
103 print(home)
104 bbs()
View Code

 

终极版装饰器

  • 提供多种认证方式 (用户名密码 /SSL /ldap )
  •  1 print("=========== 终极版装饰器, 支持多种认证方式)===========")
     2 user, pwd = 'alex','abc123'
     3 def auth(auth_type):
     4     print("auth func: ", auth_type)
     5     def outer_wrapper(func):
     6         def wrapper(*args, **kwargs):
     7             print("wrapper func args ", *args, **kwargs)
     8             if auth_type == "local":
     9                 username = input("username: ")
    10                 password = input("password: ")
    11 
    12                 if user == username and password == pwd:
    13                     print("\033[32;1mUser has passed authentication\033[0m")
    14                     func(*args, **kwargs)  # 执行之后有一个Func的返回值,但装饰器并没有处理这个返回值
    15                 else:
    16                     exit("\033[32;1mInvalid username or password \033[0m")
    17             elif auth_type == "ldap":
    18                 print('ldap')
    19         return wrapper
    20     return outer_wrapper
    21 
    22 
    23 def index():
    24     print("welcome to index page")
    25     return ("from index")
    26 @auth(auth_type = "local")
    27 def home():
    28     print("welcome to home page")
    29     return("from home")
    30 @auth(auth_type = "ldap")
    31 def bbs():
    32     print("welcome to bbs page")
    33     return("from bbs")
    34 
    35 
    36 index()
    37 home()
    38 print(home)
    39 bbs()
    View Code

     

 

 

 

 

 

\