作为模块中的函数,还是作为其创建的类中的方法,哪个更适合pythonic?
我有一些Python代码,这些代码基于从iCalendar文件解析的VEvent对象创建了Calendar对象。
日历对象只有一种方法,可以在事件被解析时添加事件。
现在,我想创建一个工厂函数,该函数从文件对象,路径或者URL创建日历。
我一直在使用iCalendar python模块,该模块直接在返回其实例的Class上将工厂函数实现为class方法。
cal = icalendar.Calendar.from_string(data)
从我对Java的了解很少,这是Java代码中的一种常见模式,尽管我似乎在与实际实例化实例的类不同的类上找到了更多的对工厂方法的引用。
问题是,这也被认为是Pythonic吗?还是仅将模块级方法创建为工厂函数被认为更具Python风格?
解决方案
不考虑我们在某处阅读的某种模式的深奥差异,现在想在任何地方使用,例如工厂模式,这是很Python的。
大多数时候,我们会认为@static方法是一种解决方案,最好使用模块函数,除非我们在一个模块中填充多个类,并且每个类对同一接口有不同的实现,那么最好使用@静态方法
最终,通过@static方法或者模块功能创建实例的天气几乎没有什么不同。
我可能会使用类的初始值设定项(init),因为python中较受接受的"模式"之一是类的工厂是类初始化。
工厂模式有其优点和缺点。但是,选择一种创建实例的方法通常对代码几乎没有实际影响。
恕我直言,模块级方法是一种更干净的解决方案。它隐藏在Python模块系统后面,该模块系统为其赋予了唯一的名称空间前缀,通常使用"工厂模式"来实现。
[笔记。在分离"日历"事件集合和"事件"日历中的单个事件时要非常谨慎。在问题中,似乎可能有些混乱。]
工厂设计模式有很多变化。
- 独立的便利功能(例如,calendarMaker(data))
- 一个单独的类(例如CalendarParser),用于构建目标类(Calendar)。
- 类级别的方法(例如Calendar.from_string)。
这些有不同的目的。都是Pythonic,问题是"你是什么意思?"和"可能会发生什么变化?"意义就是一切;改变很重要。
便利功能是Pythonic。像Java这样的语言不能具有自由浮动的功能。我们必须在一个类中包装一个孤独的函数。 Python使我们可以拥有一个孤独的函数,而无需增加类的开销。当构造函数没有状态更改,替代策略或者先前操作的任何内存时,该函数才有意义。
有时,人们会定义一个类,然后提供一个便捷函数,该函数构成该类的实例,设置状态和策略以及任何其他配置的常用参数,然后调用该类的单个相关方法。这既使我们具有类的有状态性,又为独立函数提供了灵活性。
使用了类级别的方法模式,但是它有局限性。第一,它被迫依赖于类级别的变量。由于这些可能会造成混淆,因此当我们需要添加功能(例如有状态性或者替代策略)时,作为静态方法的复杂构造函数会遇到问题。请确保永远不要扩展静态方法。
第二,它或者多或者少与其余的类方法和属性无关。这种from_string
只是Calendar对象的许多替代编码之一。我们可能会一直使用from_xml,from_JSON
,from_YAML`。这些都与日历的功能或者功能无关。这些方法都是关于如何编码日历以进行传输的。
在成熟的Python库中,我们将看到工厂与它们创建的对象是分开的。编码(如字符串,XML,JSON,YAML)受到或者多或者少的随机变化的影响。但是,基本的东西很少改变。
将两个问题分开。尽量使编码和表示远离状态和行为。
静态方法很少具有价值,但类方法可能有用。这取决于我们希望类和工厂函数实际执行的操作。
模块中的工厂函数将始终创建"正确"类型的实例(这里的"正确"始终是"日历"类,但我们也可以使其依赖于创建该实例的内容)在......之外。)
如果希望使其不依赖于数据而是依赖于调用的类,请使用类方法。类方法就像静态方法,可以在没有实例的情况下在类上调用它,但是它接收作为第一个参数调用的类。这使我们可以实际创建该类的实例,该实例可以是原始类的子类。 dict.fromkeys()是一个类方法的示例,它从键列表和单个值(默认为None)创建一个dict。因为它是一个类方法,当我们将dict子类化时,完全免费获得了" fromkeys"方法。这是一个如何自己编写dict.fromkeys()的示例:
class dict_with_fromkeys(dict): @classmethod def fromkeys(cls, keys, value=None): self = cls() for key in keys: self[key] = value return self