如何在 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

Swift 中的值与引用类型,以 Wikipedia 实现堆的排列算法为例

在 Python 中将 Wikipedia 表刮到 CSV

使用 Python 获取 Wikipedia 文章

PNG CRC 是如何精确计算的?

如何检查点是否在线下?