web-dev-qa-db-ja.com

Python部分クラスと同等のものはありますか?

「新しい」スタイルのクラス(python 3.2)を使用しています)を使用して、クラスを複数のファイルに分割する方法はありますか?大きなクラス(実際には単一のクラスである必要があります)オブジェクト指向の設計の観点から、カップリングなどを検討しますが、クラスの編集を簡単にするために、いくつかのファイルに分割するのは良いことです。

38
Matthew Lund

あなたの問題が本当にエディターで大きなクラスを処理しているだけの場合、私が実際に探す最初の解決策は、問題を解決するためのより良い方法です。 2番目の解決策は、より良いエディター、できればコードの折りたたみを備えたエディターです。

つまり、クラスを複数のファイルに分割する方法はいくつかあります。 Pythonを使用すると、__init__.pyを入れてフォルダをモジュールとして使用できます。これにより、他のファイルからのインポートが可能になります。この機能は、各ソリューションで使用します。たとえば、最初にbigclassと呼ばれるフォルダ。

  1. フォルダーには、最終的にクラスを構成するさまざまな.pyファイルを配置します。それぞれに、最終的なクラスnotクラスの関数と変数の定義を含める必要があります。同じフォルダの__init__.pyに次のように書き込んで、それらをすべて結合します。

    class Bigclass(object):
    
        from classdef1 import foo, bar, baz, quux
        from classdef2 import thing1, thing2
        from classdef3 import magic, moremagic
        # unfortunately, "from classdefn import *" is an error or warning
    
        num = 42   # add more members here if you like
    

    これには、objectから直接派生した単一のクラスが作成されるという利点があり、継承グラフではこれが見栄えが良くなります。

  2. 多重継承を使用して、クラスのさまざまな部分を組み合わせることができます。個々のモジュールで、クラスの一部を使用してBigclassのクラス定義を記述します。次に、__init__.pyに次のように記述します。

    import classdef1, classdef2, classdef3
    
    class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
        num = 42   # add more members if desired
    
  3. 多重継承が問題になる場合は、can単一継承を使用します。各クラスにチェーン方式で別のクラスを継承させるだけです。複数のクラスで何も定義しない場合、順序は関係ありません。たとえば、classdef2.pyは次のようになります。

    import classdef1
    class Bigclass(classdef1.Bigclass):
         # more member defs here
    

    classdef3は、classdef2からBigclassをインポートして追加します。あなたの__init__.pyは最後のものをインポートするだけです:

    from classdef42 import Bigclass
    

私は通常、どのファイルからどのメンバーをインポートするかについてより明示的ですが、これらのソリューションのいずれでも機能するので、#1を選択します。

これらのシナリオのいずれかでクラスを使用するには、フォルダー名をモジュール名として使用して、クラスをインポートするだけです:from bigclass import Bigclass

40
kindall

次のようなデコレータでこれを行うことができます:

class Car(object):

    def start(self):
    print 'Car has started'


def extends(klass):
    def decorator(func):
      setattr(klass, func.__name__, func)
      return func
    return decorator

#this can go in a different module/file
@extends(Car)
def do_start(self):
    self.start()


#so can this
car = Car()
car.do_start()

#=> Car has started
9
Ciprian Tarta

数百行を含むクラス定義は「実際に」発生しますが(人気のあるオープンソースのPythonベースのフレームワークでいくつか見ました)、メソッドが何をしているかを熟考すれば、長さを減らすことができると思いますほとんどのクラスの管理可能なポイント。いくつかの例:

  • ほとんど同じコードが2回以上発生する場所を探します。そのコードを独自のメソッドに分割し、引数を使用して各場所から呼び出します。
  • オブジェクトの状態をまったく使用しない「プライベート」メソッドは、スタンドアロン関数としてクラスから取り出すことができます。
  • 特定の条件下でのみ呼び出されるメソッドは、それらのメソッドをサブクラスに配置する必要があることを示している場合があります。

あなたの質問に直接取り組むために、クラスの定義を分割することが可能です。 1つの方法は、クラスを定義し、それから外部関数をメソッドとして追加することによって、クラスを「モンキーパッチ」することです。もう1つは、組み込みtype関数を使用してクラスを「手動で」作成し、その名前、基本クラス、およびそのメソッドと属性を辞書に提供することです。しかし、そうしないと定義が長くなるという理由だけでこれを行うことはお勧めしません。その種の治療法は私の考えでは病気よりも悪いです。

7
wberry

