如何将静态方法用作策略设计模式的默认参数?

9
我希望创建一个类,使用与以下类似的策略设计模式:
class C:

    @staticmethod
    def default_concrete_strategy():
        print("default")

    @staticmethod
    def other_concrete_strategy():
        print("other")

    def __init__(self, strategy=C.default_concrete_strategy):
        self.strategy = strategy

    def execute(self):
        self.strategy()

以下是出现的错误:

NameError: name 'C' is not defined

strategy=C.default_concrete_strategy替换为strategy=default_concrete_strategy可以正常工作,但如果保留默认设置,策略实例变量将是一个静态方法的对象而不是可调用的方法。

TypeError: 'staticmethod' object is not callable

如果我去掉@staticmethod装饰器,它将正常工作。但是,还有其他方法吗?我希望默认参数可以记录自身,以便其他人可以立即看到如何包含策略的示例。
此外,是否有更好的方法来公开策略,而不是作为静态方法?我认为在这里实现完整的类没有意义。

1
策略模式在Python中大多数情况下是无用的。因为你可以将函数作为一等对象传递,所以你只需传递函数即可。 - Bakuriu
@Bakuriu,正如你所看到的,这个策略是一个一流的对象函数。我认为这仍然被称为策略模式,对吧? - mtanti
是的,但策略模式大多数是在不允许函数传递的语言中发明的。我的意思是,在您的类的99%的用例中,您可以直接传递函数并以更少的复杂性获得相同的结果。 - Bakuriu
@Bakuriu,您能否举个例子说明如何实现这个功能? - mtanti
与问题在类主体内调用类的staticmethod?有关。 - martineau
1个回答

16

不行,因为 class 定义还没有完成运行,所以当前命名空间中还不存在类名。

你可以直接使用函数对象:

class C:    
    @staticmethod
    def default_concrete_strategy():
        print("default")

    @staticmethod
    def other_concrete_strategy():
        print("other")

    def __init__(self, strategy=default_concrete_strategy.__func__):
        self.strategy = strategy

C在方法被定义时还不存在,因此您通过本地名称引用default_concrete_strategy.__func__展开staticmethod描述符以访问底层原始函数(staticmethod描述符本身不可调用)。

另一种方法是使用一个特殊的默认值; None 在这里可以正常工作,因为所有正常的strategy值都是静态函数:

class C:    
    @staticmethod
    def default_concrete_strategy():
        print("default")

    @staticmethod
    def other_concrete_strategy():
        print("other")

    def __init__(self, strategy=None):
        if strategy is None:
            strategy = self.default_concrete_strategy
        self.strategy = strategy

由于它从self检索default_concrete_strategy,因此调用了描述符协议,并且在类定义完成后才由staticmethod描述符本身返回(未绑定的)函数。


@Bakuriu:没错;直接解包容易得多。 - Martijn Pieters
1
@mtanti 不行,你必须把 @staticmethod 放在类里面。我的意思是像这样 def method():pass; def __init__(self, func=method):pass; method = staticmethod(method)。不过我必须说,我更喜欢在 __init__ 中使用 None 作为默认值,并检查它。 - Bakuriu
@mtanti:不可以,因为描述符需要在类上的对象上设置,而不是在实例上设置。 - Martijn Pieters
1
@mtanti:这不是唯一的方法,但它是最易读的方法。另一种选择是手动应用装饰器;在没有@staticmethod行的情况下定义default_concrete_strategy,然后在定义__init__方法之后,使用default_concrete_strategy = staticmethod(default_concrete_strategy) - Martijn Pieters
@Bakuriu 我不知道你可以这样装饰一个方法。有意思。 - mtanti
显示剩余5条评论

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