web-dev-qa-db-ja.com

Pythonでの密接に結合されたクラス定義のhas-a関係の回避

私は次のコードを持っています:

class Car(object):
    def __init__(self, my_id):
        self.my_id = my_id
        self.color = color
        self.brand = brand

        self.get_color()
        self.get_brand()

    def get_color():
        return requests.get('https://example.com/{}/color'.format(self.my_id))

    def get_brand():
        return requests.get('https://example.com/{}/brand'.format(self.my_id))

def get_description(car):
    return 'My {} is {}'.format(car.brand, car.color)

def get_color(car):
    return 'My car is {}'.format(car.color)

def main():
    c = Car(123)
    print get_description(c)

まず、必要なすべてのデータを収集するCarオブジェクトを初期化し、次に、ファーストクラスの関数を介してそのデータを操作できるようにすることで、非常にうまく機能します。

ただし、これらすべての関数を別のクラスDescriptionに移動したいと思います。このクラスはCarからの大量のデータを使用するため、次のように初期化されます。

class Description(object):
    def __init__(self, car):
        self.car = car
        self.description_text = description_text
        self.color_text = color_text

ここで、carインスタンスは、インスタンス化時にDescriptionオブジェクトに引数として渡されます。

ただし、2つのクラスが緊密に結合されているため、このアプローチは最善ではないと言われました。

この例を2つのクラスを使用するように作り直すにはどうすればよいですか? DescriptionCarからデータを継承する必要がありますか? Descriptionからすべてのデータを渡してCarをインスタンス化する必要がありますか?

4
mart1n

ミックスインとプロパティを使用してクラスを作り直す

この例を2つのクラスを使用するように作り直すにはどうすればよいですか? DescriptionはCarからデータを継承する必要がありますか?車からすべてのデータを渡してDescriptionをインスタンス化する必要がありますか?

これは実際には、ミックスインとプロパティの関係の優れたユースケースです。すぐに実行されるメソッドの前にgetを付けないでください。代わりに@propertyを使用してください。

class Description_Mixin(object):
    """mixin assumes you have a brand and color"""

    @property
    def brand_and_color_description(self):
        return 'My {} is {}'.format(self.brand, self.color)

    @property
    def type_and_color_description(self):
        return 'My {} is {}'.format(type(self).__name__.lower(), self.color)

リクエストメソッドにプロパティを使用しないでください。通常のメソッドを使用して、これが完了するまでに時間がかかる可能性がある(そして今後も続く)関数であることを示します。ここで、コードは意味的に車が「has-a」の記述を持っていると言います。

class Car(Description_Mixin):
    def __init__(self, my_id):
        self.my_id = my_id
        self.color = self.get_color()
        self.brand = self.get_brand()

    def get_color(self):
        return requests.get('https://example.com/{}/color'.format(self.my_id))

    def get_brand(self):
        return requests.get('https://example.com/{}/brand'.format(self.my_id))

そして使い方:

def main():
    c = Car(123)
    print c.brand_and_color_description

そして、あなたはあなたのミックスインをそれらの種類の説明を必要とする他のタイプのために再利用することができます。

フォローアップ-属性の遅延読み込み:

説明の特定の部分のみを返すcliオプションが必要な場合、Carのすべてのリソースをロードする必要はありません。たとえば、cliオプションに基づいて、車のtype_and_color_descriptionのみを取得する場合、get_brand()を呼び出す必要はありません。これを説明するためにこのコードを変更する方法はありますか(つまり、Carからのすべてのデータがあると仮定しないでください)?

これはロジスティクスの問題です。これを行う実装は次のとおりですが、最初の取得で色とブランド属性の返却が遅くなります。

class Car(Description_Mixin):
    def __init__(self, my_id):
        self.my_id = my_id

    @property
    def color(self):
        try:
            return self._color
        except AttributeError:
            self._color = requests.get(
              'https://example.com/{}/color'.format(self.my_id))
            return self._color

    @property
    def brand(self)
        try:
            return self._brand
        except AttributeError:
            self._brand = requests.get(
              'https://example.com/{}/brand'.format(self.my_id))   
            return self._brand
3
Aaron Hall