我一直在阅读很多以前关于工厂函数等的SO讨论,但仍然不知道对于这种特殊情况的最佳(pythonic)方法是什么。我预先承认,我对这个问题施加了一个人为的约束,因为我希望我的解决方案在不修改我试图扩展的模块的情况下工作:我可以对它进行修改,但让我们假设它必须保持原样,因为我试图了解在这种情况下的最佳实践。
我正在使用 http://pypi.python.org/pypi/icalendar 模块,该模块处理从 Icalendar 规范(以下简称 ical)的解析和序列化。它将文本解析为类似字典的"组件"对象的层次结构,其中每个"组件"都是实现不同有效类型(VCALENDAR,VEVENT等)的简单派生类的实例,并且它们都由递归工厂从公共父类中吐出:
class Component(...):
@classmethod
def from_ical(cls, ...)
我创建了一个"日历文件"类来扩展"日历"类,其中包括它自己的生成器函数:
class CalendarFile(Calendar):
@classmethod
def from_file(cls, ics):
打开一个文件(ics
)并传递它:
instance = cls.from_ical(f.read())
它初始化并修改instance
中的其他一些内容,然后返回它。问题是instance
最终成为Calendar
对象而不是CalendarFile
对象,尽管cls
CalendarFile
。除了进入 ical 模块的工厂功能并在那里摆弄之外,有没有办法将该对象本质上"重新转换"为"日历文件"?
我考虑过的替代方案(同样不修改原始模块)是:
- 使
CalendarFile
类成为 has-aCalendar
类(每个实例创建自己的Calendar
对象的内部实例),但这似乎有条不紊。 - 摆弄返回的对象以为其提供所需的方法(我知道有一个术语用于创建自定义对象,但它逃脱了我)。
- 将其他方法转换为函数,并让它们与
Calendar
实例一起使用。 - 或者也许答案是我不应该首先尝试从模块进行子类化,这种类型的代码属于模块本身。
试图了解"最佳"方法是什么,并了解我是否缺少任何替代方案。谢谢。
通常,我希望定义为类方法的替代构造函数简单地调用类的标准构造函数,将其收到的参数转换为标准构造函数的有效参数。
>>> 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
对象的有关系。不要修改__class__
;建立您自己的遵循标准 OO 实践的 OO 结构。