1

我一直在阅读很多以前关于工厂函数等的 SO 讨论,但仍然不知道针对这种特殊情况的最佳(pythonic)方法是什么。我会预先承认我对这个问题施加了一些人为的约束,因为我希望我的解决方案能够在不修改我试图扩展的模块的情况下工作:我可以对其进行修改,但让我们假设它必须保持为-是因为我试图了解这种情况下的最佳实践。

我正在使用http://pypi.python.org/pypi/icalendar模块,该模块处理 Icalendar 规范(以下称为 ical)的解析和序列化。它将文本解析为类似字典的“组件”对象的层次结构,其中每个“组件”都是实现不同有效类型(VCALENDAR、VEVENT 等)的平凡派生类的实例,它们都被吐出来自公共父类的递归工厂:

class Component(...):
  @classmethod
  def from_ical(cls, ...)

我创建了一个扩展了 ical 'Calendar' 类的 'CalendarFile' 类,其中包括它自己的生成器函数:

class CalendarFile(Calendar):
  @classmethod
  def from_file(cls, ics):

它打开一个文件 ( ics) 并将其传递:

     instance = cls.from_ical(f.read())

它初始化并修改了一些其他的东西instance,然后返回它。问题是它instance最终成为一个Calendar对象而不是一个CalendarFile对象,尽管cls它是CalendarFile. 没有进入 ical 模块的工厂函数并在那里摆弄,有没有办法将该对象本质上“重铸”为“日历文件”?

我考虑过的替代方案(同样不修改原始模块)是:

  • 使CalendarFile类成为具有Calendar类(每个实例创建自己的Calendar对象的内部实例),但这似乎有条不紊。
  • 摆弄返回的对象,为其提供所需的方法(我知道有一个术语用于创建自定义对象,但它让我无法理解)。
  • 将附加方法变成函数,并让它们与Calendar.
  • 或者答案可能是我不应该首先尝试从模块继承,而这种类型的代码属于模块本身。

我再次试图了解“最佳”方法是什么,并了解我是否缺少任何替代方法。谢谢。

4

1 回答 1

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)

在大多数情况下,我强烈推荐这种方法;这是替代构造函数的黄金标准。

但这是一个特例。

我现在查看了源代码,我认为添加新类的最佳方法是直接编辑模块;否则,放弃继承并采取选项一(您的“拥有”选项)。不同的类都是同一个容器类的略有不同的版本——它们甚至不应该是单独的类。但是,如果您想在编写代码时添加一个新类,则必须向模块本身添加一个新类。此外,from_iter具有欺骗性的名称;它根本不是真正的构造函数。我认为它应该是一个独立的功能。它构建了一整棵链接在一起的组件树,构建各个组件的代码隐藏在对各种工厂函数的调用链中,这些工厂函数也应该是独立的函数,但不是。IMO 的大部分代码应该放在__init__对您进行子类化有用的地方,但事实并非如此。

事实上,Component甚至没有一个子类添加任何方法。通过向 的子类添加方法Calendar,您完全无视代码的实际习惯用法。我不太喜欢它的成语,但是无视那个成语,你会让情况变得更糟。如果您不想修改原始模块,那么在这里忘记继承并为您的对象提供与对象的has-a关系Calendar。不要修改__class__;建立您自己的遵循标准 OO 实践的 OO 结构。

于 2013-09-19T16:18:20.143 回答