2013-02-21 79 views
22

这是关于使用python从不同形式的相同数据创建类或类型的实例的最佳实践的问题。使用类方法更好还是更好地使用单独的函数?可以说我有一个类来描述文档的大小。 (注:这是一个简单的例子,我想知道创造而不是类的实例来描述文档的大小的最佳方式的最佳途径。)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

我会抛硬币。大多数python库似乎更喜欢程序化API,但那是因为它们意味着以与代码库内部的可重用代码不同的方式使用。 – millimoose 2013-02-21 00:27:16

+4

PS,不要调用变量'bytes';这是内建类型(在2.6及更高版本中)。 – abarnert 2013-02-21 00:32:24

+3

当然,我只是意识到我在我的回答中犯了同样的错误:'Size(bytes = 20)'。不要照我的意思去做,照我说的去做。 :) – abarnert 2013-02-21 01:09:48

回答

22

首先,大多数时候你认为你需要这样的东西,你不需要;这是一个迹象表明你正在试图像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是做一个“交替构造函数” -there是示例遍布stdlib- itertools.chain.from_iterabledatetime.datetime.fromordinal惯用方式等

函数是惯用的方法来做到的“我不关心实际的课程是什么“工厂。看看,例如,内置的open函数。你知道它在3.3中返回什么吗?你关心?不。这就是为什么它是一个函数,而不是io.TextIOWrapper.open或其他。

你给出的例子看起来像是一个完全合法的用例,并且非常清楚地适合“替代构造函数”bin(如果它不适合“具有额外参数的构造函数”bin)。

+2

尽管我同意这一点,但指出另一个设计选择而不是@classmethod是为了使__init __(kilobytes = 345)等...... – 2013-02-21 00:37:55

+0

@JonClements:好点 - 尽管我认为这是真的符合第一句话,那肯定不是很清楚,所以我会编辑它。 – abarnert 2013-02-21 00:38:40

+0

是的 - 我在想这个用例:http://dpaste.com/958194/(除了它应该是n *基*咳嗽*) – 2013-02-21 00:49:30