对象的工厂方法 - 最佳实践?
这是一个关于在Python中如何从不同形式的数据创建类或类型实例的最佳实践的问题。是使用类方法更好,还是使用一个单独的函数更好呢?假设我有一个类,用来描述文档的大小。(注意:这只是一个例子。我想知道创建类实例的最佳方法,而不是描述文档大小的最佳方法。)
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
def __init__(self, bits):
self._bits = bits
@property
def bits(self):
return float(self._bits)
@property
def bytes(self):
return self.bits / self.BYTE
@property
def kilobits(self):
return self.bits / self.KILO
@property
def kilobytes(self):
return self.bytes / self.KILO
@property
def megabits(self):
return self.kilobits / self.KILO
@property
def megabytes(self):
return self.kilobytes / self.KILO
我的 __init__
方法接受一个以比特为单位的大小值(就是比特,我想保持这个方式),但假设我有一个以字节为单位的大小值,我想创建这个类的实例。使用类方法更好,还是使用一个单独的函数更好呢?
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
@classmethod
def from_bytes(cls, bytes):
bits = bytes * cls.BYTE
return cls(bits)
或者
def create_instance_from_bytes(bytes):
bits = bytes * Size.BYTE
return Size(bits)
这可能看起来不是个问题,也许这两种方法都是有效的,但每次我需要实现类似的功能时,我都会考虑这个问题。很长一段时间,我更喜欢使用类方法,因为我喜欢将类和工厂方法结合在一起的组织好处。而且,使用类方法可以创建任何子类的实例,这样更符合面向对象的原则。另一方面,我的一个朋友曾经说过:“当你不确定时,就做标准库里的做法”,但我还没有在标准库中找到这样的例子。
1 个回答
首先,大多数时候你觉得需要像这样的东西,其实并不需要;这说明你在试图把Python当成Java来用。解决的方法是退一步,问问自己为什么需要工厂函数。
很多时候,最简单的做法就是用一个构造函数,里面可以有默认值、可选参数或者关键字参数。即使在Java中你不会这样写的情况,甚至在C++或ObjC中重载构造函数感觉不对的情况,在Python中看起来也很自然。例如,size = Size(bytes=20)
,或者size = Size(20, Size.BYTES)
,这些写法都很合理。再比如,一个Bytes(20)
类,继承自Size
,只不过重载了__init__
,这也看起来很正常。而且这些定义起来都很简单:
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):
或者:
BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
不过,有时候你确实需要工厂函数。那么,这时候该怎么办呢?其实,通常有两种东西会被统称为“工厂”。
@classmethod
是实现“备用构造函数”的标准写法——在标准库中有很多例子,比如itertools.chain.from_iterable
,datetime.datetime.fromordinal
等等。
而函数则是实现“我不在乎实际类是什么”的工厂的标准写法。比如,看看内置的open
函数。你知道它在3.3版本中返回什么吗?你在乎吗?并不在乎。这就是为什么它是一个函数,而不是io.TextIOWrapper.open
之类的。
你给出的例子看起来是一个完全合理的用例,明显适合“备用构造函数”的分类(如果它不适合“带额外参数的构造函数”的分类的话)。