1. メソッドをクラスメソッドにするときは、デコレータを使う
あるメソッドをクラスメソッドにするとき、 Python ではデコレータを使うことができる。
2.1 組み込み関数 によると、(太字は引用者による)
classmethod( function)
function のクラスメソッドを返します。 クラスメソッドは、インスタンスメソッドが暗黙の第一引数としてインスタンスをとるように、第一引数としてクラスをとります。クラスメソッドを宣言するには、以下の書きならわしを使います:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
ふーむ、ここでも第一引数が必須で、それがクラスを参照するということか。Python のこういう書き方は、最初とっつきにくいと感じた。 (+_+)
デコレータの書き方としては、関数の前に
@classmethod
を置けばいいだけ。
2. クラスメソッドを試してみる
デコレータを使う場合
class Hoge: @classmethod def sayHoge(cls, x): print x + " hoge" Hoge.sayHoge("ABC") Hoge().sayHoge("abc")
結果は、
ABC hoge abc hoge
上記の通り、クラス経由、インスタンス経由で呼出すことができる。
デコレータを使わない場合
デコレータの記法が使える前は、次のように組み込み関数
classmethod()
を使っていた。
class OldHoge(): def sayHoge(cls, x): print x + " hoge" sayHoge = classmethod(sayHoge) OldHoge.sayHoge("DEF") OldHoge().sayHoge("def")
つまり、classmethod() が、普通のメソッドをクラスメッドになるようにしている。
この使い方を見ると、デコレータは、突然導入された特殊な記法ではなく、関数をラップしているだけだと実感できる。
3. staticmethod() もある
ところで、一つ気になることがある。
クラスメソッドは C++ や Java における静的メソッドとは異なります。そのような機能を求めているなら、staticmethod() を参照してください。 バージョン 2.2 で 新たに追加 された仕様です。 バージョン 2.4 で 変更 された仕様: 関数デコレータ構文を追加しました
(同上の classmethod() の説明より、太字は引用者による)
使い方を見てみると、先ほどの classmethod() とは大きく違う点がある。
2.1 組み込み関数 によると、
staticmethod( function)
function の静的メソッドを返します。 静的メソッドは暗黙の第一引数を受け取りません。静的メソッドの宣言は、以下のように書き慣わされます:
class C: @staticmethod def f(arg1, arg2, ...): ...
staticmethod() には、classmethod() にあった 第 1 引数は必要ない。
Python における静的メソッドは Java や C++ における静的メソッドと類似しています。より進んだ概念については、 classmethod() を参照してください。 バージョン 2.2 で 新たに追加 された仕様です。 バージョン 2.4 で 変更 された仕様: 関数デコレータ構文を追加しました(同上より)
Java の static メソッドのつもりで書いたので、混乱してきた。
ここまでを整理してみる。
- classmethod() に似た staticmethod() という関数がある。
- 両方ともバージョン 2.2 で追加され、2.4 でデコレータ構文を使うことができるようになった。
- classmethod() の方が staticmethod() より進んだ概念らしい。
問題は最後の項目。何がどう進んだ概念だろうか?(@_@)
classmethod() との違い
3.2 標準型の階層 によると、
静的メソッド (static method) オブジェクト 静的メソッドは、上で説明したような関数オブジェクトからメソッドオブジェクトへの変換を阻止するための方法を提供します。(…) クラスメソッドオブジェクト クラスメソッドオブジェクトは、静的メソッドオブジェクトに似て、別のオブジェクトを包むラッパであり、そのオブジェクトをクラスやクラスインスタンスから取り出す方法を代替します。このようにして取得したクラスメソッドオブジェクトの動作については、上の ``ユーザ定義メソッド (user-defined method)'' で説明されています。(…)
うーん、よくわからない ^^;
newbie-question: difference between classmethod and staticmethod in Python2.2 によると、
Use a staticmethod when you know which class you want to access as you are writing the code. (…)
Use a classmethod if you have a class hierarchy and want the method to operate on the actual class used in the call rather than the class where it was defined:
つまり、staticmethod は、アクセスするためのクラスがわかっている必要がある。それに対して、 classmethod は違う。多分、この違いについて「より進んだ概念」と称しているのかな?
具体的な違いについては、コードを見た方が理解が早い。上記の引用先には、コードの例が書かれている。自分でも試しておこう。
予め断わっておくと、先ほど書いたコードは、classmethod のメリットを全く享受していない。理由は、@classmethod にしたメソッドの第 1 引数である、当該クラスへの参照を全く利用していないため。
4. クラスメソッドを試してみる
次のように、@classmethod を使ったクラスと、@staticmethod を使ったクラスを作り、それぞれサブクラスを作成する。
# ---------------------------------------------------------- # @classmethod を使った場合 class Hoge: @classmethod def setHoge(cls, x): # クラス名が固定されていない cls.HOGE = x # クラス変数を設定 Hoge.setHoge("100"); print '*** Hoge.setHoge("100")' print "Hoge.HOGE: " + Hoge.HOGE class SubHoge(Hoge): pass print "SubHoge.HOGE: " + SubHoge.HOGE # クラス変数をサブクラスから設定 SubHoge.setHoge("200"); print '*** SubHoge.setHoge("200")' print "Hoge.HOGE: " + Hoge.HOGE # サブクラスでの変更が影響しない print "SubHoge.HOGE: " + SubHoge.HOGE # ---------------------------------------------------------- # @staticmethod を使った場合 print "-"*30 class Piyo(): @staticmethod def setPiyo(x): # クラス名 Piyo が固定されている Piyo.PIYO = x # クラス変数を設定 Piyo.setPiyo("100"); print '*** Piyo.setPiyo("100")' print "Piyo.PIYO: " + Piyo.PIYO class SubPiyo(Piyo): pass print "SubPiyo.PIYO: " + SubPiyo.PIYO # クラス変数をサブクラスから変更 SubPiyo.setPiyo("200"); print '*** SubPiyo.setPiyo("200")' print "Piyo.PIYO: " + Piyo.PIYO # サブクラスでの変更が影響 print "SubPiyo.PIYO: " + SubPiyo.PIYO
結果は、
*** Hoge.setHoge("100") Hoge.HOGE: 100 SubHoge.HOGE: 100 *** SubHoge.setHoge("200") Hoge.HOGE: 100 SubHoge.HOGE: 200 ------------------------------ *** Piyo.setPiyo("100") Piyo.PIYO: 100 SubPiyo.PIYO: 100 *** SubPiyo.setPiyo("200") Piyo.PIYO: 200 SubPiyo.PIYO: 200
上記の結果を見てわかるように、classmethod を使った方では、cls. で修飾してクラス変数にアクセスしているため、サブクラスでの変更が影響していない。(ということだと思う ^^;)
5. Java の static メソッドとの比較
Java での動作も確認しておく。
public class Piyo { public static String PIYO = ""; public static void setPiyo(String x){ Piyo.PIYO = x; } public static void main(String[] args) { // クラス変数を設定する Piyo.setPiyo("100"); System.out.println("Piyo.setPiyo(\"100\");"); System.out.println("Piyo.Piyo: " + Piyo.PIYO); System.out.println("SubPiyo.PIYO: " + SubPiyo.PIYO); // クラス変数をサブクラスから設定する SubPiyo.setPiyo("200"); System.out.println("SubPiyo.setPiyo(\"200\");"); System.out.println("Piyo.PIYO: " + Piyo.PIYO); // サブクラスでの設定が影響している System.out.println("SubPiyo.PIYO: " + SubPiyo.PIYO); } } class SubPiyo extends Piyo{}
結果は、
Piyo.setPiyo("100"); Piyo.Piyo: 100 SubPiyo.PIYO: 100 SubPiyo.setPiyo("200"); Piyo.PIYO: 200 SubPiyo.PIYO: 200
Python の @staticmethod と同じ動作であることがわかる。
0コメント:
コメントを投稿