web-dev-qa-db-ja.com

Pythonでクラスを使用する必要があるのはいつですか?

私はpythonで約2年間プログラミングを行っています。主にデータ(パンダ、mpl、numpy)だけでなく、自動化スクリプトと小さなWebアプリもあります。私はより良いプログラマーになり、pythonの知識を増やし、気になっていることの1つは、クラスを使用したことがないことです(小さなWebアプリのランダムflaskコードをコピーする以外) 。私は一般的にそれらが何であるかを理解していますが、単純な機能でなぜそれらが必要なのか、頭を包み込むようには思えません。

私の質問に具体性を追加するには:常に複数のデータソース(mongo、sql、postgres、apis)からのデータのプル、大量または少量のデータの変更とフォーマットの実行、csv/Excelへのデータの書き込みを含む大量の自動レポートを作成します/ html、メールで送信します。スクリプトの範囲は、最大250行から最大600行です。クラスを使用してこれを行う理由と、その理由はありますか?

101
metersk

クラスは Object Oriented Programming の柱です。 OOPは、コードの編成、再利用性、およびカプセル化に大きな関心を持っています。

まず、免責事項:OOPは、Pythonでよく使用される別のパラダイムである Functional Programming とは部分的に対照的です。 Python(または確かにほとんどの言語)でプログラミングするすべての人がOOPを使用するわけではありません。オブジェクト指向ではないJava 8では多くのことができます。 OOPを使用したくない場合は、使用しないでください。二度と使用しないデータを処理するための1回限りのスクリプトを作成している場合は、そのままの状態で書き続けてください。

ただし、OOPを使用する理由はたくさんあります。

いくつかの理由:

  • 組織:OOPは、コードでデータとプロシージャの両方を記述および定義するよく知られた標準的な方法を定義します。データとプロシージャの両方をさまざまなレベルの定義(異なるクラス)で保存でき、これらの定義について話す標準的な方法があります。つまり、OOPを標準的な方法で使用すると、後の自分や他の人がコードを理解、編集、使用するのに役立ちます。また、複雑な任意のデータストレージメカニズム(dictsのリストまたはリストまたはdictsまたはセットのdictsのリストなど)を使用する代わりに、データ構造の一部に名前を付けて、それらを便利に参照できます。

  • 状態:OOPは、状態を定義および追跡するのに役立ちます。たとえば、古典的な例では、学生を処理するプログラム(たとえば、成績プログラム)を作成している場合、必要なすべての情報を1つの場所(名前、年齢、性別、学年、コース、学年、教師、仲間、食事、特別なニーズなど)、このデータはオブジェクトが生きている限り保持され、簡単にアクセスできます。

  • カプセル化 :カプセル化では、プロシージャとデータが一緒に保存されます。メソッド(関数のOOP用語)は、操作および生成するデータと一緒に定義されます。 アクセス制御 を許可するJavaなどの言語、またはPythonでは、パブリックAPIの記述方法に応じて、メソッドとデータをユーザーから隠すことができます。つまり、コードが必要な場合、またはコードを変更したい場合、コードの実装に必要なことは何でもできますが、パブリックAPIは同じままにできます。

  • 継承 :継承を使用すると、1つの場所(1つのクラス)でデータとプロシージャを定義し、後でその機能をオーバーライドまたは拡張できます。たとえば、Pythonでは、機能を追加するためにdictクラスのサブクラスを作成する人がよく見かけます。一般的な変更は、存在しないディクショナリからキーが要求されたときに例外をスローするメソッドをオーバーライドして、不明なキーに基づいてデフォルト値を与えることです。これにより、現在または後で自分のコードを拡張したり、他の人が自分のコードを拡張したり、他の人のコードを拡張したりできます。

  • 再利用性:これらすべての理由およびその他の理由により、コードの再利用性が高まります。オブジェクト指向コードを使用すると、堅牢な(テスト済みの)コードを1回作成し、何度でも再利用できます。特定のユースケースで何かを微調整する必要がある場合は、既存のクラスから継承して、既存の動作を上書きできます。何かを変更する必要がある場合は、既存のパブリックメソッドシグネチャを維持しながらすべてを変更できます。誰も賢明ではありません(うまくいけば)。

繰り返しますが、OOPを使用しない理由はいくつかありますが、必要はありません。しかし幸いなことに、Pythonのような言語では、ほんの少しでもたくさんでも使えます。それはあなた次第です。

学生のユースケースの例(コード品質の保証なし、単なる例):

オブジェクト指向

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

標準辞書

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))
89
dantiston

関数の状態を維持する必要があり、ジェネレーター(返すのではなく生成する関数)で状態を維持できない場合。ジェネレーターは独自の状態を維持します。

standard演算子のいずれかをオーバーライドする場合は、クラスが必要です。

Visitorパターンを使用するたびに、クラスが必要になります。他のすべてのデザインパターンは、ジェネレーター、コンテキストマネージャー(クラスとしてよりもジェネレーターとして実装される)、およびPODタイプ(辞書、リスト、タプルなど)を使用して、より効果的かつクリーンに実現できます。

「Pythonic」コードを作成する場合は、クラスよりもコンテキストマネージャーとジェネレーターを優先する必要があります。きれいになります。

機能を拡張する場合は、ほとんどの場合、継承ではなく包含で機能を実現できます。

すべてのルールとして、これには例外があります。機能をすばやくカプセル化する(つまり、ライブラリレベルの再利用可能なコードではなくテストコードを記述する)場合は、状態をクラスにカプセル化できます。シンプルで、再利用可能にする必要はありません。

C++スタイルのデストラクタ(RIIA)が必要な場合は、クラスを絶対に使用しないでください。コンテキストマネージャーが必要です。

14

あなたはそれを正しくやると思います。クラスは、いくつかのビジネスロジックまたは困難な関係を持つ現実の困難なプロセスをシミュレートする必要がある場合に合理的です。例として:

  • 共有状態のいくつかの機能
  • 同じ状態変数の複数のコピー
  • 既存の機能の動作を拡張するには

この古典的なビデオ もご覧になることをお勧めします

10
valignatev

クラスは、実世界のエンティティを定義します。個別に存在し、他のロジックとは別の独自のロジックを持つものに取り組んでいる場合、そのクラスを作成する必要があります。たとえば、データベース接続をカプセル化するクラス。

そうでない場合は、クラスを作成する必要はありません

4
Ashutosh

それはあなたのアイデアとデザインに依存します。あなたが優れたデザイナーであれば、OOPはさまざまなデザインパターンの形で自然に出てきます。単純なスクリプトレベルの処理では、OOPがオーバーヘッドになる可能性があります。再利用可能および拡張可能などのOOPの基本的な利点を考慮し、それらが必要かどうかを確認してください。 OOPは、複雑なものをよりシンプルにし、よりシンプルなものを複雑にします。 OOPを使用するか、OOPを使用しないかのいずれかの方法で単純に物事を単純に保ちます。これを使用する方が簡単です。

0
Mohit Thakur