1: 2017-01-07 (土) 05:49:15 njf |
現: 2017-01-10 (火) 19:28:19 njf |
| + | Pythonでは関数ベースでもクラスを使ったオブジェクト指向でもプログラムも書けるようになっています。 |
| + | |
| + | 小規模のツールなどでは関数のみを利用しても十分ですが、ある程度複雑な処理や、再利用しやすいプログラムを書くならクラスを使った方が便利です。 |
| + | |
| + | こちらではPythonのクラスについて簡単にまとめます。 |
| + | |
| *基本 [#u61fc2c4] | | *基本 [#u61fc2c4] |
| | | |
| クラスの定義は「class」で行います。 | | クラスの定義は「class」で行います。 |
| + | クラス名の後には「(obect)」を付けます。 |
| + | これは省略可能ですが、継承などで一部の記法が使えなくなるので付けるようにするのがオススメです。 |
| + | メソッドの定義は「def」です。 |
| コンストラクタは「__init__」で、インスタンス化に「new」は不要です。 | | コンストラクタは「__init__」で、インスタンス化に「new」は不要です。 |
| | | |
- | class TestClass: | + | class TestClass(object): |
| def __init__(self): | | def __init__(self): |
| print "construct!" | | print "construct!" |
| + | |
| testClass = TestClass() | | testClass = TestClass() |
| | | |
| 結果 | | 結果 |
| construct! | | construct! |
| + | |
| + | また全てのメソッドは「self」を引数としなければならず、これは自身のインスタンスを表します。 |
| + | 他の言語で言うところの「this」とほぼ同じです。 |
| + | |
| + | class TestClass(obect): |
| + | |
| + | def whatIsSelf(self): |
| + | print type(self) |
| + | print self |
| + | |
| + | testClass = TestClass() |
| + | |
| + | testClass.whatIsSelf() |
| + | |
| + | 結果 |
| + | <class '__main__.TestClass'> |
| + | <__main__.TestClass object at 0x10bcdb8d0> |
| + | |
| + | |
| + | この「self」を使ってクラス内のメソッドを呼びます。 |
| + | 多くの他言語の「this」と違って、省略することはできません。 |
| + | |
| + | class CallMethod(obect): |
| + | def method1(self): |
| + | print "method1 called" |
| + | |
| + | def method2(self): |
| + | self.method1() |
| + | |
| + | callMethod = CallMethod() |
| + | |
| + | callMethod.method2() |
| + | |
| + | 結果 |
| + | method1 called |
| + | |
| + | クラス変数も定義できます。 |
| + | クラス変数はクラス名でも「self」を含むインスタンスからでも参照可能です。 |
| + | |
| + | class ClassVariables(obect): |
| + | a = 1 |
| + | b = 2 |
| + | |
| + | def __init__(self): |
| + | print self.b |
| + | self.a = 10 |
| + | self.c = 11 |
| + | |
| + | classVariables = ClassVariables() |
| + | |
| + | print ClassVariables.a |
| + | print classVariables.a |
| + | print classVariables.c |
| + | 結果 |
| + | 2 |
| + | 1 |
| + | 10 |
| + | 11 |
| + | |
| + | ここでインスタンスでそのクラス変数を参照するときには、もしインスタンスでその変数を変更していなければ初期値を参照し、そうでなければ変更後の値を参照します。また、インスタンスの変数はメソッドの中で始めて定義することもできます。 |
| + | |
| + | 言い換えれば、インスタンスの変数はメソッドで代入したときに始めて定義され、その後はクラス変数よりインスタンスの変数を優先して参照すると言うことです。 |
| + | |
| + | これは他のprototype型のクラス定義を採用している言語と同様です。 |
| + | |
| + | *参照の制限 [#y20ef82a] |
| + | |
| + | Pythonではprivateなどの参照範囲を制限する修飾子はサポートされていません。 |
| + | 代わりに「_」(アンダーバー)で始まる要素には慣習的にクラス外から参照しない(しようと思えばできる)、「__」(アンダーバー2つ)で始まる要素はクラス外から参照するとエラーになる(しかし、こちらもしようど思えばできる)、という命名ルールがあります。 |
| + | class PrivateMethod(object): |
| + | def __method(self): |
| + | print "__method" |
| + | |
| + | def _method(self): |
| + | self.__method() |
| + | |
| + | privateMethod = PrivateMethod() |
| + | |
| + | 「_」で始まるメソッドは参照可能(ただし慣習的に参照するのは非推奨)。 |
| + | privateMethod._method() |
| + | 結果 |
| + | __method |
| + | |
| + | 「__」で始まるメソッドはクラス内からの呼び出しは可能ですが、外部から呼び出すとエラーとなります。 |
| + | privateMethod.__method() |
| + | |
| + | 結果 |
| + | Traceback (most recent call last): |
| + | File "class_test.py", line 42, in <module> |
| + | privateMethod.__method() |
| + | AttributeError: PrivateMethod instance has no attribute '__method' |
| + | |
| + | しかし、実はこれは「__」で始まるメソッドは「_クラス名メソッド名」と名前が変更されるだけなので、無理矢理アクセスすることも可能です。 |
| + | |
| + | privateMethod._PrivateMethod__method() |
| + | |
| + | 結果 |
| + | |
| + | __privateMethod |
| + | |
| + | つまり、Pythonには言語仕様として完全にアクセス範囲を制限する機能はありません。 |
| + | そのため、アクセスをコントロールするには人間が気をつけるしかありません。 |
| + | プログラムを無駄に複雑にしないために、なるべく慣習的なスタイルを守るようにしましょう。 |
| + | |
| + | *継承 [#u224ed0a] |
| + | |
| + | 継承はクラス名の後の「()」の中に親クラスの名前を入れることで行います。 |
| + | 親クラスへのアクセスはそのままクラス名を指定するか、「super」関数を使います。 |
| + | |
| + | class ParentClass(object): |
| + | def __init__(self): |
| + | print "parent" |
| + | |
| + | def printType(self): |
| + | print type(self) |
| + | |
| + | |
| + | class ChildClass(ParentClass): |
| + | |
| + | def __init__(self): |
| + | ParentClass.__init__(self) |
| + | |
| + | def printType(self): |
| + | super(ChildClass,self).printType() |
| + | |
| + | childClass = ChildClass() |
| + | childClass.printType() |
| + | |
| + | 結果 |
| + | parent |
| + | <class '__main__.ChildClass'> |
| + | |
| + | 「super」は親クラスの名前を知らなくても利用できるので、コピペしたときに親を参照したいのに別クラスを参照してしまったというような事故なども防げて何かと便利です。 |
| + | しかし、クラスの定義のところで、「(object)」を付けた方が良いと書きましたが、もしこれを省略すると「super」が使えなくなります。 |
| + | |
| + | つまり「(object)」を指定することで、Pythonのobject型を継承していると判断され、それ特有の関数なども使えるようになるわけです。 |
| + | |
| + | Pythonでは全ての基本的なクラスは「object」を継承しているので、それにならってクラスを自作するときも「object」を継承しておくのがおすすめです。 |
| + | |
| + | ただし、小規模な開発でのクラス定義だと、さほどobjectを継承させるメリットは無いかも知れないので、場合によって調整してください。 |
| + | |
| + | また、Pythonは多重継承もサポートしています |
| + | |
| + | class Class1(Class2,Class3): |
| + | ... |
| + | |
| + | ただし、多重継承は継承関係が複雑化したり名前の衝突が起きやすいことから嫌われる傾向にあります。メリットがはっきりしている場合を除けば避けた方が無難かも知れません。 |
| + | |
| + | *クラスメソッドとスタティックメソッド [#o110c130] |
| + | |
| + | Pythonではクラスメソッドとスタティックメソッドの両方が使えます。 |
| + | |
| + | 記法としては「@classmethod」と「@staticmethod」というアノテーションを使います。 |
| + | |
| + | |
| + | class StaticAndClassMethod(object): |
| + | @classmethod |
| + | def classMethod(cls): |
| + | print "class method" |
| + | |
| + | @staticmethod |
| + | def staticMethod(): |
| + | print "static method" |
| + | |
| + | |
| + | StaticAndClassMethod.classMethod() |
| + | StaticAndClassMethod.staticMethod() |
| + | |
| + | 結果 |
| + | class method |
| + | static method |
| + | |
| + | クラスメソッドとスタティックメソッドの違いは、第一引数にクラスをとるかどうかです。 |
| + | |
| + | そのため、メソッドの中でクラスを使わないならスタティックメソッドを使うのが普通です。 |
| + | |
| + | しかし、クラス名を使えばスタティックメソッドから他のスタティックメソッドにアクセスできますし、クラスメソッドでクラスを使わないことも可能なので、明確に違いがあるというわけでもありません。実際、どちらかのみを実装しているPython以外の言語も多くあります。 |
| + | |
| + | 使いどころとしては、似たような関数がたくさんある場合には、それを一つのクラスのメソッドにすると名前の衝突が起こりにくく、また同様の関数がまとまってわかりやすくなります。また、作ったクラスに関連した関数がある場合にはまとめると関連がわかりやすくなります。 |
| + | |
| + | しかし小規模開発なら関数の個数自体が少ないので、スタティックメソッドは使わず関数のみを使用しても十分でしょう。 |