问题描述
在策略模式的2009年Wikipedia条目中,有一个示例用php .
编写大多数其他代码示例都可以做:
a = Context.new(StrategyA.new) a.execute #=> Doing the task the normal way b = Context.new(StrategyB.new) b.execute #=> Doing the task alternatively c = Context.new(StrategyC.new) c.execute #=> Doing the task even more alternative
在Python代码中,使用提交按钮使用了不同的技术.我想知道如果python代码也像其他代码示例一样进行.
更新:使用Python中的一流功能?
推荐答案
python中的示例与其他示例没有什么不同.模拟PHP脚本:
class StrategyExample: def __init__(self, func=None): if func: self.execute = func def execute(self): print("Original execution") def executeReplacement1(): print("Strategy 1") def executeReplacement2(): print("Strategy 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat2 = StrategyExample(executeReplacement2) strat0.execute() strat1.execute() strat2.execute()
输出:
Original execution Strategy 1 Strategy 2
主要区别是:
- 您不需要编写任何其他类或实现任何接口.
- 相反,您可以传递将绑定到您想要的方法的函数参考.
- 该函数仍然可以单独使用,如果您要使用(可以将if func == None模式使用).
- 的确,与python一样,它像往常一样干净而又优雅.但是您丢失了信息;由于没有明确的界面,因此将程序员视为成年人知道他们在做什么.
请注意,有3种方法可以在Python中动态添加一种方法:
-
我向您展示的方式.但是该方法将是静态的,它不会通过"自我"参数.
-
使用类名:
StrategyExample.execute = func
在这里,所有实例都将获得func作为execute方法,并将self作为参数传递.
-
仅与实例结合(使用types模块):
strat0.execute = types.MethodType(executeReplacement1, strat0)
或使用Python 2,还需要更改实例的类:
strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)
这将将新方法绑定到strat0,并且仅将strat0绑定为第一个示例.但是start0.execute()将获得self作为参数.
如果您需要使用对功能中当前实例的引用,则将第一个和最后一个方法组合在一起.如果您不这样做:
class StrategyExample: def __init__(self, func=None): self.name = "Strategy Example 0" if func: self.execute = func def execute(self): print(self.name) def executeReplacement1(): print(self.name + " from execute 1") def executeReplacement2(): print(self.name + " from execute 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat1.name = "Strategy Example 1" strat2 = StrategyExample(executeReplacement2) strat2.name = "Strategy Example 2" strat0.execute() strat1.execute() strat2.execute()
您会得到:
Traceback (most recent call last): File "test.py", line 28, in <module> strat1.execute() File "test.py", line 13, in executeReplacement1 print self.name + " from execute 1" NameError: global name 'self' is not defined
因此,适当的代码将是:
import sys import types if sys.version_info[0] > 2: # Python 3+ create_bound_method = types.MethodType else: def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) class StrategyExample: def __init__(self, func=None): self.name = "Strategy Example 0" if func: self.execute = create_bound_method(func, self) def execute(self): print(self.name) def executeReplacement1(self): print(self.name + " from execute 1") def executeReplacement2(self): print(self.name + " from execute 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat1.name = "Strategy Example 1" strat2 = StrategyExample(executeReplacement2) strat2.name = "Strategy Example 2" strat0.execute() strat1.execute() strat2.execute()
这将输出预期的结果:
Strategy Example 0 Strategy Example 1 from execute 1 Strategy Example 2 from execute 2
当然,在这种情况下,函数不能再单独使用,但仍然可以绑定到任何对象的任何其他实例,而无需任何接口限制.
其他推荐答案
为搜索" Python策略模式"并降落在这里的Google人回答一个旧问题...
这种模式实际上不存在支持头等函数的语言.您可能需要考虑在Python中利用此功能:
def strategy_add(a, b): return a + b def strategy_minus(a, b): return a - b solver = strategy_add print solver(1, 2) solver = strategy_minus print solver(2, 1)
这种方法非常干净.
另外,请务必查看Joe Gregorio的Pycon 2009谈论Python和设计模式(或缺乏其设计): http://pyvideo.org/video/146/pycon-2009------lack-ob-design-patterns-in -pyth
其他推荐答案
您是对的,Wikipedia示例没有帮助.它混合了两件事.
-
策略.
-
python的特征,简化了策略的实现. "无需明确实施此模式"的说明是不正确的.您通常需要实现策略,但是Python通过允许您在函数周围使用类包装的开销的情况下使用功能来简化这一点.
首先,策略.
class AUsefulThing( object ): def __init__( self, aStrategicAlternative ): self.howToDoX = aStrategicAlternative def doX( self, someArg ): self. howToDoX.theAPImethod( someArg, self ) class StrategicAlternative( object ): pass class AlternativeOne( StrategicAlternative ): def theAPIMethod( self, someArg, theUsefulThing ): pass # an implementation class AlternativeTwo( StrategicAlternative ): def theAPImethod( self, someArg, theUsefulThing ): pass # another implementation
现在您可以做这样的事情.
t = AUsefulThing( AlternativeOne() ) t.doX( arg )
它将使用我们创建的策略对象.
第二,python替代方案.
class AUsefulThing( object ): def __init__( self, aStrategyFunction ): self.howToDoX = aStrategyFunction def doX( self, someArg ): self.howToDoX( someArg, self ) def strategyFunctionOne( someArg, theUsefulThing ): pass # an implementation def strategyFunctionTwo( someArg, theUsefulThing ): pass # another implementation
我们可以做到这一点.
t= AUsefulThing( strategyFunctionOne ) t.doX( anArg )
这也将使用我们提供的策略函数.
问题描述
In the 2009 Wikipedia entry for the Strategy Pattern, there's a example written in PHP.
Most other code samples do something like:
a = Context.new(StrategyA.new) a.execute #=> Doing the task the normal way b = Context.new(StrategyB.new) b.execute #=> Doing the task alternatively c = Context.new(StrategyC.new) c.execute #=> Doing the task even more alternative
In the Python code a different technique is used with a Submit button. I wonder what the Python code will look like if it also did it the way the other code samples do.
Update: Can it be shorter using first-class functions in Python?
推荐答案
The example in Python is not so different of the others. To mock the PHP script:
class StrategyExample: def __init__(self, func=None): if func: self.execute = func def execute(self): print("Original execution") def executeReplacement1(): print("Strategy 1") def executeReplacement2(): print("Strategy 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat2 = StrategyExample(executeReplacement2) strat0.execute() strat1.execute() strat2.execute()
Output:
Original execution Strategy 1 Strategy 2
The main differences are:
- You don't need to write any other class or implement any interface.
- Instead you can pass a function reference that will be bound to the method you want.
- The functions can still be used separately, and the original object can have a default behavior if you want to (the if func == None pattern can be used for that).
- Indeed, it's clean short and elegant as usual with Python. But you lose information; with no explicit interface, the programmer is assumed as an adult to know what they are doing.
Note that there are 3 ways to dynamically add a method in Python:
The way I've shown you. But the method will be static, it won't get the "self" argument passed.
Using the class name:
StrategyExample.execute = func
Here, all the instance will get func as the execute method, and will get self passed as an argument.
Binding to an instance only (using the types module):
strat0.execute = types.MethodType(executeReplacement1, strat0)
or with Python 2, the class of the instance being changed is also required:
strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)
This will bind the new method to strat0, and only strat0, like with the first example. But start0.execute() will get self passed as an argument.
If you need to use a reference to the current instance in the function, then you would combine the first and the last method. If you do not:
class StrategyExample: def __init__(self, func=None): self.name = "Strategy Example 0" if func: self.execute = func def execute(self): print(self.name) def executeReplacement1(): print(self.name + " from execute 1") def executeReplacement2(): print(self.name + " from execute 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat1.name = "Strategy Example 1" strat2 = StrategyExample(executeReplacement2) strat2.name = "Strategy Example 2" strat0.execute() strat1.execute() strat2.execute()
You will get:
Traceback (most recent call last): File "test.py", line 28, in <module> strat1.execute() File "test.py", line 13, in executeReplacement1 print self.name + " from execute 1" NameError: global name 'self' is not defined
So the proper code would be:
import sys import types if sys.version_info[0] > 2: # Python 3+ create_bound_method = types.MethodType else: def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) class StrategyExample: def __init__(self, func=None): self.name = "Strategy Example 0" if func: self.execute = create_bound_method(func, self) def execute(self): print(self.name) def executeReplacement1(self): print(self.name + " from execute 1") def executeReplacement2(self): print(self.name + " from execute 2") if __name__ == "__main__": strat0 = StrategyExample() strat1 = StrategyExample(executeReplacement1) strat1.name = "Strategy Example 1" strat2 = StrategyExample(executeReplacement2) strat2.name = "Strategy Example 2" strat0.execute() strat1.execute() strat2.execute()
This will output the expected result:
Strategy Example 0 Strategy Example 1 from execute 1 Strategy Example 2 from execute 2
Of course, in the case the functions cannot be used stand alone anymore, but can still be bound to any other instance of any object, without any interface limitation.
其他推荐答案
Answering an old question for the Googlers who searched "python strategy pattern" and landed here...
This pattern is practically non-existent in languages that support first class functions. You may want to consider taking advantage of this feature in Python:
def strategy_add(a, b): return a + b def strategy_minus(a, b): return a - b solver = strategy_add print solver(1, 2) solver = strategy_minus print solver(2, 1)
This approach is very clean and simple.
Also, be sure to check out Joe Gregorio's PyCon 2009 talk about Python and design patterns (or lack thereof): http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth
其他推荐答案
You're right, the wikipedia example isn't helpful. It conflates two things.
Strategy.
Features of Python that simplify the implementation of Strategy. The "there's no need to implement this pattern explicitly" statement is incorrect. You often need to implement Strategy, but Python simplifies this by allowing you to use a function without the overhead of a class wrapper around a function.
First, Strategy.
class AUsefulThing( object ): def __init__( self, aStrategicAlternative ): self.howToDoX = aStrategicAlternative def doX( self, someArg ): self. howToDoX.theAPImethod( someArg, self ) class StrategicAlternative( object ): pass class AlternativeOne( StrategicAlternative ): def theAPIMethod( self, someArg, theUsefulThing ): pass # an implementation class AlternativeTwo( StrategicAlternative ): def theAPImethod( self, someArg, theUsefulThing ): pass # another implementation
Now you can do things like this.
t = AUsefulThing( AlternativeOne() ) t.doX( arg )
And it will use the strategy object we created.
Second, Python alternatives.
class AUsefulThing( object ): def __init__( self, aStrategyFunction ): self.howToDoX = aStrategyFunction def doX( self, someArg ): self.howToDoX( someArg, self ) def strategyFunctionOne( someArg, theUsefulThing ): pass # an implementation def strategyFunctionTwo( someArg, theUsefulThing ): pass # another implementation
We can do this.
t= AUsefulThing( strategyFunctionOne ) t.doX( anArg )
This will also use a strategy function we provided.