見出し画像

Pythonをはじめよう!- クラス- 1

スコープと名前空間

異なるスコープと名前空間がどのように参照されるか、また global および nonlocal が変数の束縛にどう影響するか、以下例示です。

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

このコード例の出力は:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

ローカルな 代入は 関数scope_test の spam を変更しません。 nonlocal 代入は scope_test 上の spamを変更し、 global 代入はモジュールレベルで変更しています。

 global 代入の前には spam は何も束縛されていなかったことも分かります。

クラスの作り方

クラス定義の最も単純な形式は、次のようになります:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

関数定義 (def 文) と同様、クラス定義して使えるようにします。 (クラス定義を if 文の分岐先や関数内部に置くことも可能。)

実際には、クラス定義の内側にある文は、通常の関数定義もできるし、他の文を書くこともできます。

クラス定義することで新たな名前空間が作成されローカルな名前空間として利用できるようになります 

ローカルな変数に対する全ての代入はこの新たな名前空間に入ります。特に、関数定義を行うと、新たな関数の名前はこの名前空間に結び付けられます。

クラスオブジェクト

クラスオブジェクトでは2種類の動作、属性参照とインスタンス生成をサポートしています。

属性参照 (attribute reference) は、Python におけるすべての属性参照は標準的な構文、 obj.name を使います。クラスオブジェクトが生成された際にクラスの名前空間にあった名前すべてが有効な属性名です。従って、以下のようなクラス定義では:

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

MyClass.i “と “MyClass.f “は属性参照であり、それぞれ整数と関数オブジェクトを返します。クラス属性に代入を行うこともできます。従って、 MyClass.i の値を代入して変更できます。 __doc__ も有効な属性で、そのクラスに属している docstring、この場合は "A simple example class" を返します。

クラスの インスタンス化 (instantiation) は関数の表記法と同じです。

クラスオブジェクトは、単にクラスの新しいインスタンスを返す引数がない関数と同じ表記となります。上記のクラスだと

x = MyClass()

は、クラスの新しい インスタンス (instance) を生成し、そのオブジェクトをローカル変数 x へ代入します。

このクラスのインスタンス生成を行うと、空のオブジェクトを生成します。多くのクラスは、オブジェクトを作成する際に初期化が必要です。そのために、クラスには __init__() という初期化のメソッド定義することができます。

def __init__(self):
    self.data = []

クラスが __init__() メソッドを定義すると、クラスのインスタンスに対して自動的に __init__() を呼び出します。

新たな初期済みのインスタンスを次のようにして得ることができます:

x = MyClass()

より大きな柔軟性を持たせるために、 __init__() メソッドに複数の引数をもたせることができます。その場合、次の例のように、クラスのインスタンス生成操作に渡された引数は __init__() に渡されます。例えば、

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart

x = Complex(3.0, -4.5)
x.r, x.i

(3.0, -4.5)

インスタンスオブジェクト

インスタンスオブジェクトは属性の参照ができます。有効な属性名には (データ属性およびメソッドの) 二種類あります。

データ属性 (data attribute) は、データ属性を宣言する必要はありません。ローカルな変数と同様に、これらの属性は最初に代入された時点で出力されます。
例えば、上で生成した MyClass のインスタンス x に対して、次のコードを実行すると、値 16 を印字し、 x の痕跡は残りません。

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)

del x.counter

del x.counter

で消去。del文は、データの削除を行います。

もうひとつのインスタンス属性は メソッド (method) です。メソッドとは、オブジェクトに "属している" 関数のことです。(Python では、メソッドという用語はクラスインスタンスだけのものではありません。オブジェクト型にもメソッドを持つことができます。例えば、リストオブジェクトには、 append, insert, remove, sort などといったメソッドがあります。とはいえ、以下では特に明記しない限り、クラスのインスタンスオブジェクトのメソッドだけを意味するものとして使うことにします。)

インスタンスオブジェクトで有効なメソッド名が定義されます。例では、MyClass.f は関数なので、x.f はメソッドです。しかし、MyClass.i は関数ではないので、x.i はメソッドではありません。

メソッドオブジェクト

普通、メソッドはバインドされた直後に呼び出されます:

x.f()

MyClass の例では、上のコードは文字列 'hello world' を返します。変数に代入し、必要な時に呼び出すことができます。

xf = x.f
while True:
    print(xf())

hello world を時が終わるまで印字し続けます。


Pythonでは、クラスメソッドを定義する場合、第一引数は必ずselfを使わなければいけません。

引数を指定するのはだい2引数以降となります。


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

一般的に、インスタンス変数はそれぞれのインスタンスについて固有のデータのためのもので、クラス変数はそのクラスのすべてのインスタンスによって共有される属性やメソッドのためのものです:

class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs
'canine'
>>> e.kind                  # shared by all dogs
'canine'
>>> d.name                  # unique to d
'Fido'
>>> e.name                  # unique to e
'Buddy'

共有データはリストや辞書のような mutable オブジェクトが便利ですが、以下のコードの tricks リストはクラス変数として使われるべきではありません、理由としては一つのリストがすべての Dog インスタンスによって共有されることになるからです。

class Dog:

    tricks = []             # mistaken use of a class variable

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

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                # unexpectedly shared by all dogs
['roll over', 'play dead']

このクラスの正しい設計としてはインスタンス変数にします。

class Dog:

    def __init__(self, name):
        self.name = name
        self.tricks = []    # creates a new empty list for each dog

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']


この記事が気に入ったらサポートをしてみませんか?