Python数年後のJavaおよびPHPの後で、Python $ ===の世界にやって来ました。言語自体は非常に単純ですが、いくつかの問題を抱えています '私の頭を包むことができない、そしてこれまで読んだ数多くのドキュメントやチュートリアルで答えを見つけることができなかったマイナーな問題。
経験豊富なPython開業医にとって、この質問はばかげているように見えるかもしれませんが、私は本当に答えが欲しいので、言語をさらに進めることができます:
JavaおよびPHP( 厳密には必須ではありませんが ))では、それぞれにclass
を書き込む必要があります独自のファイル。ファイルの名前はclass
のファイルがベストプラクティスです。
しかし、Python、または少なくとも私がチェックしたチュートリアルでは、同じファイルに複数のクラスがあるのはokです。
このルールは、本番環境、デプロイ可能なコードに適用されますか、それとも、教育のみのコードを簡潔にするために行われますか?
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
架空の代替ディレクトリ構造よりも優れています。
番号。
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のコンテキストでは、関連する意味的に類似したクラス定義を同じファイルに保持することを好むことです。ファイルが大きくなって扱いにくくなる場合は、再編成を検討してください。
Pythonでアプリケーションを構築する場合、パッケージとモジュールの観点から考える必要があります。
モジュールはあなたが話しているファイルに関するものです。同じモジュール内に多数のクラスがあることは問題ありません。目的は、同じモジュール内のすべてのクラスが同じ目的/ロジックを提供することです。モジュールが長すぎる場合は、ロジックを再設計してモジュールを細分割することを検討してください。
Python Enhancement Proposals のインデックスについて時々読むことを忘れないでください。
これに対する本当の答えは一般的なものであり、使用する言語には依存しません。ファイル内にあるべきものは、主に、それが定義するクラスの数に依存しません。それは論理的なつながりと複雑さに依存します。限目。
したがって、高度に相互接続されている非常に小さなクラスがいくつかある場合は、それらを同じファイルにバンドルする必要があります。クラスが別のクラスに密接に接続されていない場合、または複雑すぎて別のクラスに含めることができない場合は、クラスを分割する必要があります。
そうは言っても、ファイルごとに1クラスのルールは、通常、優れたヒューリスティックです。ただし、重要な例外があります。実際には、唯一のユーザークラスの実装の詳細にすぎない小さなヘルパークラスは、通常、そのユーザークラスのファイルにバンドルする必要があります。同様に、3つのクラスがある場合はvector2
、vector3
、vector4
、それらを別々のファイルに実装する理由はほとんどありません。