2013-02-21 10 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

+4

PS、変数 'bytes'を呼び出さないでください。これは組み込み型(2.6以降)です。 – abarnert

+3

もちろん、私は私の答えで同じミスを犯したことに気づいた。サイズ(バイト= 20)。私のようにしないでください、私が言うように。 :) – abarnert

答えて

22

まず、あなたはこのようなものが必要だと思うほとんどの時間、あなたはしないでください。それはあなたがJavaのようにPythonを扱おうとしている兆候です。その解決策は、なぜ戻って工場が必要なのかを尋ねることです。

しばしば、デフォルトの/オプション/キーワード引数を持つコンストラクタを持つだけです。たとえオーバーロードされたコンストラクタがC++やObjCで間違っているようなケースであっても、Javaでそのように書くことは決してできない場合でも、Pythonでは完全に自然に見えるかもしれません。たとえば、size = Size(bytes=20)、またはsize = Size(20, Size.BYTES)が適切です。そのため、Sizeから継承し、__init__というオーバーロードを絶対に加えないクラスBytes(20)が妥当と思われます。時々、あなたは必要性のファクトリ関数を行う

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

しかし:

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

または:そして、これらを定義するのは簡単です。だから、あなたは何をしていますか?まあ、しばしば一緒に「工場」にまとめられる2種類のものがあります。

@classmethod「が代替コンストラクタ」を行うための慣用的な方法である全てstdlib- itertools.chain.from_iterable上の実施例は、等、datetime.datetime.fromordinal、-thereある

機能は、「私はない行うための慣用的な方法であります実際のクラスが「工場」であることを気にしてください。例えば、内蔵のopen機能を見てください。それが3.3で何を返すか知っていますか?手入れする?いいえ。だからこそ、それは関数なので、io.TextIOWrapper.openなどではありません。

あなたの与えられた例は完全に合法的なユースケースのように思えますが、「代替コンストラクタ」ビンにはっきりと当てはまります(余分な引数を持つコンストラクタに収まらない場合)。

+2

私はこれに同意しますが、@classmethodの代わりに別のデザインを選んだのは、 '__init __(kilobytes = 345)'を作ることだけです。 –

+0

@JonClements:良い点 - 最初の文に合っていますが、それは明らかに書かれているようにはっきりしていないので、私はそれを編集します。 – abarnert

+0

Yup - 私はこのユースケースのために何を考えていたのですか:http://dpaste.com/958194/(それ以外はn * base * cough *でなければなりません) –

関連する問題