Pythonで同じファイルに複数のクラスを入れても大丈夫ですか?


18

私は、長年のJavaとPHPの後、新たにPythonの世界に来ています。言語自体はかなり単純ですが、頭をかき回すことのできない「マイナーな」問題に苦労しています。 。

経験豊富なPythonの実践者にとって、この質問はばかげているように思えるかもしれませんが、言語をさらに進めるために、私は本当にそれに対する答えが欲しいです。

JavaおよびPHP(厳密には必須ではありませんが)では、ベストプラクティスとしてclassファイル名を使用して、それぞれを独自のファイルに書き込むことが期待されていますclass

しかし、Python、または少なくとも私がチェックしたチュートリアルでは、同じファイルに複数のクラスを含めることは問題ありません。

このルールは、本番環境、展開準備の整ったコード、または教育専用コードの簡潔さのためだけに行われますか?

回答:


13

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がない限り)。ツールのない人がアクセスしにくくなります。

名前空間と組織の観点から望ましいと思う場合、クラスのグループを異なるモジュールに分割することを防ぎますか?

番号。

PythonZenから、Pythonの成長と発展の哲学と原則を反映しています。

名前空間は素晴らしいアイデアの1つです-それらをもっとやってみましょう!

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

ネストはフラットよりも優れています。

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

以下は、抽象基本クラスmoduleの実際のコードです。私は、言語のさまざまな抽象型を示すためのリファレンスとして使用するのが好きです。

これらのクラスのそれぞれが個別のファイルを必要とすると言いますか?

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行のコードを表示するために、完全に別のファイルに移動する必要があるのはなぜですか?それは非常に非現実的です。さらに、各ファイルにほぼ同一のボイラープレートインポートがあり、コードの冗長な行が追加されます。

モジュールのリストを調べる代わりに、これらの抽象基本クラスのすべてを見つけることができる単一のモジュールがあることを知っていると非常に便利です。それらを互いにコンテキストで表示すると、それらをよりよく理解することができます。Iterator Iterableであることがわかったら、ちらっと見て、Iterableの構成要素をすばやく確認できます。

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

結論

Pythonのコンテキストでは、関連する、意味的に類似したクラス定義を同じファイルに保存することを希望します。ファイルが大きくなりすぎて扱いにくい場合は、再編成を検討してください。


1
私が理解している間、あなたが提出したコードのおかげで、同じファイルに複数のクラスを持つことは大丈夫ですので、私は議論を非常に説得力のあるものと見つけることができません。:たとえば、次のようなコードだけで、ファイル全体持っているPHPに非常に一般的だったclass SomeException extends \Exception {}
オリヴィエMalki

3
異なるコミュニティには異なるコーディング標準があります。Javaの人々はpythonを見て、「なぜファイルごとに複数のクラスを許可するのですか?」と言います。Pythonの人々はJavaを見て、「なぜ各クラスに独自のファイルが必要なのか!?」と言います。それはあなたが作業しているコミュニティのスタイルに従うことが最善である。
ゴートロボット

私もここで混乱しています。どうやら私は自分の答えでPythonについていくつかのことを誤解したようです。しかし、可能な限り多くのメソッドをクラスに追加するために、「フラットはネストよりも優れています」と言えますか?一般に、凝集性とSRPの原則は、機能的に互いに密接に関連するクラスを提供するモジュールに有利なモジュールにまだ適用されると思います(ただし、モジュールは単一のクラスよりも粗いパッケージコンセプトをモデル化するため、おそらく複数のクラスがあります) )、特にモジュールスコープ変数(一般的には回避されることが望ましい)の範囲が拡大するためです。

1
Pythonの禅は、互いに緊張している原則のリストです。あなたは、「疎密は密密よりも優れている」というあなたの主張に同意して答えるかもしれません。-すぐ後に続く、「フラットはネストよりも優れています。」The Python of Zenの個々の行は簡単に誤用され、極端に使用される可能性がありますが、全体としては、コーディングと、合理的な人々が同意しない可能性のある共通の基盤を見つけるのに役立ちます。人々は私のコード例を密集しているとは思わないでしょうが、あなたの説明は非常に密集しているように聞こえます。
アーロンホール

ジェフリー・アルバートソン/ Comic Book Guy、ありがとう。:) Pythonのほとんどのユーザーは(二重下線)特別なメソッドを使用するべきではありませんが、コアデザイナー/アーキテクトがメタプログラミングに従事して、演算子、コンパレータ、添え字表記、コンテキスト、その他をカスタム使用できるようにする必要があります言語機能。それらが最小の驚きの原則に違反しない限り、価値に対する害の比率は無限であると思います。
アーロンホール

4

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

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

Index of Python Enhancement Proposalsについて時々読むことを忘れないでください。


2

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

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

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


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