python 修饰器 教程_python修饰器教程

这篇文章是对于python中修饰器(decorator)的简单介绍和应用举例,如果需要深究,不妨移步exhaustive articles about decorator。在python中,所有的东西都是对象,即便是函数也不例外。这意味着函数也是可以被赋值给一个变量的,也是可以作为参数传递给另外一个函数的,也是可以作为另一个函数的返回值的。这些性质是如此的美妙,导致有着一大堆奇妙的用法。下面给一个例子:

def outer_function():

print("1. This is outer function!")

def inner_function():

print("2. This is inner function, inside outer function!")

print("3. This is outside inner function, inside outer function!")

return inner_function()

# call the function

outer_function()

# output

1. This is outer function!

3. This is outside inner function, inside outer function!

2. This is inner function, inside outer function!

在上面的运行过程中,我们可以发现,内部函数inner_function的输出是最后的(也就是第二号),这个过程紧接着发生在inner_function被返回后,在outer_function的结尾。

python的修饰器是可以接受函数在参数,并且返回另一个函数作为返回值的一种函数。作为一个修饰器,我们最基本的用法就是将函数作为一个参数传入,并且修饰器内的内部函数inner_function的签名必须要实现最主要的功能,也就是修饰功能(这里描述得不清楚,我们看后面的例子)。

作为函数的修饰器

修饰器可以作为函数的修饰器,我们现在不妨写一个简单的函数修饰器作为例子,我们将会编写一个修饰器,它可以测量一个函数的运行时间。让我们不妨先想一下,如果不用修饰器我们会怎么实现这个功能呢?一般来说应该是:

import time

begin = time.clock()

ret = func(*arg, **kwargs)

end = time.clock()

print(end-begin)

然而这种方法很不灵活,每次都需要自己手动添加开始时间和结束时间,非常麻烦,如果利用修饰器,则可以优雅地完成这个任务,如:

import time

def timetest(input_func):

def timed(*args, **kwargs):

begin = time.clock()

result = input_func(*args, **kwargs)

end = time.clock()

print(end-begin)

return result

return timed

@timetest

def foobar(*args, **kwargs):

time.sleep(0.3)

print('test world')

foobar(["hello, world"], foo=2, bar=5)

# outputs

# test world

# 0.0016730000000000356

我们将函数foobar传入名为timetest的修饰器中,在这个修饰器中,函数foobar是被引用为变量input_func,然后结果就是,input_func的运行结果将会作为返回值进行返回,而在其之前之后就可以添加一些你需要的功能,比如测量时间了。

在修饰器的名字前面前缀上@符号,就可以调用修饰器修饰你的函数了,你的修饰器函数名需要和你编写的修饰器名字一样,然后修饰器下面的函数将会被作为需要被传入的函数,作为参数传入修饰器。这个过程称之为修饰。

方法修饰器

方法是类内部的“函数”,和函数不太一样的是,他和类密切相关。方法修饰器允许你通过修饰器重写类属性,而不需要找到被调用的那个函数,显式地进行修改。

def method_decorator(method):

def inner(city_instance):

if city_instance.name == "SFO":

print("Its a cool place to live in.")

else:

method(city_instance)

return inner

class City(object):

def __init__(self, name):

self.name = name

@method_decorator

def print_test(self):

print(self.name)

p1 = City("SFO")

p1.print_test()

# output

# Its a cool place to live in.

在上面的代码片段中,我们修饰了类方法print_test,其中,如果城市实体的名字不是”SFO”的话,method_decorator将打印出城市的名字。试想,如果不用修饰器的话,如果在原先的打印城市名字的方法中添加判断是否是“SFO”的功能,那么你必须找到这个print_test方法,然后显式地添加这个功能,如果以后的开发迭代过程中你还需要更改,那么你又需要再次找到这个方法,并且再次更改,这个是一个很麻烦的事情,因此好好利用修饰器吧,他能让你的代码变得优雅。

基于类的修饰器

如果你想要创建一个可调用的object,其可以返回另一个可调用的object,那么函数修饰器是一个很棒的方法。如果你想要返回值是一个函数,你也应该优先考虑函数修饰器。然而,如果你想要修饰器返回一个可定制的object,其可以完成一些不同于函数所能完成的事情,这个情况下,一个类修饰器也许应该优先被考虑。(换句话说,也就是这个修饰器函数可能比较复杂,不能或者很难用一个简单的函数表示,这个时候你可以用类这个数据形式表示修饰器。)

