まえがき
私が資格取得もかねて Python の基礎知識を見直していたところ、Python の getter/setter にはいろいろ書き方があることを知りました。業務で 1 年半 Python を書いてきて「もう使いこなしているぞ」という段階においてです。
プログラミング経験者で他の言語を習得しようとするときには、概念だけを知っている状態でとりあえず実装してみようとなる人もいますが、このときに、習得しようとしている言語にもっと便利な書き方があることに気付かず、すでに知っている言語と同じような書き方で終わってしまうことがあります。
備忘録を兼ねつつ、Python を学習中の方にも、すでに習得しているという方にも知っていてほしいと思ったので、今回の記事を書くことにします。
前提
この記事では、getter/setter の概念は習得済みであるとします。
deleter については省略します。
また、getter/setter の是非論については扱いません。
Python での getter/setter の書き方
今回、私が知っている 3 通りの方法をご紹介しますが、言語仕様に即した他の書き方がもしありましたら、コメントでお寄せいただけると幸いです。
今回は、本の価格をプロパティに持つ BookPrice クラスを作成します。
get_* や set_* といったメソッドを用意する
おそらく最も知られている方法かと思います。
get_price
を getter、set_price
を setter のメソッドとします。
プロパティ名を __price
のように __
(アンダースコア 2 つ)で始めるとプライベートになります。
from typing import Optional
class BookPrice:
def __init__(self) -> None:
self.__price: Optional[int] = None
def get_price(self) -> int:
print('getter called')
return self.__price
def set_price(self, value: int) -> None:
print('setter called')
self.__price = value
book_price = BookPrice()
book_price.set_price(1200)
print(book_price.get_price())
価格を取得したいときは get_price
メソッドを実行し、価格を設定したい場合は set_price
の引数に価格を指定して実行します。
実行すると以下のようになります。
$ python sample1.py
setter called
getter called
1200
property クラスをインスタンス化する
Python で property について調べると最初に出てくる書き方ですが、私はこの記事を書いていて初めて知りました。
from typing import Optional
class BookPrice:
def __init__(self) -> None:
self.__price: Optional[int] = None
def get_price(self) -> int:
print('getter called')
return self.__price
def set_price(self, value: int) -> None:
print('setter called')
self.__price = value
price = property(fget=get_price, fset=set_price, fdel=None, doc='本の価格')
book_price = BookPrice()
book_price.price = 1200
print(book_price.price)
get_price
と set_price
を用意するところまでは先ほどの方法と同じですが、最後に property
クラスをインスタンス化する形でプロパティを設定します。
getter/setter/deleter およびプロパティの説明を指定します。
価格を取得したいときは book_price.price
で、価格を設定したい場合は book_price.price = 1200
のようにします。
呼び出し側では、price
というプロパティに対して値を直接取得したり設定したりしているように見えます。
実行すると以下のようになります。
$ python sample2.py
setter called
getter called
1200
property クラスをデコレータに使用する
私は Python の資格の勉強で知りました。
from typing import Optional
class BookPrice:
def __init__(self) -> None:
self.__price: Optional[int] = None
@property
def price(self) -> int:
"""本の価格"""
print('getter called')
return self.__price
@price.setter
def price(self, value: int) -> None:
print('setter called')
self.__price = value
book_price = BookPrice()
book_price.price = 1200
print(book_price.price)
呼び出し方は先ほどの方法と同じですが、クラスの実装が少し異なります。
getter には @property
デコレータを設定し、メソッド名はプロパティ名の price
にします。setter には @{プロパティ名}.setter
デコレータを設定し、メソッド名は getter と同じで price
にします。
同じ名前のメソッドが複数存在するため、違和感がありますが、これで問題ないようです。
また、setter のデコレータで price
なんて補完されるのかよ、という感じですが、VSCode および PyCharm においては、getter が存在することでプロパティ名が自動補完できるようなので、開発者体験としても問題ありません。
実行すると以下のようになります。
$ python sample3.py
setter called
getter called
1200
まとめ
Python の getter/setter は、調べてみると方法が 3 通りあったわけですが、こんなに方法があることに気付くには、 実装方法を知っていても改めて調べてみる ことが必要でした。
実務においても、要件を満たせばそれでよいのではなく、時間の許す限り、より良い書き方を求めていきたいと思いました。