Python中的装饰器(语法糖)

本文深入探讨了Python装饰器的概念、实现步骤及多种应用场景,包括时间性能测试、参数处理和带参数装饰器的使用。此外,介绍了Python内置装饰器如staticmethod、classmethod和property,并通过实例展示了如何使用functools模块的wraps装饰器来保留原始函数的特殊属性。

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志,性能测试,事务处理等。装饰器是为了解决这类问题的绝佳设计,抽离出大量函数中与函数本身无关的雷同代码并继续重用。装饰器的作用就是为了已经存在的对象添加额外的功能。

装饰器是一个函数,用来包装函数的函数。

第一步,一个简单的函数

#!/usr/bin/python
def myfunc():
    print("myfunc() called")
myfunc()
执行结果:

myfunc() called
第二步,我想测试这个函数的时间性能。

#!/usr/bin/python
import time
def myfunc():
    start = time.clock()
    print("myfunc() called")
    end = time.clock()
    print ('time used is ',end-start)
myfunc()
执行结果:

myfunc() called
time used is  0.009169672696094571

可是,如果我还有一个函数叫myfunc2,我也想测试这个函数的时间开销。那么我是否需要复制一份代码呢,只是该一个函数名,这样太累赘了。

将测试时间开销的函数独立出来,这样就可以将业务和数据分离,代码可以很好的重用了。

第三步,分离业务逻辑和测试数据

#!/usr/bin/python
import time
def myfunc():
    print("myfunc() called")

def timeit(func):
    start = time.clock()
    func()
    end = time.clock()
    print ('time used is ',end-start)
timeit(myfunc)

执行结果:

myfunc() called
time used is  0.01921845603380964

当我想在调用myfunc()的时候,就已经有测试时间性能的功能,而不需要额外的修改代码,即调用myfunc的效果相当于timeit(myfunc)

第四步,最少的改动

#!/usr/bin/python
import time
def myfunc():
    print("myfunc() called")

def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

myfunc = timeit(myfunc)
myfunc()
执行结果为:

myfunc() called
time used is  0.0110723728723904

是否还能在简洁?可以的,使用语法糖@。

第五步,使用语法糖@降低字符输入量。

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc():
    print("myfunc() called")
myfunc()
执行结果为:

myfunc() called
time used is  0.013483512431895571

其作用和第四步的一样,只是用语法糖有一个限制,就是在装饰函数要在被装饰函数之前定义,不然就找不到装饰函数。如实例中所是,timeit函数要在myfunc函数之前定义。

第六步,对确定数量参数的函数进行装饰

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper(a, b):
        start = time.clock()
        func(a, b)
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))
myfunc(1,2)
测试结果为:

myfunc( 1, 2) called
time used is  0.021083260107009123

第七步,对参数熟练不确定的函数进行装饰

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.clock()
        func(*args, **kwargs)
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))

@timeit
def myfunc2(a, b, c):
    print("myfunc2( %s, %s, %s) called"%(a,b,c))
    
myfunc(1,2)
myfunc2(1,2,3)
运行结果为:

myfunc( 1, 2) called
time used is  0.005005443617309361
myfunc2( 1, 2, 3) called
time used is  0.0040197501857303815

第七步,让装饰器带参数

#!/usr/bin/python
import time

def timeit(arg):
    def _wrapper(func):
        def wrapper(*args, **kwargs):
            start = time.clock()
            func(*args, **kwargs)
            end = time.clock()
            print ('time used is ',end-start, arg)
        return wrapper
    return _wrapper

@timeit("first time")
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))

@timeit("second time")
def myfunc2(a, b, c):
    print("myfunc( %s, %s, %s) called"%(a,b,c))
    
myfunc(1,2)
myfunc2(1,2,3)

测试结果:

myfunc( 1, 2) called
time used is  0.01058011828321179 first time
myfunc( 1, 2, 3) called
time used is  0.005932319138874216 second time

第八步,python内置的装饰器,staticmethod、classmethod和property


 functools模块提供了两个装饰器。这个模块是Python 2.5后新增的。

    wraps(wrapped[, assigned][, updated]): 
    这是一个很有用的装饰器。函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名bar会变成包装函数的名字wrap,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。


http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html

http://my.oschina.net/shniu/blog/215365?p=1

http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html





### Python 装饰器语法糖使用方法 Python 中的装饰器通过 `@` 符号来简化函数或类的修饰过程。这种语法糖不仅提高了代码的可读性和简洁度,而且使得功能扩展更加直观。 #### 基本装饰器示例 当想要增强某个函数的功能而不改变其原有定义时,可以利用装饰器。下面展示了一个简单装饰器的应用: ```python def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() ``` 这段代码展示了如何使用 `@` 语法糖将 `say_hello()` 函数传递给 `my_decorator()` 进行封装[^3]。 #### 参数化的装饰器 如果希望装饰器能够接收参数,则可以通过嵌套一层闭包结构实现更为灵活的功能定制: ```python import functools def repeat(num_times): def decorator_repeat(func): @functools.wraps(func) def wrapper(*args, **kwargs): for _ in range(num_times): value = func(*args, **kwargs) return value return wrapper return decorator_repeat @repeat(num_times=4) def greet(name): print(f"Hello {name}") greet("Alice") ``` 此例子中,`repeat()` 是一个带有参数的高阶装饰器,它可以控制目标函数被执行的具体次数[^1]。 #### 类装饰器应用 除了普通的函数装饰器外,Python 同样允许使用类作为装饰器对象。此类需重写 `__call__` 方法以便能够在调用时生效: ```python class CountCalls: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print(f"This is call #{self.num_calls} of {self.func.__name__!r}") return self.func(*args, **kwargs) @CountCalls def say_goodbye(person="world"): print(f"Goodbye, {person}") say_goodbye() say_goodbye("Bob") ``` 上述代码片段说明了怎样构建并运用基于类形式的计数型装饰器[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值