我一直在阅读许多以前关于工厂功能等的SO讨论,但仍然不知道对于这种特殊情况最好的(pythonic)方法是什么。我先前承认,我正在对这个问题施加一些人为限制,因为我希望我的解决方案能够在不修改我试图扩展的模块的情况下工作:我可以对它进行修改,但我们假设它必须保持为 - 因为我试图了解这种情况下的最佳做法。
我正在使用http://pypi.python.org/pypi/icalendar模块,该模块处理从Icalendar规范(以下称为ical)的解析和序列化。它将文本解析为类似字典的“组件”对象的层次结构,其中每个“组件”是实现不同有效类型(VCALENDAR,VEVENT等)的普通派生类的实例,它们都是由来自普通父类的递归工厂:
class Component(...):
@classmethod
def from_ical(cls, ...)
我创建了一个'CalendarFile'类,它扩展了'Calendar'类,包括它自己的生成器函数:
class CalendarFile(Calendar):
@classmethod
def from_file(cls, ics):
打开一个文件(ics
)并传递给它:
instance = cls.from_ical(f.read())
它初始化并修改instance
中的其他一些内容然后返回它。问题是instance
最终成为Calendar
对象而不是CalendarFile
对象,尽管cls
为CalendarFile
。没有进入ical模块的工厂功能并在那里摆弄,是否有任何方法可以将该对象“重铸”为“CalendarFile”?
我考虑过的替代方案(同样没有修改原始模块):
CalendarFile
类成为一个Calendar
类(每个实例)创建自己的Calendar
对象的内部实例,但这看起来有条不紊。
Calendar
的实例一起使用。
我再次尝试理解“最佳”方法是什么,并且还要了解我是否缺少任何替代方法。感谢。
答案 0 :(得分:0)
通常,我希望将一个替代构造函数定义为类方法,只需调用类的标准构造函数,将它接收的参数转换为标准构造函数的有效参数。
>>> class Toy(object):
... def __init__(self, x):
... self.x = abs(x)
... def __repr__(self):
... return 'Toy({})'.format(self.x)
... @classmethod
... def from_string(cls, s):
... return cls(int(s))
...
>>> Toy.from_string('5')
Toy(5)
在大多数情况下,我强烈推荐这样的方法;这是替代构造函数的黄金标准。
但这是一个特例。
我现在查看了源代码,我认为添加新类的最佳方法是直接编辑模块;否则,废品继承并选择一个(您的“has-a”选项)。不同的类都是同一容器类的略微差异化版本 - 它们实际上甚至不应该是单独的类。但是如果你想在编写代码的习语中添加一个新类,你必须向模块本身添加一个新类。此外,from_iter
被欺骗性地命名;它根本不是一个构造函数。我认为它应该是一个独立的功能。它构建了一个连接在一起的整个组件树,构建各个组件的代码隐藏在对各种工厂函数的调用链中,这些函数也应该是独立的函数,但不是。 IMO中的大部分代码应该生活在__init__
中,在那里对子类化很有用,但事实并非如此。
实际上,Component
的子类都没有添加任何方法。通过向Calendar
的子类添加方法,您完全忽略了代码的实际习惯用法。我不太喜欢它的成语,但是无视这个成语,你会让它变得更糟。如果您不想修改原始模块,那么请忘记此处的继承,并为您的对象提供与Calendar
个对象的has-a关系。不要修改__class__
;建立遵循标准OO实践的自己的 OO结构。