Python装饰器和装饰器模式有什么区别?

31

“Python装饰器”和“装饰器模式”有什么区别?

何时应该使用Python装饰器,何时应该使用装饰器模式?

我正在寻找Python装饰器和装饰器模式实现相同的例子。

@AcceptedAnswer

我知道Jakob Bowyer的答案是有效的。然而,正是Srikar的答案让我明白了为什么。

在阅读了Srikar的回答并学习了给定的资源后,我编写了这个例子,以便我可以可视化和理解Python装饰器和装饰器模式。

我必须不同意Srikar的“Python装饰器不是装饰器模式的一种实现”。根据我的学习,我坚信Python装饰器是装饰器模式的一种实现。只是不是经典方式。

另外,我需要补充的是,尽管Srikar说“Python装饰器在定义时向函数和方法添加功能”,但你也可以轻松地在运行时使用Python装饰器

然而,我仍然将Srikar的答案标记为已接受,因为它帮助我理解了Python中装饰器模式的实现

"""
Testing Python decorators against the decorator pattern
"""
def function(string):
    return string

def decorator(wrapped):
    def wrap(string):
        # Assume that this is something useful
        return wrapped(string.upper())
    return wrap

def method_decorator(wrapped):
    def wrap(instance, string):
        # Assume that this is something useful
        return wrapped(instance, string.upper())
    return wrap

@decorator
def decorated_function(string):
    print('! '.join(string.split(' ')))

class Class(object):
    def __init__(self):
        pass
    def something_useful(self, string):
        return string

class Decorator(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped
    def something_useful(self, string):
        string = '! '.join(string.split(' '))
        return self.wrapped().something_useful(string)

    @method_decorator
    def decorated_and_useful(self,string):
        return self.something_useful(string)


if __name__ == '__main__':
    string = 'Lorem ipsum dolor sit amet.'
    print(function(string))                  # Plain function
    print(decorator(function)(string))       # Python decorator at run time
    print(decorated_function(string))        # Python decorator at definition time
    a = Class()
    print(a.something_useful(string))        # Plain method
    b = Decorator(Class)
    print(b.something_useful(string))        # Decorator pattern
    print(b.decorated_and_useful(string))    # Python decorator decorated the decorator pattern

2
@Srikar 是正确的。 这里还有一个Stack Overflow的问题,你可能会觉得很有趣! - mac
1
@Srikar,我不能接受那些没有解决问题描述的答案,抱歉,但是我的问题中提出的大多数解决方案都不起作用。 - seler
1
据维基百科关于Python装饰器的文章所述:“尽管名字相同,但Python装饰器并不是装饰器模式的实现”。它进一步解释了原因。 - martineau
感谢@apcelent。这可能有助于理解此主题的任何人;) - seler
Srikar的答案是完全正确的。装饰器不适用于运行时修改。我的答案增加了一些细微差别,但他的答案是最好的。 https://www.codementor.io/@sheena/advanced-use-python-decorators-class-function-du107nxsv - rfportilla
2个回答

37

装饰器模式 - 在面向对象编程中,装饰器模式是一种设计模式,它允许在运行时动态地为现有对象添加行为。装饰器模式可以用于在设计时提供必要的基础工作后,在运行时独立于同一类的其他实例,扩展(装饰)某个对象的功能。

Python中的装饰器 - 尽管名称相似,Python装饰器并不是装饰器模式的实现。装饰器模式是一种用于静态类型的面向对象编程语言,以允许在运行时向对象添加功能的设计模式;Python装饰器在定义时向函数和方法添加功能,因此比装饰器模式类更高级。

装饰器模式本身在Python中极易实现,因为该语言是鸭子类型的,所以通常不被视为装饰器模式。因此,在Python中,装饰器是任何可调用的Python对象,用于修改函数、方法或类的定义。

我希望我已经澄清了差异。如果您还没有完全理解,请查看以下链接。最后您将会明白得更加清晰:

  • Python装饰器简单入门

  • 来源1来源2


  • 装饰器模式本身在Python中很容易实现,因为它是一种鸭子类型语言,所以通常不被认为是这样。因此,在Python中,装饰器是任何可调用的Python对象,用于修改函数、方法或类定义。在Python中,函数是对象;Python装饰器(使用@语法)扩展了该函数对象的功能,独立于其他函数(即内置的function类型的实例)。那怎么不是装饰器模式的实现呢? - Karl Knechtel

    4
    区别在于:
    (a) Python 装饰器与现有方法相结合,改变该方法的行为。例子:
    @modifyBehavior
    def original(myString):
        print myString
    

    原始行为被覆盖。您不能使用它来添加新功能。

    (b) 装饰器模式是关于多态性的。在上面的示例代码中,Decorator.something_useful的行为被覆盖了。原始方法丢失了。这并不是真正的装饰器模式。您应该寻求增强或添加功能,而不是替换方法。您应确保a.something_useful(string)返回与b.something_useful(string)相同的内容。实际上,在装饰器模式中,您通常会替换原始对象。这就是我的意思:

    class Class(object):
        def __init__(self):
            pass
        def something_useful(self, string):
            return string
    
    class Decorator(object):
        def __init__(self, wrapped):
            self._wrapped = wrapped
        def withUnderscores(self, string):
            return '_'.join(string.split(' '))
        def __getattr__(self, name):
            return getattr(self._wrapped, name)
    
    
    if __name__ == '__main__':
        string = 'Lorem ipsum dolor sit amet.'
        obj = Class()
        print('Original: ', obj.something_useful(string))
        #This has no underscore function.  Use decorator to add.
        obj = Decorator(obj)
        print('Replaced spaces: ', obj.withUnderscores(string))
        print('Original still works: ', obj.something_useful(string))
    

    您可以使用多个装饰器来增加功能。这样,您只需在需要时添加所需的内容即可。更多阅读:GoF


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接