クラスとオブジェクト
これまで扱ってきたすべての型(文字列、リスト、辞書)は、実はクラスです。"hello".upper() を呼び出すとき、あなたは文字列オブジェクトのメソッドを呼び出しています。クラスを使うと、独自のデータと振る舞いを持った独自の型を定義できます。Player クラスは名前、スコア、レベルを保持し、自分自身を表示する方法を知ることができます。
設計図とインスタンス
クラスは設計図です。インスタンスはその設計図から作られた具体的な存在です。必要な数だけインスタンスを作ることができ、それぞれが独自のデータを持ちながら、クラスで定義された同じメソッドを共有します。
class Dog:
def bark(self):
print("Woof!")
rex = Dog()
luna = Dog()
rex.bark() # "Woof!"
luna.bark() # "Woof!"Dog はクラスです。rex と luna はインスタンスです: 2匹の異なる犬で、それぞれがクラスで定義された同じ振る舞いを共有しています。
__init__ と self
__init__ は、新しいインスタンスを作成するときにPythonが自動的に呼び出すメソッドです。ここでオブジェクトの初期データをセットアップします。self は、メソッドが操作対象の特定のインスタンスを参照する方法で、常に最初のパラメータです。
class Player:
def __init__(self, name, score=0):
self.name = name
self.score = score
def add_points(self, points):
self.score += points
def display(self):
print(f"{self.name}: {self.score} points")
あきら = Player("あきら")
ゆうき = Player("ゆうき", score=50)
あきら.add_points(30)
あきら.display() # "あきら: 30 points"
ゆうき.display() # "ゆうき: 50 points"self.name と self.score はインスタンス属性です: それらはクラス自体ではなく、特定のオブジェクトに属します。各 Player インスタンスは独自の name と score を持ちます。
メソッド
クラス内で定義された関数はすべてメソッドです。インスタンスメソッドは常に self を最初のパラメータとして持ちます。Pythonがそれを自動的に渡します。メソッドは self を通してインスタンスのデータを読み書きできます。
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def scale(self, factor):
self.radius *= factor
return self # selfを返すことでチェーン可能: c.scale(2).scale(0.5)
c = Circle(5)
print(c.area()) # 78.53975
c.scale(2)
print(c.area()) # 314.159クラス変数とインスタンス変数
クラスに直接定義された変数(__init__ の内部ではなく)はクラス変数です。すべてのインスタンスが同じクラス変数を共有します。__init__ 内で self に設定された変数はインスタンス変数で、各オブジェクト固有のものです。
class Player:
max_lives = 3 # クラス変数、すべてのPlayerで共通
def __init__(self, name):
self.name = name # インスタンス変数、各Player固有
self.lives = Player.max_lives
def die(self):
self.lives -= 1
あきら = Player("あきら")
ゆうき = Player("ゆうき")
Player.max_lives = 5 # 現在と将来のすべてのインスタンスで変更すべてのインスタンスで共有される値にはクラス変数を使います: 定数、カウンター、デフォルト値など。オブジェクトごとに異なるデータにはインスタンス変数を使います。
__str__ と __repr__
__str__ は print() やf文字列がオブジェクトをどう表示するかを制御します。__repr__ はコンソールやデバッグで表示される開発者向けの表示を制御します。常に __repr__ を定義しましょう。デバッグ表示とは別にユーザー向けのきれいな表示が欲しいときは __str__ を定義しましょう。
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return f"{self.name} ({self.score} pts)"
def __repr__(self):
return f"Player(name={self.name!r}, score={self.score})"
あきら = Player("あきら", 87)
print(あきら) # "あきら (87 pts)" (__str__を使用)
repr(あきら) # "Player(name='あきら', score=87)" (__repr__を使用)常に __repr__ を定義しましょう。デバッグ表示とは別にユーザー向けのきれいな表現が欲しいときは __str__ を定義しましょう。__repr__ のみが定義されている場合、Pythonはそれを両方に使用します。
プライベートの慣例
Pythonには真の意味でのプライベート変数はありませんが、名前の先頭にアンダースコアを1つ付けること(_balance)は「これは内部用なので、クラスの外から直接使わないでください」というシグナルとなる慣例です。言語によって強制されるものではなく、他の開発者へのコミュニケーションです。
class BankAccount:
def __init__(self, balance):
self._balance = balance # _は「触らないで」を意味する
def deposit(self, amount):
if amount > 0:
self._balance += amount
def balance(self):
return self._balanceダブルアンダースコア(__name)は名前マングリングを引き起こします。Pythonはサブクラスでの衝突を避けるために属性を _ClassName__name にリネームします。これが必要になることはめったにありません。シングルアンダースコアがほとんどのコードでの慣例です。
継承
クラスは別のクラスから継承でき、そのすべての属性とメソッドを自動的に取得します。その後、サブクラスで特定のメソッドをオーバーライドして、その振る舞いを変更できます。これにより、共通の基底クラスを再利用しつつ、必要な箇所で特殊化できます。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
pets = [Dog("Rex"), Cat("Luna"), Dog("Max")]
for pet in pets:
print(pet.speak())Dog と Cat は Animal から __init__ を継承するので、自身のものを持つ必要はありません。speak() を自分たち固有の振る舞いでオーバーライドします。
super()
super() は親クラスのメソッドを呼び出します。親の振る舞いを完全に置き換えるのではなく、拡張したい場合に使用します: 親の __init__ を呼び出してそのセットアップを実行し、その上にサブクラスに必要なものを追加します。
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof") # Animal.__init__を呼び出す
self.tricks = [] # 追加のものを足す
def learn(self, trick):
self.tricks.append(trick)
rex = Dog("Rex")
rex.learn("sit")
print(rex.tricks) # ["sit"]サブクラスが独自の __init__ を持ち、親も持っている場合は常に super().__init__() を呼び出しましょう。
クラスメソッドと静的メソッド
@classmethod はインスタンスではなくクラス自身を受け取るメソッドを作成します。代替のコンストラクタに便利です: 文字列、ファイル、または別の形式からインスタンスを作成します。@staticmethod は組織化の目的でクラス内に存在するただの関数で、インスタンスもクラスも受け取りません。
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
@classmethod
def from_string(cls, data):
name, score = data.split(",")
return cls(name, int(score))
あきら = Player.from_string("あきら,87")class Player:
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) >= 2
Player.is_valid_name("あきら") # True
Player.is_valid_name("A1") # False代替コンストラクタには @classmethod を使用しましょう。論理的にクラスに属するが、インスタンスやクラスのデータを必要としないユーティリティ関数には @staticmethod を使用しましょう。
@property
@property を使うと、括弧なしで、メソッドを属性のようにアクセスできます。他の属性から計算される値で、シンプルな属性アクセスとして読むのが自然な場合に使います。
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
@property
def diameter(self):
return self.radius * 2
c = Circle(5)
print(c.area) # 78.53975 (属性のように見えて、メソッドのように実行される)
print(c.diameter) # 10プロパティは計算値に便利です: 他の属性から派生し、() なしでアクセスするのが自然なものに。
実践
インスタンス属性、メソッド、@property、__str__ を持つ Player クラス:
class Player:
max_lives = 3
def __init__(self, name: str):
self.name = name
self.score = 0
self.lives = Player.max_lives
def earn_points(self, amount: int) -> None:
self.score += amount
def take_hit(self) -> bool:
self.lives -= 1
return self.lives > 0
@property
def is_alive(self) -> bool:
return self.lives > 0
def __str__(self) -> str:
return f"{self.name} | Score: {self.score} | Lives: {self.lives}"
あきら = Player("あきら")
あきら.earn_points(50)
あきら.take_hit()
print(あきら) # "あきら | Score: 50 | Lives: 2"
print(あきら.is_alive) # True
