40のおっさんのPython学習記録

20年以上前、学部の必修のC言語が全く理解できずに同級生に放り投げ、その後コーディングから遠ざかったガチ文系のおっさんが、ふと思い立ってPythonに挑戦しています。

新たな気づき(Chapter 12-3)

おっさんです。花粉症で集中できずにどうしてもこのクラスを扱う12章がもくもく会の会場で完結できなかったのを、意地で自宅で全てやりました。無理やりやっつけることが目的化し、完全に理解できているかは怪しいです。でもとにかく前に進みましょう。

  • インスタンス変数にアンダースコアを2つ先頭につけるとクラスの外からのアクセス・参照ができなくなる。
class Person():
    def __init__(self, name):
        self.__name = name         #アンダースコア2つで外部参照不能になる
    def who(self):
        print(self.__name+"です。") #クラス内では利用可能

man=Person("宇佐美")
man.who()                          #宇佐美です。
print(man.__name)                  #直接アクセスはエラー
  • ただし、非公開にしたインスタンス変数も、ゲッター関数を用いて値を調べたり、セッター関数を用いて値を変更したり可能。ただ、1個の変数のアクセスに2個の関数を使うのは効率的とはいえないことから「プロパティ」の考え方を導入。デコレータ@propertyにつづいてゲッター関数を定義し、デコレータ@関数名.setterにつづいてセッター関数を定義する。この時ゲッターもセッターも関数名は共通にする。
@property
def 関数名(self):
    return self.__非公開変数  #プロパティの値を取得し返す
    
@関数名.setter
def 関数名(self, value):
    self.__非公開変数 = value #プロパティに値を設定
  • 以下の事例の場合、nameにはゲッターとセッターの両方を定義しているが、priceにはゲッターしか定義しておらずセッターがないので、shoes.priceで新たな値を設定しようとしてもエラーになる。
class Goods:
    #初期化メソッド
    def __init__(self, name, price):
        #非公開の__dataインスタンス変数(辞書)
        self.__data = {"name":name, "price":price}

    #nameプロパティ:ゲッター
    @property
    def name(self):
        return self.__data["name"]

    #nameプロパティ:セッター
    @name.setter
    def name(self, value):
        self.__data["name"] = value

    #priceプロパティ:ゲッター
    @property
    def price(self):
        price = self.__data["price"]
        price_str = f"{price:,}円"
        return price_str

    #priceプロパティのセッターは用意せず

shoes = Goods("Dream",6800)
print(shoes.name) #Dream
shoes.name = "Dream Girls"
print(shoes.name) #Dream Girls
print(shoes.price) #6,800円
shoes.price = 7000
print(shors.price) #priceにはセッター関数がないのでエラー
  • デコレータを使わずに、プロパティ変数 = property(ゲッター関数、セッター関数)で指定することもできる。具体的には以下のコードのようになるが、@で始まるデコレータが消えた代わりに、関数名にget_あるいはset_で始まるものになり、クラス定義の最後にproperty()を用いたプロパティの設定を行っている。アウトプットは上記の事例と同じ。
class Goods:
    #初期化メソッド
    def __init__(self, name, price):
        #非公開の__dataインスタンス変数(辞書)
        self.__data = {"name":name, "price":price}

    #nameプロパティ:ゲッター
    def get_name(self):
        return self.__data["name"]

    #nameプロパティ:セッター
    def set_name(self, value):
        self.__data["name"] = value

    #priceプロパティ:ゲッター
    def get_price(self):
        price = self.__data["price"]
        price_str = f"{price:,}円"
        return price_str

    #priceプロパティのセッターは用意せず

    #nameプロパティのゲッターとセッターを指定
    name = property(get_name, set_name)
    #priceプロパティのゲッターを指定(セッターは指定せず)
    price = property(get_price)

shoes = Goods("Dream",6800)
print(shoes.name) #Dream
shoes.name = "Dream Girls"
print(shoes.name) #Dream Girls
print(shoes.price) #6,800円
shoes.price = 7000
print(shors.price) #priceにはセッター関数がないのでエラー