Classesは、データと振る舞いを組み合わせたものです。クラスは、まとまったデータと振る舞いのコピー、つまりinstancesを作るために使われ、それらは一般にobjectsと呼ばれます。オブジェクトは、現実世界の存在(車や猫など)を表すこともできますし、より抽象的な概念(整数、乗り物、哺乳類など)を表すこともできます。クラスは、オブジェクト指向プログラミング(OOP)という考え方に欠かせません。この考え方では、問題を1つ以上のオブジェクトとしてモデル化することが求められます。オブジェクトは互いに作用し合い、状態を保ち、データに基づいて動作します。
クラスとは、新しい_オブジェクトの型_を定義したもので、そこから新しいinstancesが作られます。多くの場合、クラスはデータ(fields、attributes、properties、data members、variablesなどと呼ばれます)と、そのデータを操作するコードや関数(methodsと呼ばれます)をひとつにまとめたものです。この意味で、クラスは_設計図_であり、同じような型のオブジェクトをたくさん作り、使うための指示書でもあります。複雑なプログラムには多くのクラスがあり、それぞれがさまざまな種類のオブジェクトを作り出します。クラスからオブジェクトを作り、カスタマイズする過程はinstantiation(クラスのインスタンスを作ること)と呼ばれます。
Pythonでのクラス定義はとてもシンプルです。
class MyClass:
# Class body goes here
クラスのフィールド(プロパティ、属性、データメンバー、変数とも呼ばれます)は、クラスの本体に追加できます。
class MyClass:
number = 5
string = "Hello!"
MyClassのインスタンス(オブジェクト)を作り、名前を付けて束縛できます。
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
Class attributesは、クラスから作られたすべてのオブジェクト(またはインスタンス)で共有され、dot notationでアクセスできます。.をオブジェクト名の後に置き、その後に属性名を続ける書き方です。
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
クラス属性は、他のどのメソッドよりも先に、クラス本体で定義します。クラス属性はクラスに属しているため、インスタンス間で共有できます。これらの属性はクラスのすべてのインスタンスで共有されるので、その値にはクラスから直接アクセスしたり、変更したりできます。クラス属性の値を変更すると、値は_そのクラスからインスタンス化されたすべてのオブジェクトで_変わります。
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
常にデータが同期されたオブジェクトがたくさんあっても、あまり役には立ちません。幸いなことに、クラスから作られたオブジェクトは、それぞれ固有のinstance attributes(インスタンスプロパティ、変数、フィールド)でカスタマイズできます。名前のとおり、インスタンス属性はオブジェクトごとに固有で、個別に変更できます。
__init__()でオブジェクトのインスタンス化をカスタマイズする特別な"dunder method"(「double underscore method」の略)である__init__()は、クラスのインスタンスをカスタマイズするために使われ、オブジェクトのインスタンス属性やプロパティを初期化するのにも使えます。インスタンス属性の初期化を担うことから、__init__()はclass constructorやinitializerとも呼ばれます。__init__()はselfという必須の仮引数を1つ取ります。これは、これから作られる(初期化される)オブジェクトを指します。インスタンス属性やプロパティのためのデータは、self仮引数の後に続けて、__init__()の引数として渡すことができます。
以下では、MyClassにlocation_xとlocation_yというインスタンス属性があります。見てのとおり、この2つの属性には、__init__()に渡されたlocation(タプル)引数の1番目と2番目のインデックスが代入されています。これで、クラスをインスタンス化してオブジェクトを作ると、そのクラスインスタンスのlocation_x属性とlocation_y属性が初期化されます。
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
上のMyClassを初期化するときは、引数を1つ渡すだけでよいことに注意してください。クラスを呼び出すと、Pythonがselfを渡してくれます。
インスタンス変数を作るもう1つの方法は、クラス変数にオブジェクト経由でアクセスし、それを別の値に変更してしまうことです。
>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'
>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'
>>> obj_one.string
'Hello!'
では、クラス変数を変更するとどうなるか見てみましょう。
>>> MyClass.string = "World!"
>>> obj_two.string
# obj_two.string has not changed
'Hi!'
>>> obj_one.string
# obj_one.string changed
'World!'
属性stringは、obj_twoの場合には_インスタンス変数_になっていますが、obj_oneでは_クラス変数_のままです。
これはクラスの内部からも行えます。
class Demo:
new_var = 3
def add_two(self):
self.new_var += 2
<object>.add_two()が呼ばれ、self.new_var += 2が実行された瞬間に、new_varはクラス変数から同名のインスタンス変数に変わります。
これは、クラスのすべてのインスタンスがある属性を同じ値で開始する必要がある場合、初期化のときに便利です。ただし、そうするとインスタンス変数がシャドーイングするため、クラス変数はそれをシャドーイングしているインスタンスからアクセスできなくなります。このような状況では、__init__()メソッドでself.<attribute>としてインスタンス属性を設定するほうが、安全で明確かもしれません。
methodは、クラス自身(クラスメソッドと呼ばれ、後の演習で詳しく扱います)か、そのクラスの_インスタンス_(オブジェクト)に束縛されたfunctionです。オブジェクトやインスタンスを操作するメソッドは、selfを最初の仮引数として定義する必要があります。残りの仮引数は、通常の(束縛されていない)関数と同じように定義できます。クラスを操作するメソッドは、@classmethodデコレーターを付け、(慣例として)clsを最初の仮引数として定義します。クラスメソッドは、クラスから先にオブジェクトを作らなくても、クラスに対して直接呼び出せます。
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Class method. Uses the @classmethod decorator, and cls as the first parameter.
# Can be called without first making an instance of the class.
@classmethod
def change_string(cls, new_string):
#Class attributes are referred to with cls.
cls.string = new_string
return cls.string
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
属性へのアクセスと同じように、メソッドの呼び出しも、オブジェクト名の後に.を置き、その後にメソッド名を続けるだけです。呼び出されたメソッドは、最初の引数としてオブジェクトのコピーを受け取る必要はありません。Pythonがselfを自動的に補ってくれます。
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
クラス属性には、クラスの外部からアクセスするのと同じ方法で、インスタンスメソッドの内部からもアクセスできます。
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
passによる実装の仮置きとスタブこれまでのコンセプト演習や、プラクティス演習のスタブでは、関数の本体で実際のコードの代わりにpassキーワードが使われているのを見たことがあるでしょう。
passキーワードは、文法的に有効なプレースホルダーです。空の関数やクラス定義に対して、Pythonが構文エラーを出すのを防いでくれます。要するに、Pythonインタプリターに『心配しないで! ここにはいずれ_必ず_コードを書きます。まだ書いていないだけです』と伝えるようなものです。
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or place-holding the body of this method.
pass