かもメモ

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

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って自由な言語なんだな〜という印象です。


[参考]

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