おぼえがき

過ちては則ち改むるにうんぬんかんぬん

セッターとゲッターについて(Python)

f:id:feci:20210726231508j:plain

Python3

 

Python3.8.5におけるセッターとゲッターについて。

#スーパークラス(親クラス)
class Ca(object):
  def __init__(selfa=None):  #親クラスのコンストラクター
    self.a = a                 #self.aにaを代入
  def fa(self):
    print('あいうえお')

#サブクラス(子クラス)
class Ca_a(Ca):          #引数に親クラスを指定することでクラスを継承
  def fa(self):          #オーバーライドで親のfaに上書きしてしまう
    print('かきくけこ')

#サブクラス(孫クラス)
class Ca_c(Ca_a):                         #Ca_aクラスのサブクラス
  def __init__(selfpasa='漢字'b=False):  #孫クラスのコンストラクター
    super().__init__(a)                   #親クラスのコンストラクターを呼び出して使用
    self.__b = b                          #bをself._bに代入・_を付けてbを隠し要素にする
    self.pas = pas

  #ゲッター
  @property        #デコレーターでpropertyを付与することでb関数はクラス変数として扱われる
  def b(self):     #この関数を呼び出すときはパレンティス(丸括弧)は必要ない
    return self.__b #bの値を返す

 #セッター
  @b.setter              #@propertyが付いている関数名に.setterをデコレートしてセッターにする
  def bs(selfc):       #この関数(セッター)もクラス変数と同じ記述で呼び出す
    if self.pas == 1234#コンストラクターのpasの値が1234のときだけbへの再代入を許可する
      self.__b = c       #bに引数cの値を再代入する
    else:
      raise ValueError   #pasの値が1234以外の場合はすべてValueErrorを返す

 def fa(self):    #オーバーライドで親(子)のfaに上書きしてしまう
    print('たちつてと')


#実行
cc = Ca_c(1234)  #孫クラスのインスタンスの作成・パラメータが1234以外の場合はセッターを呼び出すとError

cc.b = True  #Ca_cクラスのbの値を再代入したいが@property設定があるのでできない(AttribteError)
print(cc.b)  #False

cc.bs = True #セッターを通しているのでbに再代入ができる
print(cc.b)  #True

セッターはプロパティに値を再代入するためのメソッドです。

つまりは特定のプロパティの書き込み専用メソッドです。

 

ゲッターはプロパティを呼び出すためのメソッドです。

つまりは特定のプロパティの読み込み専用メソッドです。

 

通常のプロパティであれば、そのクラスのインスタンスから呼び出しも書き出しも問題なくできてしまいます。

そこでPythonではクラス変数の名前の前にアンダースコアを2つ続けて記述することで、そのプロパティにクラス外からアクセスできない状態にすることができます。

 

つまり、aプロパティにはクラス外からインスタンスを経由してアクセスできますが、__aプロパティにはインスタンスを経由しても直接はそのプロパティにはアクセスすることができません。

このような隠しプロパティを呼び出すためのユーザー定義関数がゲッターであり、そのプロパティに再代入をするためのユーザー定義関数がセッターとなっています。

 

 

上記のスクリプトでは、Ca_cクラスの引数bの値を__bプロパティとして、クラス外からアクセスできない状態に設定しています。

この__bプロパティを呼び出すには、ゲッターであるb関数をインスタンス経由で実行してやればOKです。

 

今回のゲッターにはデコレーターを使って@propertyと関数宣言の上の行に記述しているので、このメソッドを呼び出すときは cc.b というクラス変数を呼び出すときと同じ記述になります。

@propertyが付与されたメソッドの呼び出しには、通常のメソッドの呼び出し時のようなパレンティス(丸括弧)を記述する必要はありません。

 

 

セッターにもデコレーターで属性を付与します。

セッターの関数宣言の上の行に @〇〇.setter と記述します。

 

〇〇の部分には、@propertyを付与したメソッド名が入ります。

つまり今回のスクリプトでは、ゲッターがbメソッドなのでセッターにつけるデコレーターの記述は@b.setter ということなります。

 

あとはセッターにプロパティに再代入したい値を引数として指定して、そのクラスのインスタンスからクラス変数を呼び出して値を再代入する要領で記述すれば、プロパティへの書き込みができます。

つまり今回のスクリプトでは cc.bs = True と記述することで、セッターを経由してbプロパティの値をデフォルト値のFalseからTrueに書き換えています。

 

 

このセッターの実用的な使い方としては、「特定のパスを引数として入力しなければプロパティへの書き換えを許可しない」などがあるようです。

 

たとえば上記スクリプトのようにCa_cクラスの引数に認証用のpasという引数を用意しておいて、コンストラクターに正しいパスを入力できた場合にだけ、セッターのif文の条件を満たしてbプロパティの書き換えを許可する、という感じの処理が作れます。

こうすることで、悪意あるプロパティの書き換えをブロックしていくみたいです。

 

 

最後にプロパティを隠すためのアンダースコアですが、これには1つだけ付けるパターンと2つ付けるパターンが存在しています。

 

プロパティ名の先頭に1つだけアンダースコアを付ける場合は、ソースを見た人に「このプロパティの値を安易に弄らないでね」という警告を与える意味合いがあるらしいです。

このパターンではただの警告用の標識なので、Pythonの処理的にプロパティのアクセスに制約を設ける効果は何一つありません。

ふつうにインスタンス経由でクラス外からプロパティを書き換えられます。

 

しかしアンダースコアを2つ付ける場合は、Pythonの処理としてそのプロパティにクラス外からアクセスすることができなくなります。

 

プロパティの先頭にあるアンダースコアには、上記のような2パターンの意味があるようです。

アンダースコア1つは警告、そして2つの場合は強制措置ですね。

 

 

以上です。