在 python 中将工厂生成的类的功能扩展到模块之外的最佳方法是什么?



我一直在阅读很多以前关于工厂函数等的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-a Calendar 类(每个实例创建自己的 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 结构。

最新更新