[Pythonクイズ]アクセサ? property関数? デコレーター? それとも? クラス定義時の属性アクセス手段には何を使っていますか?Pythonステップアップクイズ

インスタンスの属性へのアクセスを提供するときにどんなやり方をしています? これクイズじゃなくてアンケートな気がしてきたぞ(笑)。

» 2025年09月09日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Pythonステップアップクイズ」のインデックス

連載目次

このクラス定義、あなたならどう書き換えますか? このクラス定義、あなたならどう書き換えますか?

【問題】

 以下はクラスFooを定義するPythonコードである。そのname属性へのアクセスに着目して、このコードを書き換えるとしたらどんな方法を採るかを考えてほしい。

  1. アクセサなんていらない
  2. property関数を使う
  3. @propertyデコレーターを使う
  4. その他

class Foo:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def set_name(self, new_name):
        self.name = new_name

このクラス定義、あなたならどう書き換えますか?

3つのLLMはどう書き替えたか?

 今回はChatGPT 5/Gemini 2.5 Flash/Claude(Claude Sonnet 4)の3つのLLMに読者の皆さんに出題したのと同じ問題を解いてもらうことにしました。結果はというと、全員が一致した選択肢を選びました。そこで、ここではそれを正解としました。

答えが全員一致して喜んでいる皆さん 答えが全員一致して喜んでいる皆さん

 さて、3つのLLMが正解としたのはどれなのでしょう。



かわさき

 どうもHPかわさきです。

 クラスって使ってますか? 筆者は大規模なプログラムを組むってことは多くないのでクラスを使うこともあまり多くないのですが、皆さんはどうなんでしょ。というか、Pythonクイズでクラスを題材にするのってびみょ〜かもしれないという意見もあるんですよね。そっちをやるなら、NumPyとかpandasから問題を作った方がいいのかなぁとか悩んでいます。

 それはさておき、今回は何を選んでも正解なようなものなので、テキトーに選んでください。


【答え】

 正解は選択肢3の「@propertyデコレーターを使う」でした。筆者としてはどれを選んでも正解だと思っていますが、3つのLLMが同じ選択をしたので暫定的にそうします。他の選択肢を選んだ方も間違いじゃありませんよ。知らんけど。

「@propertyデコレーターを使え」と3つのLLMがささやくのよ 「@propertyデコレーターを使え」と3つのLLMがささやくのよ

 アクセサを用意しないのもアリですし、property関数を使うのもアリですし、@propertyデコレーターを使うのもアリです。その他の選択肢としては__getattr__/__setattr__特殊メソッドを使う、データクラスを使うなどの方法が考えられますが、もちろんそれもアリです。以下では選択肢1から選択肢3までについて主に考えていくことにしましょう。

【解説】

 まず問題文のコードについて見ておきます。

class Foo:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def set_name(self, new_name):
        self.name = new_name

問題文のコード

 これはFooクラスのname属性に対して読み取り/書き込みを行うためのメソッドを用意しようというものです。このクラスを使う側では、以下のようなコードを書くことになります。

f = Foo('deep')

name = f.get_name()
print(name)  # deep
f.set_name('insider')
name = f.get_name()
print(name)  # insider

問題文のFooクラスを使うコードの例

 ですが、Pythonではあまりこういう書き方はしません。特に「get_name」とか「set_name」のような名前をメソッドに付けることはないようです。その代わりに、「name = f.name」や「f.name = 'insider'」のように属性(インスタンス変数)に直接アクセスするコードを書くのが一般的です。

 上で述べたPython的な慣習が選択肢1を選択する大きな理由となるでしょう。この場合、コードはとてもシンプルになりますね。

class Foo:
    def __init__(self, name):
        self.name = name

アクセサを用意しないのが一番シンプル

 このクラスを使う側では、以下のようなコードを書くことになります。

f = Foo('deep')

name = f.name
print(name)  # deep
f.name = 'insider'
name = f.name
print(name)  # insider

アクセサは使わない

 属性への直接アクセスはシンプルでよいのですが、その際に何らかのロジックをかませたいという場合(例えば、属性の値を変更する際に何らかの検証をするなど)、このままではマズいでしょう。そうした場合に考えられるのが選択肢2の「property関数を使う方法」と「@propertyデコレーターを使う方法」です。

 property関数を使うコードはこんな感じです。何かロジックをかませようとしていますね(笑)。

class Foo:
    def __init__(self, name):
        self._name = name

    def get_name(self):
        print('doing something')
        return self._name

    def set_name(self, new_name):
        if isinstance(new_name, str):
            self._name = new_name
        else:
            raise TypeError('new_name must be a string')

    name = property(get_name, set_name)