在一个类中,你可以添加方法和属性,用这些去修饰可调用对象,或者完成在其上面(指的是可调用对象)的操作。

class decoclass(object):

def __init__(self, f):

self.f = f

def __call__(self, *args, **kwargs):

# before f actions

print('decorator initialised')

self.f(*args, **kwargs)

print('decorator terminated')

# after f actions

@decoclass

def klass():

print('class')

klass()

# output

# decorator initialised

# class

# decorator terminated

这样,我们可以让修饰器变得更加的复杂和灵活,可以用更复杂的修饰器完成更加美妙的功能。

链式修饰器

修饰器的链式调用类似于多继承,其可以用于构造类。其实,为了修饰一个函数,你想要用多少修饰器都是没问题的,你只需要把他们一个接一个“链接”起来就行了。我们看一下例子:

def makebold(f):

return lambda: "" + f() + ""

def makeitalic(f):

return lambda: "" + f() + ""

@makebold

@makeitalic

def say():

return "Hello"

print(say())

# output

# Hello

你需要牢记在心的是,修饰器的链接顺序是很关键的,也就是说链式修饰器是对顺序敏感的,当你层叠修饰器的时候,他们开始层叠的顺序是从下至上,也就是最下层的修饰器是最优先运行的,最上层的最后。

functools工具和包裹(wrap)

当我们使用修饰器的时候,我们其实是在用一个函数去替代另一个函数。

def decorator(func):

"""decorator docstring"""

def inner_function(*args, **kwargs):

"""inner function docstring """

print(func.__name__ + "was called")

return func(*args, **kwargs)

return inner_function

@decorator

def foobar(x):

"""foobar docstring"""

return x**2

如果我们尝试打印出函数foobar的名字和docstring的话,我们会发现:

print(foobar.__name__)

print(foobar.__doc__)

# output

# inner_function

# inner function docstring

我们发现,函数foobar其实是被函数inner_function替代掉了,这个意味着我们丢失了关于我们传入修饰器(也就是被修饰函数)的信息,这在一些应用中是不利于整体系统架设的。而functools.wraps就是为了解决这个问题而被创造出来的。它可以接管被修饰函数,并且在修饰器中添加被修饰函数的名字,docstring和其他信息的情况下传递被修饰体到修饰器中。让我们看一下使用修饰器而不损失被修饰函数信息的例子:

from functools import wraps

def wrapped_decorator(func):

"""wrapped decorator docstring"""

@wraps(func)

def inner_function(*args, **kwargs):

"""inner function docstring """

print(func.__name__ + "was called")

return func(*args, **kwargs)

return inner_function

@wrapped_decorator

def foobar(x):

"""foobar docstring"""

return x**2

print(foobar.__name__)

print(foobar.__doc__)

# output

# foobar

# foobar docstring

以上的实现保留了被修饰函数的信息,可以更为灵活的被使用。

带参数的修饰器

带参数的函数修饰器

from functools import wraps

def decorator(arg1, arg2):

def inner_function(function):

@wraps(function)

def wrapper(*args, **kwargs):

print("Arguements passed to decorator %s and %s" % (arg1, arg2))

function(*args, **kwargs)

return wrapper

return inner_function

@decorator("arg1", "arg2")

def print_args(*args):

for each in args:

print(each)

print_args(1, 2, 3)

# output

# Arguements passed to decorator arg1 and arg2

# 1

# 2

# 3

带参数的基于类的修饰器

class ClassDecorator(object):

def __init__(self, arg1, arg2):

print("Arguements passed to decorator %s and %s" % (arg1, arg2))

self.arg1 = arg1

self.arg2 = arg2

def __call__(self, foo, *args, **kwargs):

def inner_func(*args, **kwargs):

print("Args passed inside decorated function .%s and %s" % (self.arg1, self.arg2))

return foo(*args, **kwargs)

return inner_func

@ClassDecorator("arg1", "arg2")

def print_args(*args):

for arg in args:

print(arg)

print_args(1, 2, 3)

# output

# Arguements passed to decorator arg1 and arg2

# Args passed inside decorated function .arg1 and arg2

# 1

# 2

# 3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值