私は以前に似たようなもので遊んでいました。私のユースケースは、抽象構文ツリー内のノードのクラス階層でした。 prettyprinting機能は別のprettyprint.pyファイルにありますが、クラスのメソッドとしてはまだあります。

私が試みたのは、装飾された関数を指定されたクラスの属性として配置するデコレーターを使用することでした。私の場合、これはprettyprint.pyに多数のdef prettyprint(self)が含まれ、すべてが異なる@inclass(...)で装飾されていることを意味します

これに関する問題は、サブファイルが常にインポートされていること、およびそれらがメインクラスに依存していることを確認する必要があることです。これにより、循環依存性が生じ、乱雑になる場合があります。

def inclass(kls):
    """
    Decorator that adds the decorated function
    as a method in specified class
    """
    def _(func):
        setattr(kls,func.__name__, func)
        return func
    return _

## exampe usage
class C:
    def __init__(self, d):
        self.d = d

# this would be in a separate file.
@inclass(C)
def meth(self, a):
    """Some method"""
    print "attribute: %s - argument: %s" % (self.d, a)

i = C(10)
print i.meth.__doc__
i.meth(20)
6

私はそれを使用していませんが、 このパッケージはパーシャルと呼ばれます パーシャルクラスのサポートを追加すると主張しています。

これを自分で実装する方法は他にもいくつかあるようです。

クラスの個別の部分を別々のファイルのミックスインとして実装し、それらをどこかにインポートしてサブクラス化することができます。

または、クラスの各メソッドをどこかに実装し、中央のファイルにそれらをインポートして、クラスの属性として割り当て、オブジェクト全体を作成することもできます。そのようです:

a.py:

def AFunc( self, something ):
    # Do something
    pass

b.py:

def BFunc( self, something ):
    # Do something else
    pass

c.py:

import a, b

class C:
    AFunc = a.AFunc
    BFunc = b.BFunc

本当に必要な場合は、このプロセスを自動化することもできます。モジュールaおよびbによって提供されるすべての関数をループし、Cに属性として追加します。 。それは完全に行き過ぎかもしれませんが。

それを実行する他の(おそらくより良い)方法があるかもしれませんが、それらは心に浮かんだ2つです。

3
obmarg

まず、クラスを複数のファイルに分割すると編集が簡単になることはわかりません。まともなIDEは、1つのファイルでも複数でも、メソッドを簡単に見つけることができるはずです。まともなIDEを使用していない場合、クラスを分割すると、メンテナは指定されたファイルを推測する必要がありますメソッドが入っており、簡単ではなく難しく聞こえます。

より根本的に、このクラスは大きすぎて、その重みをサポートするために特別な言語機能が必要なため、根本的に壊れているように見えます。何行のコードについて話しているのですか?ほぼ間違いなく、次のいずれかを実行することをお勧めします。

  • 重複したコードをより少数のより一般的なプリミティブにリファクタリングする
  • Karoly Horvathがコメントで提案するように、基本クラスを定義し、サブクラスで拡張します(これは、私が保証することを求めている「部分クラス」に最も近いものです)
  • このクラスの機能のさまざまな部分をカプセル化するいくつかの個別のクラスを定義し、これらの小さいクラスのインスタンスのこのクラスを構成します。
0

同じ状況に遭遇しました-クラスを2つのファイルにスライドさせたいのですが。その理由は-GUIレイアウトのパート1が必要なため、レイアウトのみで、別のファイルがすべての機能を保持しているためです。 c#のPartialクラスのようです。 1つはXAML用、もう1つは関数用です。

0
alex

これを行うPythonicの方法は、必ずしもミックスインを使用するのではなく、多重継承を介して行われることを付け加えておきます。 ___init___呼び出しでsuper().__init__(*args, **kwargs)を使用してインスタンス属性を追加し、ベースクラスに引数を渡すことができます(Raymond Hettingerによる「スーパースーパースーパー」プレゼンテーション 1 を参照)。これにより、依存関係の注入が可能になり、基本クラスの構成について考えるようになります(1つの基本クラスだけが___init___に属性を設定し、その属性を使用するすべてのクラスがそれから継承する場合に最適です)。

これには通常、基本クラス(またはこのパターン用に作成されているクラス)を制御する必要があります。

別のオプションは、___get___を介して関数を返す記述子を使用して、分離された方法で機能をクラスに追加することです。

___init_subclass___を見て、追加することもできます。クラス生成中のクラスへのメソッド(python 3.6に追加されたと思いますが、確認してください)

0
Lars