property関数を使うコードの例

 基本のコードは問題文のものと同様です。重要な違いが2つあります。1つは「f.name」のように「self._name」にアクセスできるように、「name = property(get_name, set_name)」としている点です。property関数は最大で以下に示す4つの引数を受け取ります。

  • 第0引数(fget引数):読み取り用のゲッタ
  • 第1引数(fset引数):書き込み用のセッタ
  • 第2引数(fdel引数):属性を削除するためのデリーター
  • 第3引数(doc引数):その属性のドキュメント文字列

 上の例では第0引数と第1引数を指定しているので、ゲッタとセッタが設定されているということになります。ゲッタではロジックをかます代わりに「doing something」と表示していますが、セッタではnew_nameパラメーターの値が文字列かどうかを判断して、そうでなければ例外を発生させるようにしています。

 もう1つの違いはこのクラスを使う側から見るとアクセス対象の属性は「f.name」のようにname属性ですが、その実体は内部で使用する「_name」属性としている点です。このようにアンダースコアを付けるのは「_nameは内部で使う」と明示することが目的の一つとして挙げられるでしょう。しかし、実際問題として「return self.name」のように書くと「name」が衝突して例外が発生するので、これを避けるという意味もあります。

 一方、@propertyデコレーターを使うとコードは以下のようになります。

class Foo:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        print('doing something')
        return self._name

    @name.setter
    def name(self, new_name):
        if isinstance(new_name, str):
            self._name = new_name
        else:
            raise TypeError('new_name must be a string')

@propertyデコレーターを使ったコードの例

 読み取りに使うゲッタが「@property」で修飾されている点に注意してください。また、セッタは「@name.setter」すなわち「@ゲッタの名前.setter」と修飾されている点にも注意です。さらに両方ともメソッドの名前は「name」になっています。なお、属性を削除する「@ゲッタの名前.deleter」というデコレーターも使えますが、ここでは省略します。property関数のdoc引数に対応するドキュメント文字列はゲッタのドキュメント文字列に指定したものがコピーされて使われます(が、これもここでは省略しています)。

 property関数にしても@propertyデコレーターにしても、使い方はアクセサを持たない場合と同様です。念のため、再掲します。

f = Foo('deep')

name = f.name
print(name)  # doing something→deep
f.name = 'insider'
name = f.name
print(name)  # doing something→insider

property関数または@propertyデコレーターを使った場合の使用例

 ただし、今度は「name = f.name」行でゲッタが呼び出されるので、そこで「doing something」と表示されるようになりました。つまり、「deep」や「insider」を出力する前にこれが表示されます。また、次のように文字列以外を代入しようとすると例外が発生します。

f.name = 0  # TypeError

文字列以外を代入しようとするとTypeError例外が発生する

 このように属性へのアクセス時に何らかのロジックを介入させられるのがゲッタとセッタのよいところです。

 では、3つのLLMは、なぜproperty関数ではなく@propertyデコレーターを使う方法を正解としたのでしょう。実は属性へのアクセスを行うためのproperty関数(propertyクラスのコンストラクター)はPython 2.2で登場しました。その当時は上で見たproperty関数を呼び出す形でアクセサを設定していましたが、Python 2.6でpropertyオブジェクトがgetter、setter、deleterという3つのメソッドを持つように変更され、これらをデコレーターの形で使ってアクセサを指定できるようになったのです。

 というわけで、より新しい記述方法であることと、デコレーターを使った方がスッキリと分かりやすくアクセサを記述できること。この2点が@propertyデコレーターが推奨される理由だと思われます。

 とはいえ、最初からデコレーターを使う必要もないでしょう。最初は「アクセサなんていらない」という方針でコードを書き始めて、ゲッタやセッタが必要になった時点でコードに手を加えるというのが正解な気もします。


かわさき

 最後に各LLMがどんな答えを返してきたのかをざっくりと紹介しましょう。まず、全てのLLMが4つの選択肢について、実際のコード例とその簡単な説明をしてくれました。そして、Claudeさんはすかさず「現代的なPythonコードでは、@propertyデコレーター(選択肢3)が最も推奨される方法です」と答えてくれました。Geminiさんはそれぞれの説明までしか話してくれませんでした。ChatGPTさんは「あなたの用途に合わせて選ぶのがベストです」という満点といえば満点な回答をくれたのですが、さすがにそれはアレなので、オススメなのは? と聞いたところ選択肢3を選んでくれました。Geminiさんにもオススメをもう一度聞くと選択肢3を推してきたという感じです。

 選択肢3を推す理由としては、全てのLLMが以下を挙げていました。

  • Pythonic
  • 可読性が高い
  • ゲッタ/セッタにロジックを記述できる柔軟性

 そんなわけで自作クラスのオブジェクトの属性アクセスにロジックが必要になってきたら@propertyデコレーターを使うようにしましょう。ホントかな?

 選択肢4のその他の方法については、ちょっと原稿が長くなったので、そこは皆さんにお任せしますね!


「Pythonステップアップクイズ」のインデックス

Pythonステップアップクイズ

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。