web-dev-qa-db-ja.com

Pythonの同じファイルに複数のクラスを含めることはできますか?

Python数年後のJavaおよびPHPの後で、Python $ ===の世界にやって来ました。言語自体は非常に単純ですが、いくつかの問題を抱えています '私の頭を包むことができない、そしてこれまで読んだ数多くのドキュメントやチュートリアルで答えを見つけることができなかったマイナーな問題。

経験豊富なPython開業医にとって、この質問はばかげているように見えるかもしれませんが、私は本当に答えが欲しいので、言語をさらに進めることができます:

JavaおよびPHP( 厳密には必須ではありませんが ))では、それぞれにclassを書き込む必要があります独自のファイル。ファイルの名前はclassのファイルがベストプラクティスです。

しかし、Python、または少なくとも私がチェックしたチュートリアルでは、同じファイルに複数のクラスがあるのはokです。

このルールは、本番環境、デプロイ可能なコードに適用されますか、それとも、教育のみのコードを簡潔にするために行われますか?

21
Olivier Malki

Pythonの同じファイルに複数のクラスを含めることはできますか?

はい。哲学的な観点からも実用的な観点からも。

Pythonでは、モジュールはメモリに一度存在する名前空間です。

次の架空のディレクトリ構造があり、ファイルごとに1つのクラスが定義されているとします。

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

これらのクラスはすべてcollectionsモジュールで使用でき、(実際には合計で25個あります) _collections_abc.py

ここにいくつかの問題があり、_collections_abc.py架空の代替ディレクトリ構造よりも優れています。

  • これらのファイルはアルファベット順にソートされています。他の方法でファイルを並べ替えることもできますが、意味的な依存関係によってファイルを並べ替える機能については知りません。 _collections_abcモジュールのソースは、依存関係によって編成されています。
  • 病理学的でない場合、モジュールとクラス定義はどちらもシングルトンであり、それぞれメモリで1回発生します。モジュールをクラスに全単射マッピングする-モジュールを冗長にする。
  • ファイルの数が増えると、(IDE簡単にする)場合を除いて)クラスをさりげなく読むことが不便になり、ツールを持たない人がアクセスしにくくなります。

名前空間と組織の観点から望ましい場合、クラスのグループを別のモジュールに分割することはできませんか?

番号。

Zen of Python から、それが成長し進化した哲学と原則を反映しています:

名前空間は非常に魅力的なアイデアの1つです。もっと多くのことをしましょう。

ただし、次のことも覚えておいてください。

ネストよりもフラットの方が適しています。

Pythonは信じられないほどクリーンで読みやすいです。それを読むことをお勧めします。個別のファイルに個別のクラスを配置すると、読み取りができなくなります。これはPythonの中心的な哲学に反します。 標準ライブラリ の構造を見てください。モジュールの大部分はパッケージではなく単一ファイルのモジュールです。慣用的なPythonコードはCPython標準libと同じスタイルで書かれていることをお伝えします。

抽象基本クラスモジュール からの実際のコードを次に示します。言語でのさまざまな抽象型の表記のリファレンスとして使用したい。

これらの各クラスには個別のファイルが必要だと思いますか?

class Hashable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            try:
                for B in C.__mro__:
                    if "__hash__" in B.__dict__:
                        if B.__dict__["__hash__"]:
                            return True
                        break
            except AttributeError:
                # Old-style class
                if getattr(C, "__hash__", None):
                    return True
        return NotImplemented


class Iterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if _hasattr(C, "__iter__"):
                return True
        return NotImplemented

Iterable.register(str)


class Iterator(Iterable):

    @abstractmethod
    def next(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if _hasattr(C, "next") and _hasattr(C, "__iter__"):
                return True
        return NotImplemented


class Sized:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if _hasattr(C, "__len__"):
                return True
        return NotImplemented


class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented


class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            if _hasattr(C, "__call__"):
                return True
        return NotImplemented

それで、彼らはそれぞれ独自のファイルを持つべきですか?

私は望みません。

これらのファイルは単なるコードではなく、Pythonのセマンティクスに関するドキュメントです。

彼らはおそらく平均で10〜20行です。別の10行のコードを表示するために、完全に別のファイルに移動する必要があるのはなぜですか?それは非常に非現実的でしょう。さらに、各ファイルにほぼ同じボイラープレートインポートがあり、コードの冗長な行が追加されます。

モジュールのリストを調べる必要がなく、これらの抽象基本クラスのすべてを見つけることができる単一のモジュールがあることを知っていることは非常に便利です。それらを互いに関連させて見ることで、私はそれらをよりよく理解することができます。イテレーターがanイテラブルであることがわかったら、ちらりと見て、イテレーターが何で構成されているかをすばやく確認できます。

非常に短いクラスが2つあることもあります。時間の経過とともに大きくなる必要がある場合でも、ファイルに残ります。成熟したモジュールには、1000行を超えるコードが含まれている場合があります。ただし、ctrl-fは簡単で、一部のIDEではファイルの概要を簡単に表示できます。そのため、ファイルのサイズが大きくても、探しているオブジェクトやメソッドにすばやく移動できます。

結論

私の方向性は、Pythonのコンテキストでは、関連する意味的に類似したクラス定義を同じファイルに保持することを好むことです。ファイルが大きくなって扱いにくくなる場合は、再編成を検討してください。

15
Aaron Hall

Pythonでアプリケーションを構築する場合、パッケージとモジュールの観点から考える必要があります。

モジュールはあなたが話しているファイルに関するものです。同じモジュール内に多数のクラスがあることは問題ありません。目的は、同じモジュール内のすべてのクラスが同じ目的/ロジックを提供することです。モジュールが長すぎる場合は、ロジックを再設計してモジュールを細分割することを検討してください。

Python Enhancement Proposals のインデックスについて時々読むことを忘れないでください。

4
user132583

これに対する本当の答えは一般的なものであり、使用する言語には依存しません。ファイル内にあるべきものは、主に、それが定義するクラスの数に依存しません。それは論理的なつながりと複雑さに依存します。限目。

したがって、高度に相互接続されている非常に小さなクラスがいくつかある場合は、それらを同じファイルにバンドルする必要があります。クラスが別のクラスに密接に接続されていない場合、または複雑すぎて別のクラスに含めることができない場合は、クラスを分割する必要があります。

そうは言っても、ファイルごとに1クラスのルールは、通常、優れたヒューリスティックです。ただし、重要な例外があります。実際には、唯一のユーザークラスの実装の詳細にすぎない小さなヘルパークラスは、通常、そのユーザークラスのファイルにバンドルする必要があります。同様に、3つのクラスがある場合はvector2vector3vector4、それらを別々のファイルに実装する理由はほとんどありません。