物体的工厂方法 - 最佳实践?

Yan*_*ani 23 python coding-style decorator factory-method class-method

这是一个关于使用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
Run Code Online (Sandbox Code Playgroud)

我的__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)
Run Code Online (Sandbox Code Playgroud)

要么

def create_instance_from_bytes(bytes):
    bits = bytes * Size.BYTE
    return Size(bits)
Run Code Online (Sandbox Code Playgroud)

这可能看起来不是一个问题,也许两个例子都是有效的,但每当我需要实现这样的事情时,我都会考虑它.很长一段时间,我更喜欢类方法方法,因为我喜欢将类和工厂方法绑在一起的组织好处.此外,使用类方法保留了创建任何子类的实例的能力,因此它更加面向对象.另一方面,一位朋友曾经说过"当有疑问时,做标准库所做的事情",我还没有在标准库中找到这样的例子.

aba*_*ert 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):
Run Code Online (Sandbox Code Playgroud)

要么:

BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
Run Code Online (Sandbox Code Playgroud)

但是,有时你确实需要工厂功能.那么,那你做什么?嗯,有两种常常被混为"工厂"的东西.

@classmethod是做一个"交替构造函数" -there是示例遍布stdlib-惯用方式itertools.chain.from_iterable,datetime.datetime.fromordinal等等.

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

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

  • 虽然我同意这一点 - 只是指出另一个设计选择而不是@classmethod - 是为了使`__init __(kilobytes = 345)等等...... (2认同)