web-dev-qa-db-ja.com

クラスと関数

関数は、プログラミングの経験がなくても、数学のバックグラウンドが十分にある人にとっても簡単に理解できます。一方、クラスは把握するのが難しいようです。

誕生日と現在の年を与えられた人の年齢を計算するクラス/関数を作りたいとしましょう。このためのクラス、または関数を作成する必要がありますか?または、選択はシナリオに依存していますか?

追伸私はPythonに取り組んでいますが、質問は一般的なものだと思います。

52
multigoodverse

関数を作成します。関数do特定のもの、クラスare特定のもの。

クラスには多くの場合、特定のクラスに関連付けられた関数であるメソッドがあり、そのクラスに関連付けられていることを実行します。

本質的に、クラスとは、関数(メソッドとして)とデータ(プロパティとして)を特定の種類の物を中心に論理的な単位にグループ化する方法です。そのグループ化が必要ない場合は、クラスを作成する必要はありません。

92
Amber

アンバーは答えで言う :関数を作成します。実際、次のようなものがある場合、クラスを作成する必要はありませんしないでください

class Person(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def compute(other):
        """ Example of bad class design, don't care about the result """
        return self.arg1 + self.arg2 % other

ここでは、クラスにカプセル化された関数があります。これにより、コードが読みにくくなり、効率が低下します。実際、関数computeは次のように書くことができます。

def compute(arg1, arg2, other):
     return arg1 + arg2 % other

クラスを使用するのは、そのクラスに複数の関数があり、内部状態(属性付き)を維持することが理にかなっている場合のみにしてください。それ以外の場合、関数を再グループ化する場合は、新しい.pyファイル。

このビデオ(Youtube、約30分)を見るかもしれません 、これは私のポイントを説明しています。 Jack Diederichは、そのような場合にクラスが悪である理由と、特にAPIのようなものにおいて、なぜそれが悪い設計であるのかを示しています。
かなり長いですが、必見です

15
Maxime Lorant

クラス(またはそのインスタンス)は、物を表すためのものです。クラスは、オブジェクトの特定のクラス(そのインスタンス)によってサポートされる操作を定義するために使用されます。アプリケーションで人を追跡する必要がある場合、Personはおそらくクラスです。このクラスのインスタンスは、追跡している特定の人々を表します。

関数は物事を計算するためのものです。それらは入力を受け取り、出力を生成し、そして/または効果を持ちます。

クラスと関数は同じものではないため、実際には代替ではありません。 「誕生日と現在の年を与えられた人の年齢を計算する」クラスを作成することを検討することは、実際には意味をなしません。 PersonAgeYear、および/またはBirthdayの概念のいずれかを表すクラスがある場合とない場合があります。しかし、Ageがクラスであっても、人の年齢を計算するものと考えるべきではありません。むしろ、Ageクラスのインスタンスでの人の年齢の計算結果

アプリケーションで人をモデル化し、Personクラスがある場合、年齢の計算をPersonクラスのmethodにするのが理にかなっているかもしれません。メソッドは基本的に、クラスの一部として定義される関数です。これが、前述の「特定のクラスのオブジェクトでサポートされる操作を定義する」方法です。

そのため、個人の年齢を計算するために、個人クラスにメソッドを作成できます(おそらく、誕生日オブジェクトを個人オブジェクトから取得し、現在の年をパラメーターとして受け取ります)。ただし、計算は関数(クラスのメソッドである関数)によって行われます。

または、引数(誕生年を取得する人物オブジェクト、または単に誕生年そのもの)を受け取るスタンドアロン関数を作成することもできます。ご指摘のとおり、このメソッドが自然に属するクラスをまだ持っていない場合、これははるかに簡単です!操作を保持するためだけにクラスを作成しないでください。それがallである場合、操作はスタンドアロン関数である必要があります。

6
Ben

シナリオによって異なります。 only人の年齢を計算する場合は、single特定の動作を実装するために関数を使用します。

しかし、人の生年月日(および他のデータ)を含むオブジェクトを作成し、それを変更することを許可する場合、年齢の計算はその人に関連する多くの操作の1つであり、代わりにクラスを使用してください。

クラスは、いくつかのデータと関連する操作をマージする方法を提供します。データに対して1つの操作しかない場合、関数を使用してデータを引数として渡すと、同等の動作が得られ、コードはそれほど複雑ではありません。

種類のクラスに注意してください:

class A(object):
    def __init__(self, ...):
        #initialize
    def a_single_method(self, ...):
        #do stuff

実際にはクラスではなく、(複雑な)関数にすぎません。正当なクラスには常に少なくとも2つのメソッドが必要です(__init__)。

5
Bakuriu

私はそれが物議を醸すトピックであることを知っており、おそらく今やけどするでしょう。しかし、ここに私の考えがあります。

私自身は、できるだけクラスを避けるのが最善だと考えました。複雑なデータ型が必要な場合は、単純な構造体(C/C++)、dict(python)、JSON(js)などを使用します。つまり、コンストラクター、クラスメソッド、演算子のオーバーロード、継承などは使用しません。 OOP自体(どのデザインパターン、プライベートにする必要がある、bla bla))に夢中になり、最初にコーディングしたい基本的なものにゆるやかに焦点を当てることができます。

プロジェクトが大きくなり乱雑になると、何らかのヘリコプタービューシステムアーキテクチャが必要になるため、OOPが意味を成し始めます。「関数vsクラス」も前のタスクに依存します。

関数

  • 目的:データの処理、データの操作、結果セットの作成。
  • 使用する場合:これを行いたい場合は常に関数をコーディングします:“ y = f(x)”

