说明
我想知道我展示的代码是否可以作为Python中基于策略的设计的示例。另外,我想知道您是否见过python模块使用类似于这个例子的东西,以便我从中学习?在
最近,我需要一个类似于policy-based design的东西作为我正在工作的python模块。在
我在这个论坛里发现了一个similar question,但它已经关闭,我无法添加评论。在
让我总结一下我对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中都存在。
推迟继承:将主机类和策略类之间的继承定义推迟到主机类的用户实例化
^{pr2}$
# 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 NewClass;# in Python
NewClass = HostClass(Policy1Class, Policy2Class, ...)
使用mixin作为替代方案
我刚刚从其中一条评论中了解到,可以使用Python mixins作为创建新类的替代方法。在这种方法下,模块的代码分为基于类和mixin类。然后,您可以从基类创建新的类,并使用mixin类实现对基类提供的行为或接口的修改。在
在这个answer之后,我能够使用mixin编写我的第一个示例。在
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和基于的类都不能推断出哪个是mixin或based类的角色。这是因为它们都是新类的父类,比如PrintHelloWorld或SaveHelloWorld。在
在我尝试进行基于策略的设计时,宿主类总是有可能知道哪些类是它们的策略,因为它们是它的父类。这个特性允许我利用Python的方法解析顺序(methodresolutionorder,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++中使用的一些元编程技术,它能使computation at compile-time。原则上,在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 sequence递归映射为层次结构连接类的列表。M的第一个元素是同一性类的第五代后代。这意味着对M[0]实例的run(.)成员函数的调用将返回序列的第5个值。同样地,我调用run(.)成员函数M[1]将返回序列的第4个值。在
这只是如何利用pythonmro的一个极端示例,如图所示在前面的章节中。这只是一个玩具的例子,我没能想出一个比分形更好的主意,仅仅因为涉及到递归。请不要在家里这样做!在