如何在 Python 中编写与 Wikipedia 中的示例不同的策略模式?
Posted
技术标签:
【中文标题】如何在 Python 中编写与 Wikipedia 中的示例不同的策略模式?【英文标题】:How to write Strategy Pattern in Python differently than example in Wikipedia? 【发布时间】:2009-06-08 08:58:40 【问题描述】:在 2009 年的 Wikipedia 策略模式条目中,有一个示例 written in 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 中使用一等函数可以更短吗?
【问题讨论】:
【参考方案1】: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 一样简洁而优雅。但是您会丢失信息;由于没有明确的接口,程序员被假定为成年人知道他们在做什么。
请注意,在 Python 中动态添加方法有 3 种方式:
我向您展示的方式。但该方法将是静态的,它不会传递“self”参数。
使用类名:
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
当然,在不能单独使用函数的情况下,仍然可以绑定到任何对象的任何其他实例,没有任何接口限制。
【讨论】:
【参考方案2】:为搜索“python strategy pattern”并登陆这里的谷歌人回答一个老问题......
这种模式在支持一等函数的语言中实际上是不存在的。您可能需要考虑在 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--the--lack-of--design-patterns-in-pyth
【讨论】:
那为什么我们在 python 中有类呢?你有时设计模式在大型项目中很有帮助,你无法摆脱它:) 工作link。在其他视频中被删除。 @vivek One 在 python 或任何其他语言中没有专门用于促进设计模式的类。很多东西都可以很好地用类来表示。但是需要类来表示,例如命令或战略模式是一个弱点。任何具有适当闭包的语言都可以不用。大型项目并不是专门针对课程的要求。许多语言根本没有类,但用于创建大型项目。当然,类是在 Python 中表达数据类型的唯一方法,因此您无法摆脱它们。 Here 是 Peter Norvig 在视频中提到的“动态语言中的设计模式”的帖子。【参考方案3】:你是对的,***的例子没有帮助。它把两件事混为一谈。
策略。
简化策略实现的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 )
这也将使用我们提供的策略函数。
【讨论】:
你用的是哪一种?你会推荐使用什么? @Arrieta:两者都用。推荐两者。它们有不同的应用。如果 Strategy 有多个方法或(以某种方式)有状态,那么您需要第一个。在更典型的情况下,第二个工作正常。如果你和很多 Java 或 C++ 程序员一起工作,你必须使用第一个,因为第二个会让他们感到困惑。【参考方案4】:为了清楚起见,我仍然会使用伪接口:
class CommunicationStrategy(object):
def execute(self, a, b):
raise NotImplementedError('execute')
class ConcreteCommunicationStrategyDuck(CommunicationStrategy):
def execute(self, a, b):
print "Quack Quack"
class ConcreteCommunicationStrategyCow(CommunicationStrategy):
def execute(self, a, b):
print "Mooo"
class ConcreteCommunicationStrategyFrog(CommunicationStrategy):
def execute(self, a, b):
print "Ribbit! Ribbit!"
【讨论】:
是模板模式,不是策略【参考方案5】:我尝试在 Python 中转换 Head First Design Pattern 的第一章(涵盖策略模式)中的“鸭子”示例:
class FlyWithRocket():
def __init__(self):
pass
def fly(self):
print 'FLying with rocket'
class FlyWithWings():
def __init__(self):
pass
def fly(self):
print 'FLying with wings'
class CantFly():
def __init__(self):
pass
def fly(self):
print 'I Cant fly'
class SuperDuck:
def __init__(self):
pass
def setFlyingBehaviour(self, fly_obj):
self.fly_obj = fly_obj
def perform_fly(self):
self.fly_obj.fly()
if __name__ == '__main__':
duck = SuperDuck()
fly_behaviour = FlyWithRocket()
#fly_behaviour = FlyWithWings()
duck.setFlyingBehaviour(fly_behaviour)
duck.perform_fly()
【讨论】:
最好在构造函数中设置飞行行为,或者至少在构造函数中设置一个默认值。你可能会忘记设置它,否则会出错。以上是关于如何在 Python 中编写与 Wikipedia 中的示例不同的策略模式?的主要内容,如果未能解决你的问题,请参考以下文章
wikipedia 维基百科 语料 获取 与 提取 处理 by python3.5