关于python:对象的工厂方法-最佳实践?

Factory method for objects - best practice?

这是一个关于使用Python从同一数据的不同形式创建类或类型实例的最佳实践的问题。使用类方法更好还是使用单独的函数更好?假设我有一个用来描述文档大小的类。(注:这只是一个例子。我想知道创建类实例的最佳方法,而不是描述文档大小的最佳方法。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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__方法采用以位(位和仅位)表示的大小值,我希望保持这种方式,但假设我有一个以字节为单位的大小值,我希望创建一个类的实例。使用类方法更好还是使用单独的函数更好?

1
2
3
4
5
6
7
8
9
10
11
12
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)

1
2
3
def create_instance_from_bytes(bytes):
    bits = bytes * Size.BYTE
    return Size(bits)

这似乎不是一个问题,也许这两个例子都是有效的,但每次我需要实现类似的东西时,我都会考虑到这一点。很长一段时间以来,我更喜欢课堂教学法,因为我喜欢把课堂教学法和工厂教学法结合起来的组织效益。此外,使用类方法可以保留创建任何子类的实例的能力,因此它更面向对象。另一方面,一位朋友曾经说"当有疑问时,做标准库所做的事情",我还没有在标准库中找到这样一个例子。


首先,大多数时候你认为你需要这样的东西,但你没有;这是一个标志,你试图像Python那样对待Python,解决的办法是退后一步,问问你为什么需要一个工厂。

通常,要做的最简单的事情就是使用带有默认/可选/关键字参数的构造函数。甚至在Java中,即使是超载的构造函数会在C++或Objc中感觉错误的情况下,也可能在Python中看起来完全是自然的。例如,size = Size(bytes=20)size = Size(20, Size.BYTES)看起来是合理的。就这点而言,从Size继承的Bytes(20)类,除了__init__重载之外,什么也没有添加,看起来是合理的。这些定义很简单:

1
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):

或:

1
2
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_iterabledatetime.datetime.fromordinal等都有示例。

函数是执行"我不关心实际类是什么"工厂的惯用方法。看看,例如,内置的open功能。你知道它在3.3中会返回什么吗?你在乎吗?不。这就是为什么它是一个函数,而不是io.TextIOWrapper.open或其他什么函数。

您给出的示例似乎是一个完全合法的用例,并且非常清楚地适合"备用构造函数"bin(如果它不适合"带有额外参数的构造函数"bin)。