struct/dict/json/etc(クラスの代わりに)

  • 目的:attr./param。の保存、attr。/ param。の維持、attr。/ param。の再利用、attr。/ param。の使用後。
  • 使用する場合:属性/パラメータのセットを扱う場合(できれば変更不可)
  • 異なる言語の同じもの:struct(C/C++)、JSON(js)、dict(python)など.
  • 常に、複雑なクラスよりも単純なstruct/dict/json/etcを好む(単純にしてください!)

クラス(新しいデータ型の場合)

  • シンプルな視点:メソッドが付加された構造体(C)、dict(python)、json(js)などです。
  • このメソッドは、クラスに保存されているデータ/パラメーターと組み合わせた場合にのみ意味があります。
  • 私のアドバイス:クラスメソッド内に複雑なものをコーディングしないでください(代わりに外部関数を呼び出してください)
  • 警告:クラスを関数の偽の名前空間として誤用しないでください! (これは非常に頻繁に起こります!)
  • 他のユースケース:演算子のオーバーロードを大量に行いたい場合は、クラス(たとえば、独自の行列/ベクトル乗算クラス)を使用します
  • 自問してみてください:それは本当に新しい「データ型」ですか? (はい=>クラス|いいえ=>クラスの使用を避けることができます)

array/vector/list(大量のデータを保存するため)

  • 目的:同じデータ型の同種のデータを多数保存します。時系列
  • アドバイス#1:プログラミング言語に既にあるものを使用してください。再発明しないでください
  • アドバイス#2:「クラスmysupercooldatacontainer」が本当に必要な場合は、既存の配列/ベクトル/リスト/などのクラスをオーバーロードします(例:「クラスmycontainer:public std :: vector…」)

enum(列挙クラス)

  • 言及しただけです
  • アドバイス#1:複雑すぎるOOPデザインパターンの代わりに列挙型とスイッチケースを使用する
  • アドバイス#2:有限状態マシンを使用する
4
ulf

質問に答える前に:

Personクラスがない場合は、最初にPersonクラスを作成するかどうかを検討する必要があります。 Personの概念を頻繁に再利用する予定はありますか?その場合、Personクラスを作成する必要があります。 (渡された変数の形式でこのデータにアクセスできます。面倒でずさんなことは気にしません。)

質問に答えるには:

あなたは彼らの生年月日にアクセスできるので、その場合は、おそらくsomeperson.birthdateフィールドを持つPersonクラスがあります。その場合、someperson.ageは再利用可能な値ですか?

答えはイエスです。生年月日よりも年齢を重視することが多いため、生年月日がフィールドの場合、年齢は必ず派生フィールドである必要があります。 (これを行わない場合:someperson.chanceIsFemalesomeperson.positionToDisplayInGridなどの値、または他の無関係な値を計算する場合、Personクラスを拡張しません。その質問に対する答えは、元のクラスを拡張するか、関数(またはPersonAnalysisDataなどの独自のクラス)を作成するかを決定します。 )

3
ninjagecko

クラスを作成しないでください。少なくともOOPクラスの種類Python議論中。

この単純なクラスを考えてみましょう。

class Person(object):
    def __init__(self, id, name, city, account_balance):
        self.id = id
        self.name = name
        self.city = city
        self.account_balance = account_balance

    def adjust_balance(self, offset):
        self.account_balance += offset


if __== "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p.adjust_balance(50.0)
    print("done!: {}".format(p.__dict__))

対このnamedtupleバージョン:

from collections import namedtuple

Person = namedtuple("Person", ["id", "name", "city", "account_balance"])


def adjust_balance(person, offset):
    return person._replace(account_balance=person.account_balance + offset)


if __== "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p = adjust_balance(p, 50.0)
    print("done!: {}".format(p))

Namedtupleアプローチは、次の理由により優れています。

  • namedtuplesには、より簡潔な構文と標準的な使用法があります。
  • 既存のコードを理解するという点では、namedtuplesは基本的に理解するのが簡単です。クラスはより複雑です。そして、クラスはvery人間が読むのに複雑になることがあります。
  • namedtuplesは不変です。可変状態を管理すると、不要な複雑さが増します。
  • class inheritanceは複雑さを追加し、複雑さを隠します。

OOPクラス。

ところで、他のほとんどの言語には、namedtuplesのようなレコードタイプ機能があります。たとえば、Scalaにはケースクラスがあります。このロジックは同じように適用されます。

3
clay

これについては群れから脱却し、別の視点を提供します。

クラスを作成しないでください。

クラスへの依存は、コーダーに肥大化した遅いコードを作成させる傾向があります。渡されるクラス(オブジェクトであるため)は、関数を呼び出して1つまたは2つの文字列を渡すよりもはるかに多くの計算能力を必要とします。関数の適切な命名規則は、クラスの作成でできることのほとんどすべてを、オーバーヘッドのほんの一部とコードの読みやすさで実行できます。

ただし、クラスを理解することを学ぶべきではないという意味ではありません。他の人とコーディングしている場合、人々は常にそれらを使用するため、それらのクラスをジャグリングする方法を知る必要があります。関数に依存するようにコードを記述することは、コードがより小さく、より速く、より読みやすくなることを意味します。きびきびした迅速な機能のみを使用して書かれた巨大なサイトを見てきました。また、クラスに大きく依存し、絶えず壊れる最小限の機能を備えた小さなサイトを見てきました。 (クラスの一部としてクラスを含むクラスを拡張するクラスがある場合、簡単な保守性のすべての見た目を失ったことがわかります。)

結局のところ、渡そうとしているすべてのデータは、既存のデータ型で簡単に処理できます。

クラスは精神的な松葉杖として作成され、実際の余分な機能を提供しません。クラスが作成する傾向がある過度に複雑なコードは、長期的には松葉杖のポイントを打ち負かします。

2
liljoshu