• Python装饰器详解


      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 """
      4                                                     __----~~~~~~~~~~~------___
      5                                    .  .   ~~//====......          __--~ ~~
      6                    -.            \_|//     |||\  ~~~~~~::::... /~
      7                ___-==_       _-~o~  /    |||  \            _/~~-
      8         __---~~~.==~||=_    -_--~/_-~|-   |\   \        _/~
      9     _-~~     .=~    |  \-_    '-~7  /-   /  ||          /
     10   .~       .~       |   \ -_    /  /-   /   ||         /
     11  /  ____  /         |     \ ~-_/  /|- _/   .||        /
     12  |~~    ~~|--~~~~--_      ~==-/   | ~--===~~        .
     13           '         ~-|      /|    |-~~~       __--~~
     14                       |-~~-_/ |    |   ~\_   _-~            /
     15                            /       \__   /~                \__
     16                        _--~ _/ | .-~~____--~-/                  ~~==.
     17                       ((->/~   '.|||' -_|    ~~-/ ,              . _||
     18                                  -_     ~      ~~---l__i__i__i--~~_/
     19                                  _-~-__   ~)  --______________--~~
     20                                //.-~~~-~_--~- |-------~~~~~~~~
     21                                       //.-~~~--
     22                                神兽保佑
     23                               代码无BUG!
     24 
     25 """
     26 
     27 # import heartrate
     28 #
     29 # heartrate.trace(browser=True)
     30 
     31 # 01. 最简单的装饰器
     32 '''
     33 def decorator(func):
     34     def wrapper(*args, **kw):
     35         return func()
     36     return wrapper
     37 
     38 @decorator
     39 def function():
     40     print("hello, decorator")
     41 
     42 
     43 function()
     44 '''
     45 
     46 # 02. 日志打印器
     47 '''
     48 def decorator(func):
     49     def wrapper(*args, **kwargs):
     50         print('开始准备执行:{}函数了'.format(func.__name__))
     51 
     52         # 真正的执行函数
     53         func(*args, **kwargs)
     54 
     55         print('{}函数执行完毕'.format(func.__name__))
     56 
     57     return wrapper
     58 
     59 
     60 @decorator
     61 def add(x, y):
     62     print('{} + {} = {}'.format(x, y, x + y))
     63 
     64 
     65 add(5, 7)
     66 '''
     67 
     68 # 03. 时间计时器
     69 '''
     70 import time
     71 
     72 
     73 def timer(func):
     74     def wrapper(*args, **kw):
     75         t1 = time.time()
     76         # 这是函数真正执行的地方
     77         func(*args, **kw)
     78         t2 = time.time()
     79 
     80         # 计算下时长
     81         cost_time = t2 - t1
     82         print("花费时间:{}秒".format(cost_time))
     83 
     84     return wrapper
     85 
     86 
     87 @timer
     88 def want_sleep(sleep_time):
     89     time.sleep(sleep_time)
     90 
     91 
     92 want_sleep(10)
     93 '''
     94 
     95 # 04. 带参数的函数装饰器 (注意,是装饰器带参数,装饰器修饰的函数不带参数)
     96 # 在原有函数装饰器的基础上外面再套一层函数
     97 '''
     98 def say_hello(contry):
     99     def wrapper(func):
    100         def deco(*args, **kwargs):
    101             if contry == "china":
    102                 print("你好!")
    103             elif contry == "america":
    104                 print('hello.')
    105             else:
    106                 return
    107 
    108             # 真正执行函数的地方
    109             func(*args, **kwargs)
    110 
    111         return deco
    112 
    113     return wrapper
    114 
    115 
    116 # 小明,中国人
    117 @say_hello("china")
    118 def xiaoming():
    119     pass
    120 
    121 
    122 # jack,美国人
    123 @say_hello("america")
    124 def jack():
    125     pass
    126 
    127 
    128 xiaoming()
    129 print("------------")
    130 jack()
    131 '''
    132 
    133 # 05. 高阶:不带参数的类装饰器 (装饰器修饰的函数带参数)
    134 """
    135 基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。
    136 __init__ :接收被装饰函数
    137 __call__ :实现装饰逻辑。
    138 """
    139 
    140 """
    141 class logger():
    142     def __init__(self, func):
    143         self.func = func
    144 
    145     def __call__(self, *args, **kwargs):
    146         print("[INFO]: the function {func}() is running..." 
    147               .format(func=self.func.__name__))
    148         return self.func(*args, **kwargs)
    149 
    150 
    151 @logger
    152 def say(something):
    153     print("say {}!".format(something))
    154 
    155 
    156 say("hello")
    157 """
    158 
    159 # 06. 高阶:带参数的类装饰器 (注意:类装饰器带参数,装饰的函数也带参数)
    160 
    161 """
    162 带参数和不带参数的类装饰器有很大的不同。
    163 __init__ :不再接收被装饰函数,而是接收传入参数。
    164 __call__ :接收被装饰函数,实现装饰逻辑。
    165 """
    166 
    167 """
    168 class logger():
    169     def __init__(self, level='INFO'):
    170         self.level = level
    171 
    172     def __call__(self, func):  # 接受函数
    173         def wrapper(*args, **kwargs):
    174             print("[{level}]: the function {func}() is running..." 
    175                   .format(level=self.level, func=func.__name__))
    176             func(*args, **kwargs)
    177 
    178         return wrapper  # 返回函数
    179 
    180 
    181 @logger(level='WARNING')
    182 def say(something):
    183     print("say {}!".format(something))
    184 
    185 
    186 say("hello")
    187 """
    188 
    189 # 07. 使用偏函数与类实现装饰器
    190 
    191 """
    192 绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。
    193 
    194 事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。
    195 
    196 对于这个 callable 对象,我们最熟悉的就是函数了。
    197 
    198 除函数之外,类也可以是 callable 对象,只要实现了__call__ 函数(上面几个例子已经接触过了)。
    199 
    200 还有容易被人忽略的偏函数其实也是 callable 对象。
    201 
    202 接下来就来说说,如何使用类和偏函数结合实现一个与众不同的装饰器。
    203 
    204 如下所示,DelayFunc 是一个实现了 __call__ 的类,delay 返回一个偏函数,在这里 delay 就可以做为一个装饰器。
    205 """
    206 
    207 """
    208 import time
    209 import functools
    210 
    211 
    212 class DelayFunc:
    213     def __init__(self, duration, func):
    214         self.duration = duration
    215         self.func = func
    216 
    217     def __call__(self, *args, **kwargs):
    218         print(f'Wait for {self.duration} seconds...')
    219         time.sleep(self.duration)
    220         return self.func(*args, **kwargs)
    221 
    222     def eager_call(self, *args, **kwargs):
    223         print('Call without delay')
    224         return self.func(*args, **kwargs)
    225 
    226 
    227 def delay(duration):
    228  
    229     # 装饰器:推迟某个函数的执行。
    230     # 同时提供 .eager_call 方法立即执行
    231     # 此处为了避免定义额外函数,
    232     # 直接使用 functools.partial 帮助构造 DelayFunc 实例
    233     return functools.partial(DelayFunc, duration)
    234 
    235 
    236 @delay(duration=2)
    237 def add(a, b):
    238     print(a+b)
    239     return a + b
    240 
    241 add(3,5)
    242 """
    243 
    244 """
    245 >>> add    # 可见 add 变成了 Delay 的实例
    246 <__main__.DelayFunc object at 0x107bd0be0>
    247 >>> 
    248 >>> add(3,5)  # 直接调用实例,进入 __call__
    249 Wait for 2 seconds...
    250 8
    251 >>> 
    252 >>> add.func # 实现实例方法
    253 <function add at 0x107bef1e0>
    254 """
    255 
    256 # 08. 装饰类的装饰器
    257 instances = {}
    258 
    259 
    260 def singleton(cls):
    261     def get_instance(*args, **kw):
    262         # print(cls) # <class '__main__.User'>
    263         cls_name = cls.__name__  # User
    264         print('===== 1 ====')
    265         # print(instances) # {}
    266         if not cls_name in instances:
    267             print('===== 2 ====')
    268             instance = cls(*args, **kw)
    269             instances[cls_name] = instance
    270         return instances[cls_name]
    271 
    272     return get_instance
    273 
    274 
    275 @singleton
    276 class User:
    277     _instance = None
    278 
    279     def __init__(self, name):
    280         print('===== 3 ====')
    281         self.name = name
    282 
    283 
    284 u1 = User("Tom")
    285 """
    286 ===== 1 ====
    287 ===== 2 ====
    288 ===== 3 ====
    289 """
    290 
    291 u1.age = 20
    292 
    293 u2 = User("Lily")
    294 """
    295 ===== 1 ====
    296 """
    297 
    298 print(u2.age)  # 20
    299 
    300 print(u1 == u2)  # True
    301 
    302 
    303 # 09. wraps 装饰器
    304 def wrapper(func):
    305     print(func.__name__)  # wrapped
    306 
    307     def inner_function():
    308         pass
    309 
    310     return inner_function
    311 
    312 
    313 @wrapper
    314 def wrapped():
    315     print('3')
    316 
    317 
    318 print(wrapped.__name__)  # inner_function
    319 print(wrapper(wrapped).__name__)  # inner_function
    320 '''
    321 上边执行func 和下边 decorator(func)  是等价的,所以上面 func.__name__ 是等价于下面decorator(func).__name__ 的
    322 '''
    323 
    324 # 使用 functools .wraps 装饰器,将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper)
    325 from functools import wraps
    326 
    327 
    328 def wrapper(func):
    329     print(func.__name__)  # wrapped
    330 
    331     @wraps(func)
    332     def inner_function():
    333         pass
    334 
    335     return inner_function
    336 
    337 
    338 @wrapper
    339 def wrapped():
    340     pass
    341 
    342 
    343 print(wrapped.__name__)  # wrapped
    344 
    345 """
    346 准确点说,wraps 其实是一个偏函数对象(partial),源码如下
    347 
    348 def wraps(wrapped,
    349           assigned = WRAPPER_ASSIGNMENTS,
    350           updated = WRAPPER_UPDATES):
    351     return partial(update_wrapper, wrapped=wrapped,
    352                    assigned=assigned, updated=updated)
    353 """
    354 
    355 """
    356 可以看到wraps其实就是调用了一个函数update_wrapper,改写上面的代码,在不使用 wraps的情况下,也可以让 wrapped.__name__ 打印出 wrapped
    357 """
    358 from functools import update_wrapper
    359 
    360 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
    361                        '__annotations__')
    362 
    363 
    364 def wrapper(func):
    365     def inner_function():
    366         pass
    367 
    368     update_wrapper(inner_function, func, assigned=WRAPPER_ASSIGNMENTS)
    369     return inner_function
    370 
    371 
    372 @wrapper
    373 def wrapped():
    374     pass
    375 
    376 
    377 print('---')
    378 print(wrapped.__name__)
    379 
    380 
    381 # 10. 内置装饰器:property
    382 # 通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容,同时,会将这个函数变成另外一个装饰器
    383 class Student(object):
    384     def __init__(self, name):
    385         self.name = name
    386         self.name = None
    387 
    388     @property
    389     def age(self):
    390         return self._age
    391 
    392     @age.setter
    393     def age(self, value):
    394         if not isinstance(value, int):
    395             raise ValueError('输入不合法:年龄必须为数值!')
    396         if not 0 < value < 100:
    397             raise ValueError('输入不合法:年龄范围必须0-100')
    398         self._age = value
    399 
    400     @age.deleter
    401     def age(self):
    402         del self._age
    403 
    404 
    405 xiaoming = Student("小明")
    406 
    407 # 设置属性
    408 xiaoming.age = 25
    409 
    410 # 查询属性
    411 print(xiaoming.age)
    412 
    413 # 删除属性
    414 del xiaoming.age
    415 
    416 # 11. 其他装饰器:装饰器实战
    417 import signal
    418 
    419 
    420 class TimeoutException(Exception):
    421     def __init__(self, error='Timeout waiting for response from Cloud'):
    422         Exception.__init__(self, error)
    423 
    424 
    425 def timeout_limit(timeout_time):
    426     def wraps(func):
    427         def handler(signum, frame):
    428             raise TimeoutException()
    429 
    430         def deco(*args, **kwargs):
    431             signal.signal(signal.SIGALRM, handler)
    432             signal.alarm(timeout_time)
    433             func(*args, **kwargs)
    434             signal.alarm(0)
    435 
    436         return deco
    437 
    438     return wraps

     

  • 相关阅读:
    [转] Actor生命周期理解
    [转] Linux History(历史)命令用法 15 例
    [转] CDH6 安装文章链接收集
    [转] org.scalatest.FunSuite Scala Examples
    [转] Mock以及Mockito的使用
    关于 maven 打包直接运行的 fat jar (uber jar) 时需要包含本地文件系统第三方 jar 文件的问题
    [转] flume使用(六):后台启动及日志查看
    [转] etcd 搭建与使用
    [转] 2018年最新桌面CPU性能排行天梯图(含至强处理器)
    让 Linux grep 的输出不换行
  • 原文地址:https://www.cnblogs.com/sanduzxcvbnm/p/11466104.html
Copyright © 2020-2023  润新知