描述
我想知道我展示的代码是否可以被视为 Python 中基于策略的设计的示例。另外,我想知道您是否见过使用类似此示例的 python 模块,以便我可以向它们学习?
最近,我需要为我正在研究的 python 模块进行基于策略的设计之类的东西。
我在这个论坛上发现了一个类似的问题,但它已关闭,我无法添加评论。
让我总结一下我对 Python 中这种方法的期望。
- 模块类分为策略类和主机类。
- 策略类通过继承实现对宿主类的行为或接口的修改。
- 用户通过提供一组策略类从宿主类实例化一个新类。
这是我的玩具示例:
class PrintOutput:
"""Implement print output policy."""
def _output(self, message):
print(message)
class SaveOutput:
"""Implement save output policy."""
def set_filename(self, filename):
self.filename = filename
def _output(self, message):
with open(self.filename, 'w') as file:
file.write(message)
def HelloWorld(Output=PrintOutput):
"""Creates a host class."""
class _(Output):
"""Implements class affected by the policy."""
def run(self):
"""Print message."""
self._output('Hello world!')
return _
PrintHelloWorld = HelloWorld()
hw = PrintHelloWorld()
hw.run() # print "Hello World!"
SaveHelloWorld = HelloWorld(
Output=SaveOutput
)
hw = SaveHelloWorld()
hw.set_filename('output.txt')
hw.run() # save "Hello World!" in output.txt
在示例中,我希望所有类都已在模块中定义。用户只需要使用符合他或她要求的输出策略来实例化HelloWorld类。
设计成分
基本的设计成分是
多重继承:存在于 C++ 和 Python 中。
推迟继承:推迟主机类和策略类之间的继承定义,直到用户实例化主机类
// in C++ using templates
template<typename Policy1, typename Policy2, ...>
class HostClass: public Policy1, public Policy2, ... {
// Implement host class.
};
# in Python with a factory with arguments
def HostClass(Policy1, Policy2=Default2, ...):
class _(Policy1, Policy2, ...):
"""Implements class affected by the policies."""
return _
- 类实例化
// in C++
typedef HostClass<Policy1Class, Policy2Class, ...> NewClass;
# in Python
NewClass = HostClass(Policy1Class, Policy2Class, ...)
使用 mixins 作为替代
我刚刚从其中一条评论中了解到,可以使用Python mixins作为创建新类的替代方法。在这种方法下,模块的代码分为基础类和混合类。然后,您可以从基类创建新类,并使用 mixin 类来实现对基类提供的行为或接口的修改。
按照这个答案,我能够使用 mixins 编写我的第一个示例。
class PrintOutput:
"""Implement print output mixin."""
def _output(self, message):
print(message)
class SaveOutput:
"""Implement save output mixin."""
def set_filename(self, filename):
self.filename = filename
def _output(self, message):
with open(self.filename, 'w') as file:
file.write(message)
class HelloWorld:
"""Creates a host class."""
def run(self):
"""Print message."""
self._output('Hello world!')
class PrintHelloWorld(PrintOutput, HelloWorld):
pass
hw = PrintHelloWorld()
hw.run() # print "Hello World!"
class SaveHelloWorld(SaveOutput, HelloWorld):
pass
hw = SaveHelloWorld()
hw.set_filename('output.txt')
hw.run() # save "Hello World!" in output.txt
方法之间的区别
mixin 和我之前的示例之间的主要区别在于类之间的继承层次结构。mixin 和 based classes 都不能推断出哪个是 mixin 或 based class 的角色。这是因为它们都是PrintHelloWorld或SaveHelloWorld等新类的父类。
在我尝试进行基于策略的设计时,宿主类总是可以知道哪些类是他们的策略,因为它们是它的父类。此功能允许我利用 Python 的方法解析顺序 (MRO) 在主机类和策略类之间创建组合。这些组合是使用实例化的主机类作为策略来实例化另一个主机类的结果,请参见下面的示例。
class InputMessage:
"""Generate the message."""
def run(self):
return 'hello world'
def AddPrefix(Input):
"""Add a prefix."""
class _(Input):
def set_prefix(self, prefix):
self._prefix = prefix
def run(self):
return self._prefix + super().run()
return _
def AddSuffix(Input):
"""Add a suffix."""
class _(Input):
def set_suffix(self, suffix):
self._suffix = suffix
def run(self):
return super().run() + self._suffix
return _
def PrintOutput(Input):
"""Print message."""
class _(Input):
def run(self):
print(super().run())
return _
PrintPrefixSuffixMessage = PrintOutput(
AddSuffix(AddPrefix(InputMessage))
)
message = PrintPrefixSuffixMessage()
message.set_prefix('Victor says: ')
message.set_suffix(' and goodbye!')
message.run()
我不确定这种差异在这两种方法之间是否有任何实际意义。目前我只是想了解可以用它们表达什么。
走下递归的兔子洞
我想指出,可以在混合中添加递归。这让人想起 C++ 中使用的一些元编程技术,这些技术可以在编译时进行计算。这原则上在作为解释语言的 Python 中没有应用。请耐心等待下面的下一个不切实际的例子。
class Identity:
def run(self, z):
return z
def MandelbrotSeq(Base, n):
def Mandelbrot(Base, n):
if n == 0:
return Base
class _(Base):
def run(self, z):
z = super().run(z)
return z**2 + self.c
return Mandelbrot(_, n-1)
return Mandelbrot(Base, n).__mro__
M = MandelbrotSeq(Identity, 5)
m = M[0]()
m.c = 1
# print 677 the value at 5th iteration for c=1
print(m.run(0))
m = M[1]()
m.c = 1
# print 26 the value at 4th iteration for c=1
print(m.run(0))
m = M[2]()
m.c = 1
# print 5 the value at 3th iteration for c=1
print(m.run(0))
MandelbrotSeq工厂将Mandelbrot序列递归地映射为层次连接类的列表。M的第一个元素指的是Identity类的第五代后代。这意味着调用M [0] 实例的run(.)成员函数将返回序列的第 5 个值。同样,我调用M [1] 实例的run(.) 成员函数将返回序列的第 4 个值。
这只是如何利用 Python MRO 的一个极端示例,如前面部分所示。这只是一个玩具示例,仅仅因为涉及递归,我无法想出比分形之类的更好的主意。请不要在家里这样做!