Hatena::ブログ(Diary)

PASL RSSフィード

2013-04-01

インスタンスメソッド・クラスメソッド・スタティックメソッド

| 22:04 |  インスタンスメソッド・クラスメソッド・スタティックメソッドを含むブックマーク  インスタンスメソッド・クラスメソッド・スタティックメソッドのブックマークコメント

Pythonで扱われるこれらのメソッドの違いを調べてみた。結論から言うと、これらのメッソドの違いは「インスタンス変数にアクセス可能か」、「継承時に親・子クラスのどちらのクラス変数を参照するか」の2つの観点で異なる。

メソッドインスタンス変数へのアクセスどちらのクラス変数を参照するか
インスタンスメソッド
クラスメソッド不可
スタティックメソッド不可

ここで注意したいのは「親クラス」のクラス変数を変更すると「必ず」子クラスのクラス変数が変更されるということ。また当然のこととして、子クラスのインスタンス変数変更が親クラスのインスタンス変数を変更したりはしない。

具体例をつくってみた。method_example.pyというファイル名で保存。

class Boku(object):
    # これはクラス変数
    subject = "ぼくは"
    name = "ドラえもん"
    
    def __init__(self, nickname):
        self.nickname = nickname # これはインスタンス変数
    
    # これはインスタンスメソッド
    def get_name_instance(self, class_var=False):
        if class_var:
            return Boku.subject + Boku.name + ""
        else:
            return "ぼくは" + self.nickname + ""

    def set_name_instance(self, nickname, class_var=False):
        if class_var:
            Boku.name = nickname
        else:
            self.nickname = nickname

    def dialogue_instance(self, dialogue, class_var=False):
        return self.get_name_instance(class_var) + dialogue + ""
    
    # これはクラスメソッド
    @classmethod
    def get_name_class(cls):
        return cls.subject + cls.name + ""

    @classmethod
    def set_name_class(cls, nickname):
        cls.name = nickname

    @classmethod
    def dialogue_class(cls, dialogue):
        return cls.get_name_class() + dialogue + ""
    
    # これはスタティックメソッド
    @staticmethod
    def get_name_static():
        return "ぼくは" + Boku.name + ""

    @staticmethod
    def set_name_static(nickname):
        Boku.name = nickname

    @staticmethod
    def dialogue_static(dialogue):
        return get_name_static() + dialogue + ""

class Ore(Boku):
    subject = "オレは"

インスタンス変数へのアクセス

これは当たり前のことと言えば当然のことだけれども、インスタンス変数にはインスタンスメソッドからしかアクセスできない。クラスメソッドは第一引数にクラスを受け取り、第二引数以下はただの引数を受け取る。したがって第一引数にselfを書いてもクラス変数を参照しようとするだけで、インスタンス変数を参照しようとしない。上の例で言えば

@classmethod
def set_name_class(cls, nickname):
    cls.name = nickname

のclsをselfに変えてもself.nameはクラス変数nameを定義するにすぎない。第二引数以下にselfを書いても、「selfって変数にnameなんてないんだけど」と怒られるだけ。スタティックメソッドはクラスメソッドの第二引数以下だけの場合と同じ。

クラス変数へのアクセス

これも当然といえば当然で、すべてのメソッドを使ってクラス変数にアクセスできる。クラスメソッドやスタティックメソッドはもちろん、インスタンスメソッドでもクラス変数にアクセスできる。

 1 >>> import method_example
 2 >>> boku = method_example.Boku("ジャイアン")
 3 >>> boku.get_name_instance()
 4 'ぼくはジャイアン。'
 5 >>> boku.get_name_instance(class_var=True)
 6 'ぼくはドラえもん。'
 7 >>> boku.get_name_class()
 8 'ぼくはドラえもん。'
 9 >>> boku.get_name_static()
10 'ぼくはドラえもん。'

クラス変数たるsubjectとnameがそれぞれ 'ぼくは' と 'ドラえもん' なので、どのメソッドを使っても返り値は同じ。当然3行目のboku.get_name_instance()はインスタンス変数を参照しているので、インスタンスを定義した時に指定した 'ジャイアン' が返されている。

いずれかのメソッドでクラス変数を変更すると当然すべてのメソッドで返り値が変わる。たとえばインスタンスメソッドを使ってクラス変数を変更したら

11 >>> boku.set_name_instance("スネオ", class_var=True)
12 >>> boku.get_name_instance()
13 'ぼくはジャイアン。'
14 >>> boku.get_name_instance(class_var=True)
15 'ぼくはスネオ。'
16 >>> boku.get_name_class()
17 'ぼくはスネオ。'
18 >>> boku.get_name_static()
19 'ぼくはスネオ。'

当然インスタンス変数は変更されない。同じ事がクラスメソッドやスタティックメソッドを使っても起こる。

20 >>> boku.set_name_class("のび太")
21 >>> boku.get_name_instance()
22 'ぼくはジャイアン。'
23 >>> boku.get_name_instance(class_var=True)
24 'ぼくはのび太。'
25 >>> boku.get_name_class()
26 'ぼくはのび太。'
27 >>> boku.get_name_static()
28 'ぼくはのび太。'

どちらのクラス変数を参照するか

次にクラスを継承してみる。Bokuクラスを継承したOreクラスを使う。変更点はただ一つ。クラス変数subjectが 'ぼくは' から 'オレは' に変更。このときクラス変数nameも継承されているので、Oreクラスのname変数も書き換わっている。

29 >>> ore = method_example.Ore("しずかちゃん")
30 >>> ore.get_name_instance()
31 'ぼくはしずかちゃん。'
32 >>> ore.get_name_instance(class_var=True)
33 'ぼくはのび太。'
34 >>> ore.get_name_class()
35 'オレはのび太。'
36 >>> ore.get_name_static()
37 'ぼくはのび太。'

32行目get_name_instance(class_var=True)と36行目get_name_static()はどちらも「Boku.subject」を参照しているので、まだ「ぼくは」を返している。一方、クラスメソッドたるget_name_class()は「cls.subject」を参照している。clsには当該クラスが入るので、boku.get_name_class()のcls.subjectにはBoku.subjectが、ore.get_name_class()のcls.subjectにはOre.subjectが入る。

子クラスのクラス変数変更が親クラスのクラス変数を変更するか

子クラスのクラス変数を変更してみよう。スタティックメソッドを使った場合

38 >>> ore.set_name_static("出来杉君")
39 >>> ore.get_name_instance()
40 'ぼくはしずかちゃん。'
41 >>> ore.get_name_instance(class_var=True)
42 'ぼくは出来杉君。'
43 >>> ore.name
44 '出来杉君。'
45 >>> boku.name
46 '出来杉君。'

そういえばこれまで使ってなかったが、クラス変数は43・45行目のようにアクセスできる。インスタンスメソッドとスタティックメソッドはBoku.nameを参照しているので、それを継承したOreクラスの関数ore.set_name_static()を用いるとOre.nameだけでなくBoku.nameも変更される。

それではクラスメソッドを使ってクラス変数を変更してみる。

47 >>> ore.set_name_class("ドラミちゃん")
48 >>> ore.name
49 'ドラミちゃん'
50 >>> boku.name
51 '出来杉君'

クラスメソッドはcls.nameを用いているので、ore.set_name_class()はOre.nameだけを変更するにすぎず、Boku.nameは変更されない。

トラックバック - http://d.hatena.ne.jp/kiwamu_i/20130401/1364821445
リンク元