How can I use a static method as a default parameter for the strategy design pattern?
我想创建一个类,该类使用与此类似的策略设计模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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() |
这就产生了错误:
1
| NameError: name 'C' is not defined |
用strategy=default_concrete_strategy替换strategy=C.default_concrete_strategy是可行的,但默认情况下,策略实例变量将是静态方法对象,而不是可调用方法。
1
| TypeError: 'staticmethod' object is not callable |
如果我移除@staticmethod装饰器,它会工作,但是还有其他方法吗?我希望默认参数是自记录的,这样其他人就可以立即看到如何包括策略的示例。
此外,是否有更好的方法来公开策略而不是静态方法?我不认为在这里实现完整的类是有意义的。
- 策略模式在Python中基本上是无用的。因为函数是第一类对象,所以只需传递函数即可。
- @正如您所看到的,bakuriu策略是一个一流的对象函数。我认为这仍然被称为战略模式不?
- 是的,但是策略模式主要是用不允许传递函数的语言发明的。我的意思是,在类的99%的用例中,您可以直接传递函数并以较低的复杂性获得相同的结果。
- @巴库鲁,你能举一个例子说明如何做到这一点吗?
- 还与在类体中调用类StaticMethod的问题有关吗?
不可以,因为class定义尚未完成运行,所以当前命名空间中还不存在类名。
您可以直接使用函数对象:
1 2 3 4 5 6 7 8 9 10 11
| 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描述符本身不可调用)。
另一种方法是使用sentinel默认值;由于strategy的所有正常值都是静态函数,因此None在这里可以正常工作:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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描述符本身返回,而类定义完成之后。
- 或者他可以先定义default_concrete_strategy,而不进行装饰,然后按照__init__的定义进行装饰(就像2.4之前使用的装饰器一样),尽管这会很麻烦。
- @巴库鲁:没错,很容易打开。
- 如果这是唯一的办法,那就太可惜了。如果能在如何传递参数的代码中直接看到一个示例,那就太好了。@Bakuriu在构造函数中修饰一个方法会有帮助吗?
- @不,你必须把@staticmethod放在课堂上。我指的是类似于def method():pass; def __init__(self, func=method):pass; method = staticmethod(method)的东西。不过,我必须说,我更喜欢使用None作为默认值,并在__init__中检查它。
- @mtanti:没有,因为需要在类上的对象上设置描述符,而不是在实例上。
- @mtanti:这不是唯一的方法,但它是最易读的方法。另一种方法是"手工"应用修饰符;在没有@staticmethod行的情况下定义default_concrete_strategy,然后在定义__init__方法之后,使用default_concrete_strategy = staticmethod(default_concrete_strategy)。
- @巴库鲁,我不知道你能用这种方法来装饰。有趣。
- @mtanti:@expression后面跟一个类或函数定义,对于类或函数定义完成后的name_of_obj = expression(name_of_obj)来说,只不过是语法上的糖分。