Pythonのクラスについて学んだ #1
はじめに
この記事は、python実践入門 6章 を読み終わった後の学習記録です。
学習段階のため、間違った知識を記述している可能性があります。
そのため、あまりこの記事を鵜呑みにせずに、できるだけ公式ドキュメント もしくは 書籍を読むことを推奨します。
また、間違った記述を発見した場合は、コメントにてご指摘いただけると幸いです。(誰でもコメント可)
クラスの作成
class ClassName(InheritanceClass): def __init__(self, arg): # 初期化メソッド self.instance_var = arg # インスタンス変数の定義 def method_name(self, arg): pass # ...
このようにして、クラスを作成します。
命名規則(PEP8)に従い、クラス名にはキャメルケースを使用します。
メソッド名にはスネークケースを使用します。メソッドの第一引数にはself
を必ず使用します。
self
にはインスタンス自身が渡されます。
インスタンス変数には、スネークケースを使用します。
また、(InheritanceClass)
の部分は省略可能で、省略した際にはObjectClassが継承されます。
class ClassName: # 省略できる
__init__
は特殊メソッドと言われるもので、後ほど説明します。
インスタンスの作成
instance = ClassName('value')
このようにして、インスタンスを作成します。
このようにインスタンスが作成された後、__init__
メソッドが呼び出されます。
引数には__init__
に対応するものを渡します。
def __init__(self, arg):
と定義されているので、引数は2つではないのかと思ってしまいますが、実際にはそうはなりません。
それは、先程記述した通り、self
にはインスタンス自身が自動で渡されるからです。
余談: __init__
メソッド内では、主にインスタンス変数の定義が行われます。
__init__
内の処理は、インスタンスの初期化を行われることを想定されているからです。
__init__
メソッドの名前の由来は、initialize
つまり、初期化から来ています。
メソッド呼び出し
instance.method_name('value')
このようにして、インスタンスを呼び出します。
先程の説明同様、 self
にはインスタンス自身が渡されます。
プロパティー
少し具体的なクラスを作っていきましょう。
class Triangle: def __init__(self, height, width): self.height = float(height) # 念の為float型変換 self.width = float(width)
三角形のクラスを作成しました。
インスタンスを作成しましょう
figure = Triangle(10, -10) print(figure.height) # -> 10 print(figure.width) # -> -10
!?
底辺が-10の三角形なんて存在してはいけないのに、存在できてしまいました。
これではいけないので、self.height, self.width
が0未満のときにエラーを起こすメソッドを作成しましょう。
class Triangle: def __init__(self, height, width): self.height = float(height) # 念の為float型変換 self.width = float(width) self.check() def check(self) -> None: if self.height < 0: raise ValueError(f'height must be over 0.\nheight = {self.height}') if self.width < 0: raise ValueError(f'width must be over 0.\nwidth = {self.width}')
作成しました。
figure = Triangle(10, -10)
ValueError: width must be over 0. width = -10.0
コレで一安心ですね。
figure = Triangle(10, 10) figure.width = -10 print(figure.height) # -> 10 print(figure.width) # -> -10
嘘です。まだ、作成できてしまいます。
@property
そこで、@property
の出番です。
class Triangle: def __init__(self, height, width): self._height = float(height) # 念の為float型変換 self._width = float(width) self.check() @property def height(self): return self._height @property def width(self): return self._width def check(self) -> None: if self._height < 0: raise ValueError(f'height must be over 0.\nheight = {self._height}') if self._width < 0: raise ValueError(f'width must be over 0.\nwidth = {self._width}') figure = Triangle(10, 10) print(figure.height) # -> 10 print(figure.width) # -> 10
まず、インスタンス変数height, width
を_height, _width
に置き換えます。
アンダーバー_
を接頭辞につけることによって、プライベートな変数ですよとアピールします。
そのため、実際にはパブリックです。
figure._height = -10
とか全然かけちゃう。個人的には、pythonのこういう所あんまり好きじゃないですね。
@property def height(self): return self._height
このように、@property
をつけたメソッドは、()を省いて呼ぶことができます。
こうすることにより、あたかもheight
変数が存在するかのようにできます。
では、コレを用いて、areaメソッドを作成しましょう。
@property def area(self): return self._height * self._width / 2 print(figure.area) # -> -100
あたかも、area変数が存在するかのようにできました。
@property
をつけることによって、
()なんて、わざわざ冗長なものを書かずにできるのはいいですね。
@*.setter
width, heightをやはり変更したいです。
どうすればいいでしょう。
そこで、@*.setter
です。
# ... @property def height(self): return self._height @height.setter def width(self, height: float): self._height = float(height) self.check() @property def width(self): return self._width @width.setter def width(self, width: float): self._width = float(width) self.check() # ...
figure.width = 20 print(figure.width) figure.width = -10 # Error
ValueError: width must be over 0. width = -10
こうすることによって、self.height = 20
などの際に、@height.setter
をつけたheight
メソッドが呼ばれるようになりました。
こうすることによって、代入された時に任意の処理を挟むことができます。
継承、クラスメソッドなどは別の記事にまとめます。
class Triangle: def __init__(self, height, width): self._height = float(height) # 念の為float型変換 self._width = float(width) self.check() @property def height(self): return self._height @height.setter def width(self, height: float): self._height = float(height) self.check() @property def width(self): return self._width @width.setter def width(self, width: float): self._width = float(width) self.check() @property def area(self): return self._height * self._width / 2 def check(self) -> None: if self._height < 0: raise ValueError(f'height must be over 0.\nheight = {self._height}') if self._width < 0: raise ValueError(f'width must be over 0.\nwidth = {self._width}') figure = Triangle(10, 10) figure.width = 20 print(figure.height) # -> 10 print(figure.width) # -> 20 print(figure.area) # -> 100