かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Python3 Class インスタンス変数とクラス変数・インスタンスメソッドとクラスメソット。それからスタティックメソッド

インスタンス変数とクラス変数

  • インスタンス変数 … self.variable の形で定義する
  • クラス変数 … クラスブロック直下に書かれた変数はクラス変数になる
class User(object):
  # クラス変数
  name = 'User'
  
  def __init__(self, name):
    # インスタンス変数
    self.name = name

User.name # => User
ichigo = User('Ichigo')
ichigo.name # => Ichigo

クラス変数とインスタンス変数を同じ名前で作成してしまうと、一見するとインスタンスの初期化で name を上書きしたように見えてしまう。
__class__インスタンスのクラスにアクセスできるようなので、これを使って name をみてみるとクラスブロック直下の name がクラス変数だと理解できた。

ichigo = User('Ichigo')
ichigo.name # => Ichigo

print(ichigo.__class__)
# => # => <class '__main__.User'>
ichigo.__class__.name # => User

User.name = 'Idol'
ichigo.__class__.name # => Idol
ichigo.name = Ichigo

同じ変数名でもクラス変数とインスタンス変数は別物として扱われている。
ただし、同じ名前の変数を作ってしまうとインスタンスからクラス変数を使いづらいので変数名は被らないようにしたほうが良さそう。

インスタンスメソッドとクラスメソッド

  • インスタンスメソッド … クラスブロック内に通常に作成された関数。第一引数にインスタンス自身が渡される
  • クラスメソッド … クラスブロック内に @classmethod デコレーター付きで作成された関数。第一引数にクラスが渡される
class User(object):
  name = 'User'

  def __init__(self, name, age=None):
    self.name = name
    self.age = age

  def hi(self):
    print(f'Hi, {self.name} ({self.age})')

  @classmethod
  def info(cls):
    print(f'This class is {cls.name}')

# クラスメソッド
User.info() # => This class is User

ichigo = User('Ichigo', 16)
# インスタンスメソッド
ichigo.hi() # => Hi, Ichigo (16)
# クラスメソッドはインスタンスからも呼び出せる
ichigo.info() # => This class is User

クラスからもインスタンスメソッドが呼び出せるには呼び出せるっぽい
多分ダメな使い方と思うけど、第一引数にインスタンスを渡してあげるとエラーにならず実行できた…

ichigo = User('Ichigo', 16)
User.hi(ichigo) # => Hi, Ichigo (16)

クラスメソットとスタティックメソッド

  • クラスメソッド … 第一引数にクラス自身が渡される
  • スタティックメソッド … クラス自身が渡されない
class User(object):
  name = 'User'

  @classmethod
  def info1(cls, message):
    # 第一引数でクラス自身が渡される
    print(f'{cls.name} {message}')

  @staticmethod
  def info2(message):
    # 純粋にメソッドに渡される引数だけが渡される
    print(f'{User.name} {message}')

# クラスメソッド
User.info1('info1')  # => User info1
# インスタンスメソッド
User.info2('info2')  # => User info2

# User を継承したクラス
class Idol(User):
  name = 'Idol'

# 親クラスのクラスメソッドの cls は Idol クラスが渡されるようになる
Idol.info1('Idol.info1')  # => Idol Idol.info1
# スタティックメソッドは動的に渡される変数がないので出力は変わらない
Idol.info2('Idol.info2')  # => User Idol.info2

クラスメソッドの第一引数に渡されるクラスは継承されたクラスでは、継承されたクラス自身が渡されるので、ある意味動的とも言えるのかな。

所感

クラスブロック直下に置かれた変数がクラス変数になるのは、今まで通ってきた言語ではインスタンス変数になることが多かったのでチョットびっくりしました。
後はクラスからインススタンスメソッドが呼び出せたり、同じファイル内でクラス再定義できたり、Pythonって自由な言語なんだな〜という印象です。


[参考]

ジョゼ虎映画とても楽しみなのです。

Python3 class の getter / setter のメモ

また Python を少し触っています。

private 変数 のおさらい

__ 始まりで作成された変数はインスタンスからしかアクセスできない private 変数になる

class User(object):
  def __init__(self, name):
    self.__name = name
  
  def hi(self):
    print(f'Hi, {self.__name}')

ichigo = User('Ichigo')
ichigo.hi()
# => Hi, Ichigo

# private 変数にアクセスしようとしたらエラー
ichigo.__name
# => AttributeError: 'Idol' object has no attribute '__name'

# 存在しない変数にアクセスしようとしてもエラー
ichigo.name
# => AttributeError: 'Idol' object has no attribute 'name'

# private 変数に値を代入
# エラーにはならないが無視される (更新されない)
ichigo.__name = 'Aoi'

ichigo.hi()
# => Hi, Ichigo

インスタンスからしかアクセスできないのチョット使い勝手が悪いので、他の言語にもあるような getter / setter を作成したい。

1. @property デコレーターを使う方法

  • getter: @property デコレーターを使ってメソッドを定義する
  • setter: @{property_name}.setter デコレーターを使ってメソッドを定義する
class User(object):
  def __init__(self, name):
    self.__name = name
  
  def hi(self):
    print(f'Hi, {self.__name}')
  
  # name という getter / setter を作成する
  @property
  def name(self):
    return self.__name

  @name.setter
  def name(self, name):
    self.__name = name

api = User('Aoi')
print(aoi.name)  # => 'Aoi'
aoi.name = 'Kiriya'
print(aoi.name)  # => 'Kiriya'
api.hi() # => Hi, Kiriya

2. property(getter_method, setter_method) 関数を使う方法

property_name = property(getter_method, setter_method) で getter / setter を作成できる

class User(object):
  def __init__(self, name):
    self.__name = name

  def hi(self):
    print(f'Hi, {self.__name}')

  def get_name(self):
    return self.__name

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

  # getter で get_name, setter で set_name を呼び出す
  name = property(get_name, set_name)

ran = User('Ran')
print(ran.name)  # => 'Ran'
ran.name = 'Shibuki'
print(ran.name)  # => 'Shibuki'
ran.hi()  # => 'Hi, Shibuki'

₍ ᐢ. ̫ .ᐢ ₎ デキタ!


[参考]

Git 直前のコミットメッセージを編集したい

typo の達人なので

git commit --amend

$ git commit --amend

修正するコミットメッセージを直接渡したいときは

$ git commit --amend -m "message"

amend … 修正する

英語ちから is 無 だからすぐ忘れる…

おわり


わかばちゃんと学ぶ Git使い方入門

わかばちゃんと学ぶ Git使い方入門