Pythonファクトリー関数のベストプラクティス


30

foo.pyクラスを含むファイルがあるとしますFoo

class Foo(object):
   def __init__(self, data):
      ...

ここFooで、生のソースデータから特定の方法でオブジェクトを作成する関数を追加します。Fooの静的メソッドまたは別の別の関数として配置する必要がありますか?

class Foo(object):
   def __init__(self, data):
      ...
# option 1:
   @staticmethod
   def fromSourceData(sourceData):
      return Foo(processData(sourceData))

# option 2:
def makeFoo(sourceData):
   return Foo(processData(sourceData))

ユーザーにとって便利であることがより重要かどうかはわかりません。

foo1 = foo.makeFoo(sourceData)

または、メソッドとクラスの間の明確な結合を維持することがより重要かどうか:

foo1 = foo.Foo.fromSourceData(sourceData)

回答:


45

代わりに、ファクトリ関数と3番目のオプションを選択する必要があります。クラスメソッド:

class Foo(object):
   def __init__(self, data):
      ...

   @classmethod
   def fromSourceData(klass, sourceData):
      return klass(processData(sourceData))

クラスメソッドファクトリには、継承可能であるという追加の利点があります。のサブクラスを作成できるようFooになり、継承されたファクトリメソッドはそのサブクラスのインスタンスを生成します。

関数とクラスメソッドのどちらかを選択したら、クラスメソッドを選択します。これは、関数名でこれを明示する必要なく、ファクトリーがどの種類のオブジェクトを生成するかを明確に文書化します。これは、複数のファクトリメソッドだけでなく、複数のクラスもある場合に、より明確になります。

次の2つの選択肢を比較します。

foo.Foo.fromFrobnar(...)
foo.Foo.fromSpamNEggs(...)
foo.Foo.someComputedVersion()
foo.Bar.fromFrobnar(...)
foo.Bar.someComputedVersion()

foo.createFooFromFrobnar()
foo.createFooFromSpamNEggs()
foo.createSomeComputedVersionFoo()
foo.createBarFromFrobnar()
foo.createSomeComputedVersionBar()

さらに良いことに、エンドユーザーはFooクラスのみをインポートし、すべてのファクトリメソッドが1か所にあるため、クラスを作成するさまざまな方法で使用できます。

from foo import Foo
Foo()
Foo.fromFrobnar(...)
Foo.fromSpamNEggs(...)
Foo.someComputedVersion()

stdlib datetimeモジュールはクラスメソッドファクトリを広範に使用し、APIはより明確です。


素晴らしい答え。の詳細については@classmethod初心者向けの@classmethodと@staticmethodの意味を
-nealmcb

エンドユーザー機能に重点を置いて
-drtf

1

Pythonでは、通常、ファクトリメソッドよりもクラス階層を好みます。何かのようなもの:

class Foo(object):
    def __init__(self, data):
        pass

    def method(self, param):
        pass

class SpecialFoo(Foo):
    def __init__(self, param1, param2):
        # Some processing.
        super().__init__(data)

class FromFile(Foo):
    def __init__(self, path):
        # Some processing.
        super().__init__(data)

たとえば、階層全体(およびこの階層のみ)をモジュールに配置しますfoos.py。基本クラスFooは通常、すべてのメソッド(および、インターフェース)を実装し、通常はユーザーが直接構築しません。サブクラスは、ベースコンストラクターを上書きし、構築方法を指定する手段Fooです。次にFoo、次のような適切なコンストラクターを呼び出してを作成します。

foo1 = mypackage.foos.SpecialFoo(param1, param2)
foo2 = mypackage.foos.FromFile('/path/to/foo')

静的メソッド、クラスメソッド、またはモジュールレベルの関数から構築されたファクトリーよりもエレガントで柔軟だと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.