这是一个关于使用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)
OR
def create_instance_from_bytes(bytes):
bits = bytes * Size.BYTE
return Size(bits)
这似乎不是一个问题,也许这两个例子都是有效的,但每次我需要实现这样的事情时我都会考虑它。很长一段时间,我更喜欢类方法方法,因为我喜欢将类和工厂方法绑在一起的组织好处。此外,使用类方法保留了创建任何子类的实例的能力,因此它更加面向对象。另一方面,一位朋友曾说过“当有疑问时,做标准库所做的事情”,我还没有在标准库中找到这样的例子。
答案 0 :(得分:27)
首先,大多数时候你认为你需要这样的东西,你不需要;这表明你正在尝试像Java一样对待Python,而解决方案就是退后一步,问你为什么需要工厂。
通常,最简单的方法是使用带有默认/可选/关键字参数的构造函数。即使你在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
是执行“替代构造函数”的惯用方法 - stdlib遍布示例 - itertools.chain.from_iterable
,datetime.datetime.fromordinal
等。
一个函数是做“我不关心实际类是什么”工厂的惯用方法。例如,查看内置的open
函数。你知道它在3.3中的回报吗?你关心?不。这就是为什么它是一个函数,而不是io.TextIOWrapper.open
或其他什么。
您给出的示例似乎是一个完全合法的用例,非常清楚地适用于“备用构造函数”bin(如果它不适合“带有额外参数的构造函数”bin)。