Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 类装饰器与函数装饰器_Python_Decorator - Fatal编程技术网

Python 类装饰器与函数装饰器

Python 类装饰器与函数装饰器,python,decorator,Python,Decorator,在python中,有两种方法可以声明decorator: 基于类的 class mydecorator(object): def __init__(self, f): self.f = f def __call__(self, *k, **kw): # before f actions self.f(*k, **kw) # after f actions def mydecorator(f): def d

在python中,有两种方法可以声明decorator:

基于类的

class mydecorator(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        self.f(*k, **kw)
        # after f actions
def mydecorator(f):
    def decorator(*k, **kw):
        # before f actions
        f(*k, **kw)
        # after f actions

    return decorator          
基于功能的

class mydecorator(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        self.f(*k, **kw)
        # after f actions
def mydecorator(f):
    def decorator(*k, **kw):
        # before f actions
        f(*k, **kw)
        # after f actions

    return decorator          
这些声明之间有什么区别吗?
在哪种情况下应该使用每种方法?

事实上,没有“两种方法”。只有一种方法(定义一个可调用对象)或python中的多种方法可以创建一个可调用对象(它可以是其他对象的方法、lambda表达式的结果、“部分”对象、任何可调用的对象)

函数定义是创建可调用对象的最简单方法,作为最简单的方法,在大多数情况下可能是最好的方法。使用类可以为更复杂的情况(即使在最简单的情况下,它看起来也很优雅)清晰地编写代码,但它的作用并不明显。

不,有(不止)两种方法可以生成可调用对象。一种是定义一个函数,它显然是可调用的。另一种方法是在类中定义一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法,这将使其实例可调用。类本身是可调用的对象

decorator只不过是一个可调用对象,旨在接受函数作为其唯一参数并返回可调用的内容。以下语法:

@decorate
def some_function(...):
    ...
这只是一种稍微好一点的写作方式:

def some_function(...):
    ...

some_function = decorate(some_function)
您给出的基于类的示例不是一个接受函数并返回函数(即bog标准香草装饰器)的函数,而是一个使用可调用实例的函数初始化的类。对我来说,如果你不是把它当作一个类来使用,这有点奇怪(它有其他方法吗?它的状态是否改变?你是否制作了几个实例,这些实例具有类封装的常见行为?)。但是,正常使用修饰函数并不能区分两者之间的区别(除非它是一个特别具有侵入性的修饰器),所以,请做您觉得更自然的事情。

让我们测试一下吧

test_class = """
class mydecorator_class(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        print 'hi class'
        self.f(*k, **kw)
        print 'goodbye class'
        # after f actions

@mydecorator_class
def cls():
    print 'class'

cls()
"""

test_deco = """
def mydecorator_func(f):
    def decorator(*k, **kw):
        # before f actions
        print 'hi function'
        f(*k, **kw)
        print 'goodbye function'
        # after f actions
    return decorator

@mydecorator_func
def fun():
    print 'func'

fun()
"""

if __name__ == "__main__":
    import timeit
    r = timeit.Timer(test_class).timeit(1000)
    r2 = timeit.Timer(test_deco).timeit(1000)
    print r, r2
我得到的结果如下:0.0499339103699 0.0824959278107


这意味着deco级的速度要快2倍

如果要在装饰器中保持状态,应该使用类

例如,这不起作用

def mydecorator(f):
    x = 0 
    def decorator():
        x += 1 # x is a nonlocal name and cant be modified
        return f(x)
    return decorator 
有很多解决方法,但最简单的方法是使用类

class mydecorator(object):
    def __init__(self, f):
        self.f = f
        self.x = 0

    def __call__(self, *k, **kw):
        self.x += 1
        return f(self.x)

当您创建一个可调用函数并返回另一个可调用函数时,函数方法更简单、更便宜。有两个主要区别:


  • 函数方法自动与方法一起工作,而如果使用类方法,则必须阅读描述符并定义一个
    \uuuu get\uu
    方法
  • 类方法使保持状态更容易。您可以使用闭包,尤其是在Python3中,但通常首选类方法来保持状态
  • 此外,函数方法允许您在修改或存储原始函数后返回它

    但是,装饰器可以返回除可调用的内容以外的内容,也可以返回比可调用内容更多的内容。使用类,您可以:

  • 向修饰的可调用对象添加方法和属性,或对其实现操作(嗯-哦)
  • 创建在类中以特殊方式起作用的描述符(例如,
    classmethod
    property
  • 使用继承实现类似但不同的装饰器

  • 如果您有任何疑问,请扪心自问:您是否希望您的装饰程序返回一个与函数应该返回的功能完全相同的函数?使用返回函数的函数。您希望装饰程序返回一个自定义对象,该对象执行更多的操作还是与函数不同的操作?创建一个类并将其用作装饰器。

    很有趣,但我想它是特定于实现的。使用其他python实现(Jython、PyPy)甚至不同版本的CPython,您可能会得到完全不同的结果。另外,这可能是运行“mydecorator_func”和类定义代码的时间不同。这只是应用程序的启动时间,通常并不重要。修饰后的函数调用速度在这两种情况下可能是相同的。对于类,您可以使用描述符协议@phant0m:另一方面,对于类,您必须使用描述符协议(否则您的装饰程序将无法处理方法)。@Rosh:嗯,是的,您在这里有一个非常强大的点:)+1令人震惊的是,没有其他人注意到方法问题+我有一个非常完整的答案。晚到派对,但这可能是公认的答案。“函数方法自动与方法一起工作”,这是一个显著的区别。非常感谢。我认为最简单的方法是说
    非局部x;x+=1
    。或者这在低于3的Python版本中不起作用是的,看起来Python3中引入了
    非本地的
    。更多关于方法修饰符限制的信息可以在这里找到答案。由多个模块使用的有状态修饰符的使用取决于使用它的其他模块在程序其他地方导入的顺序,包括在完全不相关的模块中!所以我的第一条经验法则是,如果你想让装饰师保持状态,“停下来想想这是否是最好的方法”。有时当然是这样,在这种情况下,您应该使用类装饰器。