Python データ構造体 class

Python学習【365日チャレンジ!】82日目のマスターU(@Udemy11)です。

少し前にスマホのキャリアを楽天モバイルに変更したのですが、やはりちょっとスピードが気になりますが、楽天会員であれば、1年間1,500円割引キャンペーンを利用しているので、めちゃくちゃ通信量が安くなりました。

以前は約5,000円程度だった月額料金が、

なんとびっくりの1,080円!!

楽天のダイヤモンド会員でもあるので、月額の割引金額は2,000円と超お得なんです。

通信スピードに不満がないわけではありませんが、自宅ではWifiにつなげれば問題ありませんし、外でそんなにヘビーな使い化をするわけではないので、コストパフォーマンスは最高です。

スマホの通信費が高いと頭を悩ませている場合は、ぜひ一度、楽天モバイルを検討してみてはどうでしょうか?

それではPython学習をすすめましょう。

昨日の復習

昨日は、プロパティーを使った属性の設定について学習しました。

class Car:
    def __init__(self,name='Crown', auto_drive=False):
        self.model = name
        self._drive = auto_drive
    
    @property
    def drive(self):
        return self._drive
        
car = Car()
print(car.model)
print(car.drive)

出力結果

Crown
False

@propertyを使って関数にすることで、driveがクラス変数として扱われました。

そのクラス変数deiveの返り値を、Carクラスで初期化した_driveとすることで、クラスの外からはクラス変数driveを書き換えられないようにすることができました。

また、@drive.setterを指定して、書き換えられる条件を付け足すこともできましたね。

@propertyによる属性設定については、こちらの記事を参考にしてください。

それでは本日のPython学習をすすめましょう。

構造体とは

構造体とは何かをWikipediaで調べると次のような解説が見つかりました。

構造体とは

構造体(こうぞうたい、英: structure)はプログラミング言語におけるデータ型の一つで、1つもしくは複数の値をまとめて格納できる型。それぞれのメンバー(フィールド)は型が異なっていてもよい点が配列と異なる。

C/C++やC#などでstructとしてサポートされているほか、Visual Basicのユーザー定義型や、PascalやAdaのrecord型も構造体に相当する。

クラスベースのオブジェクト指向言語では、抽象データ型としてのクラスが構造体の役割をも内包する。Cの文法を継承した言語ではstructキーワードを含むこともあるが、言語によってその役割は異なる。

他のプログラミング言語のことはよくわからないのですが、WikiではPythonについては書かれていませんでした。。
C言語などにおけるところの構造体はPythonにはありませんが、クラスが構造体の役割を担っているという感じです。

クラス変数の作成

Pythonには、データ構造体としてclassがあるわけですが、クラス内で定義する変数は、インスタンスを作成してから、追加することができます。

class Person:
    pass

p = Person()
p.name = 'Mark'
p.age = 40
print(p.name, p.age)

出力結果

Mark 40

Personクラスでは何もしないpassを指定したあと、Personのインスタンスpを作成し、あとから、p.namep.ageというクラス変数を定義しています。

イメージとしては、設計図のキャンパスだけ用意して、あとから書き足していくような感じですね。

ある意味、便利な使い方なのですが、設計図(class)を書いたプログラマーからするとちょっと困っちゃいますよね。

ということで、Python学習者なら知っておかないといけないクラスを扱う際の注意点について解説します。

クラスを扱う際の注意点

Pythonのクラスは、インスタンス作成後にクラス変数を作成することができるので、便利な半面、注意しなければいけないこともあります。

それが、クラス内で定義したクラス変数をオーバーライドできるというところです。

クラス内で定義された__(アンダースコア*2)が付いた変数は、インスタンスからは呼び出せないことを昨日のレクチャーで学習しましたが、クラス変数名をそのまま指定して、値を定義すると、__(アンダースコア*2)が付いていても上書きされてしまいます。

class Car:
    def __init__(self, name='Crown', auto_drive=False):
        self.model = name
        self.__drive = auto_drive

    @property
    def drive(self):
        return self.__drive

    @drive.setter
    def drive(self, is_enable):
        self.__drive = is_enable

car = Car()
car.__drive = True

print(car.__drive)
print(car.drive)

出力結果

True
False

このコードでは、__driveは、インスタンスcarからは呼び出すことはできませんので、15行目にprint(car.__drive)を挿入して実行するとAttributeError: ‘Car’ object has no attribute ‘__drive’というエラーが起こります。

しかし、上記コードの15行目のようにcar.__drive = Trueと値を指定すると、クラス変数の値を上書きしちゃうんです。

とりあえず、昨日よくわからないままになっていた、クラスの外から見えないはずの__で指定されたクラス変数の値が変更されてしまうという疑問はこれで解決できました。

オーバーライドの機能はかなり強力なので、もし、インスタンス内でクラス変数を定義する場合は、クラスの中で定義されている変数名と同じものではないかということをチェックしておく必要があります。

といっても、基本的にインスタンス内でクラス変数を作るのはあまりおすすめではないとのことでした。

ここで一つ疑問がのこるのは、car.driveの値が置き換わらないということです。

上記コードで__driveをすべて_driveに変えると、最後の出力は、Trueになります。

class Car:
    def __init__(self, name='Crown', auto_drive=False):
        self.model = name
        self._drive = auto_drive

    @property
    def drive(self):
        return self._drive

    @drive.setter
    def drive(self, is_enable):
        self._drive = is_enable

car = Car()
car._drive = True

print(car._drive)
print(car.drive)

出力結果

True
True

car.__driveを上書きしても、car.driveは変更されなかったのに、car._driveにして、値を上書きしてしまうと、car.driveの値は変更されてしまいます。

__drive_drivedriveの関係を理解するためには、より詳しく調べる必要がありそうですが、いまは_driveをオーバーライドするとdriveも上書きされて、__driveをオーバーライドした場合は、driveは上書きされないと認識しておく程度でいいでしょう。

わからないことが増えてくる

講座が進むにつれて、どんどんわからないことが増えていきます。

わからないことは理解できるまで調べることが大切ですが、時間をかけて調べても理解できないことも増えてきます。

なので、ある程度の認識ができるようになったら、次に進むような柔軟な対応が必要になってきます。まさに、

トレードオフ

が必要だということですね。

こんなことがあったなという疑問として頭の片隅に残しておけば、具体的なプログラムを組むときにその疑問が解決するかもしれませんし、実際に使われることはないので全く気にする必要のないものかもしれません。

ときにはわからないことをすっ飛ばす勇気も必要ということです。

それでは、明日もGood Python!