Python装饰器是否严格实现了装饰器模式?

3

我参加了一个关于装饰器模式的会议,其中大部分提供的例子都是用Java编写的。例如,在下面的示例中,Pizza对象被两种配料装饰。

Pizza vegPizza = new ToppingsType1(new ToppingType2(new Pizza()))

在Python中,我看到装饰器被用于类似这样的场景。
@applyTopping2
@applyTopping1
makePizza():
    pass

虽然我可以看到makePizza函数被两个函数装饰,但它与Java的基于类的方法有很大不同。我的问题是,Python装饰器是否严格实现了装饰器模式,还是实现略有不同但思想相同。

PS:我不确定在哪里查找装饰器模式的标准定义。尽管维基百科提供了一个动态语言(JS)的例子,但我的问题仍然成立 :)


没有什么可以阻止Python装饰器也成为类。 - jsbueno
1
严格实现装饰器模式?是否有一个标准定义装饰器应该是什么样子的? - Constantinius
2
我认为你需要自己定义什么是“严格的装饰器模式”。(然后,我们 Python 爱好者就可以继续描述为什么严格性是无关紧要的,并且捍卫 Python 的方法 :-)) - jsbueno
1
@jsbueno 真的很有趣。无论如何,请查看以下内容:https://dev59.com/0XRB5IYBdhLWcg3wCjjO#1594484 。这是 Python 中装饰器工作原理的示例。您将会看到它是否符合您对设计模式的期望。 - Bite code
我也曾经希望有人会来支持Python方法,但可悲的是Python维基说它们不同;(不过无论如何,我不在乎,装饰器模式很好了解,但我现在只想使用Python装饰器:) :) - Bunny Rabbit
根据维基百科关于Python装饰器的文章:“尽管名称相同,但Python装饰器并不是装饰器模式的实现”。 - martineau
3个回答

2
你应该阅读这篇文章,它解释了Python的装饰器是什么。
引用:
Python中我们所说的“装饰器”并不完全等同于DecoratorPattern[...]。Python的装饰器是对Python语法的一种具体改变,使我们更方便地修改函数和方法(以及可能在将来版本中的类)。这支持更可读的应用DecoratorPattern,但也支持其他用途。
因此,你可以使用Python的装饰器来实现“经典”的装饰器模式,但你也可以用它做更多有趣的事情。
在你的情况下,它可能看起来像:
def applyTopping1(functor):
    def wrapped():
        base_pizza = functor()
        base_pizza.add("mozzarella")
        return base_pizza
    return wrapped

def applyTopping2(functor):
    def wrapped():
        base_pizza = functor()
        base_pizza.add("ham")
        return base_pizza
    return wrapped

然后你会得到一份玛格丽特比萨 :)

1

这不是一个真正的答案,只是一个有趣的例子,展示了Python相对于静态语言的优势:

def pizza_topping_decorator_factory(topping):
    def fresh_topping_decorator(pizza_function):
        def pizza_with_extra_topping(*args, **kw):
            return pizza_function(*args, **kw) + ", " + topping
        return pizza_with_extra_topping
    return fresh_topping_decorator


mozzarela = pizza_topping_decorator_factory("mozarella")
anchove = pizza_topping_decorator_factory("anchove")

@anchove
@mozzarela
def pizza():
    return "Pizza"

当将此代码粘贴到Python控制台中时:

>>> 
>>> print pizza()
Pizza, mozarella, anchove

最好不要在实际代码中使用_decorator_factory后缀以提高可读性。 - lig
@lig - shure - 上面代码中的大多数名称只是为了好玩。 - jsbueno

1
Python中的@decorator语法只是一种语法糖。
代码:
@decorator
def foo():
    pass

不过就是

def foo():
    pass

foo = decorator(foo)

最终结果取决于您希望它成为什么样子。

您可以使用类作为 decorator 并获得从该类实例化的对象,其中 foo__init__ 参数。

您可以像下面的代码一样使用带有某些参数的实例化对象作为装饰器。

class Decorator(object):
    def __init__(self, arg1, arg2):
        pass

    def __call__(self, foo):
        return foo()

@Decorator(arg1_value, arg2_value)
def foo():
    pass

只有你自己决定你的代码看起来像什么,它会做什么,而不是任何模式或来自Java世界的人:)


如果按照这种逻辑,那么在Python中,“class”关键字只是“语法糖”,对于Decorator = type("Decorator", (object,), dict(__init__=lambda s, foo: setattr(s,"f", foo), __call__=lambda s: s.f())) - jsbueno
但是,就像我们在函数中获得被调用函数的对象一样,在这里我们不会。 - Bunny Rabbit

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