9

私は基本クラスのコンストラクターをファクトリとして使用しており、このコンストラクター/ファクトリのクラスを変更して適切なクラスを選択しています-このアプローチはPythonの優れた実践ですか、それともより洗練された方法がありますか?

メタクラスに関するヘルプを読み込もうとしましたが、大きな成功はありませんでした。

これが私がしていることの例です。

class Project(object):
  "Base class and factory."
  def __init__(self, url):
      if is_url_local(url):
        self.__class__ = ProjectLocal
      else:
        self.__class__ = ProjectRemote
      self.url = url

class ProjectLocal(Project):
  def do_something(self):
    # do the stuff locally in the dir pointed by self.url

class ProjectRemote(Project):
  def do_something(self):
    # do the stuff communicating with remote server pointed by self.url

このコードがあれば、基本クラスProjectを介してProjectLocal/ProjectRemoteのインスタンスを作成できます。

project = Project('http://example.com')
project.do_something()

別の方法は、URLに基​​づいてクラスオブジェクトを返すファブリック関数を使用することです。コードは次のようになります。

def project_factory(url):
      if is_url_local(url):
        return ProjectLocal(url)
      else:
        return ProjectRemote(url)

project = project_factory(url)
project.do_something()

私の最初のアプローチは好みの問題ですか、それともいくつかの隠れた落とし穴がありますか?

4

6 に答える 6

19

これにはメタクラスは必要ありません。__new__メソッドを見てみましょう。これにより、初期化だけでなく、オブジェクトの作成を制御できるようになり、選択したオブジェクトを返すことができます。

class Project(object):
  "Base class and factory."
  def __new__(cls, url):
    if is_url_local(url):
       return super(Project, cls).__new__(ProjectLocal, url) 
    else:
       return super(Project, cls).__new__(ProjectRemote, url) 

  def __init__(self, url):
    self.url = url
于 2009-02-13T12:49:18.293 に答える
13

私はファクトリ関数のアプローチに固執します。これは非常に標準的な python であり、読みやすく理解しやすいものです。ディスクリミネーター関数と結果のマップをクラスに渡すなど、いくつかの方法でより多くのオプションを処理するために、より一般的にすることができます。

最初の例が機能する場合、それは設計というより運によるものです。__init__サブクラスで を定義し たい場合はどうしますか?

于 2009-02-13T10:15:37.570 に答える
3

次のリンクが役立つ場合があります。 http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html#factory http://code.activestate.com/recipes/86900/

さらに、新しいスタイル クラスを使用しているため、ファクトリ関数として __new__ を使用する (基本クラスではなく、別のクラスを使用する方がよい) ことが通常行われます (私の知る限り)。

ファクトリ関数は一般的に単純です(他の人がすでに投稿しているように)

さらに、あなたが行った方法で __class__ 属性を設定するのは良い考えではありません。

回答とリンクが役立つことを願っています。

ではごきげんよう。

于 2009-02-13T10:26:50.930 に答える
2

ええ、@ scooterXL が述べたように、その場合はファクトリ関数が最善のアプローチですが、クラスメソッドとしてのファクトリのケースに注目したいと思います。

次のクラス階層を検討してください。

class Base(object):

    def __init__(self, config):
        """ Initialize Base object with config as dict."""
        self.config = config

    @classmethod
    def from_file(cls, filename):
        config = read_and_parse_file_with_config(filename)
        return cls(filename)

class ExtendedBase(Base):

    def behaviour(self):
        pass # do something specific to ExtendedBase

これで、config dict と config ファイルからベース オブジェクトを作成できます。

>>> Base({"k": "v"})
>>> Base.from_file("/etc/base/base.conf")

また、無料で ExtendedBase を使用して同じことを行うこともできます。

>>> ExtendedBase({"k": "v"})
>>> ExtendedBase.from_file("/etc/extended/extended.conf")

したがって、このクラスメソッド ファクトリは、補助コンストラクタと見なすこともできます。

于 2010-06-23T08:53:37.463 に答える
1

私は通常、これを行うために別のファクトリークラスを持っています。このようにして、メタクラスや割り当てを使用する必要はありませんself.__class__

また、どのクラスを作成できるかについての知識をファクトリに入れないようにしています。むしろ、モジュールのインポート中に、使用可能なすべてのクラスをファクトリに登録します。クラスと、このクラスをファクトリに選択するタイミングに関するいくつかの情報 (これは、名前、正規表現、または呼び出し可能オブジェクト (登録クラスのクラス メソッドなど) である可能性があります)。

私にとっては非常にうまく機能し、カプセル化や情報の隠蔽なども実装しています。

于 2009-02-13T10:19:40.277 に答える
0

ファクトリ関数を使用する 2 番目のアプローチは、基本クラスの実装をそのサブクラスに依存させるよりもずっとクリーンだと思います。

于 2009-02-13T10:14:23.127 に答える