静岡理工科大学 菅沼ホーム 目次 索引

クラス

  1. クラスの定義

      C++ の用語で言えば,通常のクラスclass )のメンバー(変数及び関数)は public であり,どこからでも参照可能です.メンバー関数(メソッド)はすべて仮想関数です.メソッドの宣言では,生成されたオブジェクト自身を表す第一引数 self を明示しなければなりません.この引数は,メソッド呼び出しの際に暗黙の引数として渡されます.一般的に,「 self.var 」の形でメソッドの内側で定義される変数 var は,インスタンス変数と呼ばれ,各インスタンス固有の値を持つことが可能です.また,メソッドの外側で宣言され,かつ,self がつかないクラス変数は,そのクラスのすべてのインスタンスで同じ値を持ちます.

      インスタンス変数,C++ で言えばメンバー変数に,必ず self を付けさせるという仕様はいかがなものでしょうか.確かに,インスタンス変数を明確にし,誤ったローカル変数の使用を防ぐという意味では良いかもしれません.しかし,self が付加されるために,式が長くなり,見にくくなるという欠点があります.
    01	# -*- coding: UTF-8 -*-
    02	class Example :
    03	    """A simple example of class"""   # クラスの説明
    04	    c_var = 12345   # クラス変数
    05	    def func(self, a) :
    06	        self.i_var = a   # インスタンス変数
    ----------------------------------
    07	# -*- coding: UTF-8 -*-
    08	from function import Example
    09	print(Example.__doc__)   # クラスの説明の表示
    10	x = Example()   # Example クラスのインスタンス(オブジェクト)の生成
    11	x.func(10)   # Example.func(x, 10) と等価
    12	y = Example()
    13	y.func(20)
    14	z = Example()
    15	z.func(30)
    16	print("最初の状態  x,y,z")
    17	print(Example.c_var, x.c_var, x.i_var)
    18	print(Example.c_var, y.c_var, y.i_var)
    19	print(Example.c_var, z.c_var, z.i_var)
    20	print("***w に x を代入***")
    21	w = x
    22	print("w の値変更  x,y,z,w")
    23	w.c_var = 100
    24	w.i_var = 40
    25	print(Example.c_var, x.c_var, x.i_var)
    26	print(Example.c_var, y.c_var, y.i_var)
    27	print(Example.c_var, z.c_var, z.i_var)
    28	print(Example.c_var, w.c_var, w.i_var)
    29	print("x の値変更  x,y,z,w")
    30	xf = x.func   # 関数に対するポインタ,普通の関数でもOK
    31	xf(50)
    32	x.c_var = 1000
    33	print(Example.c_var, x.c_var, x.i_var)
    34	print(Example.c_var, y.c_var, y.i_var)
    35	print(Example.c_var, z.c_var, z.i_var)
    36	print(Example.c_var, w.c_var, w.i_var)
    37	print("クラス変数値の変更  x,y,z")
    38	Example.c_var = 123
    39	print(Example.c_var, x.c_var, x.i_var)
    40	print(Example.c_var, y.c_var, y.i_var)
    41	print(Example.c_var, z.c_var, z.i_var)
    42	print(Example.c_var, w.c_var, w.i_var)
    			
    03 行目

      クラス本体を記述する文の最初の行は文字列リテラルにすることもできます.その場合,この文字列はクラスのドキュメンテーション文字列と呼ばれ,変数 __doc__ によって参照できます( 09 行目).

    10 行目~ 19 行目

      Example クラスのオブジェクト x,y,z を生成しています.インスタンス変数の値は異なりますが,クラス変数の値はすべて同じです.

    20 行目~ 28 行目

      x を w に代入後,w のクラス変数及びインスタンス変数の値を変更しています.当然,w の値は変化しますが,x の値も変化しています.これは,x と w が同じオブジェクトを指していることも意味します.つまり,Python におけるオブジェクトは,C/C++ におけるポインタに似た性質を持つことになります.従って,関数に対する説明において述べたリストを引数とした場合と同様,クラスのオブジェクトを引数とした場合は,関数内でその値を変更すれば,関数を呼んだ側の値も変化します.

    29 行目~ 36 行目

      x と w は同じオブジェクトを指していますので,x の値を変化させても,上と同様なことが起こります.また,関数自体もポインタ(関数を指すアドレス)ですので,30,31 行目のようなことが可能です.

    37 行目~ 42 行目

      クラス変数の値を変更すると( 38 行目),そのクラスのすべてのオブジェクトのクラス変数の値も変化します( 40,41 行目).ただし,23,32 行目のように,オブジェクトのクラス変数の値を直接変更した場合は,その影響を受けません( 39,42 行目).

      このプログラムを実行すると,以下に示すような結果が得られます.
    A simple example of class
    最初の状態  x,y,z
    12345 12345 10
    12345 12345 20
    12345 12345 30
    ***w に x を代入***
    w の値変更  x,y,z,w
    12345 100 40
    12345 12345 20
    12345 12345 30
    12345 100 40
    x の値変更  x,y,z,w
    12345 1000 50
    12345 12345 20
    12345 12345 30
    12345 1000 50
    クラス変数値の変更  x,y,z
    123 1000 50
    123 123 20
    123 123 30
    123 1000 50
    				

    C++ の場合

    01	class Example {
    02		public:
    03			static const int c_var = 12345;
    04			int i_var;
    05			void func(int a) {
    06				i_var = a;
    07			}
    08	};
    09	int main()
    10	{
    11		Example x, y, z, w;
    12		x.func(10);
    13		y.func(20);
    14		z.func(30);
    15		cout << "最初の状態  x,y,z\n";
    16		cout << x.c_var << " " << x.i_var << endl;
    17		cout << y.c_var << " " << y.i_var << endl;
    18		cout << z.c_var << " " << z.i_var << endl;
    19		cout << "***w に x を代入***\n";
    20		w = x;
    21		cout << "w の値変更  x,y,z,w\n";
    22		w.i_var = 40;
    23		cout << x.c_var << " " << x.i_var << endl;
    24		cout << y.c_var << " " << y.i_var << endl;
    25		cout << z.c_var << " " << z.i_var << endl;
    26		cout << w.c_var << " " << w.i_var << endl;
    27		cout << "x の値変更  x,y,z,w\n";
    28		void (Example::*pf)(int);   // ポインタによる関数funcの型宣言
    29		pf = &Example::func;   // 関数funcに対するポインタ
    30		(x.*pf)(50);   // ポインタによる関数呼び出し
    31	//	x.func(50);   // 上3行 や「 x.i_var = 40; 」と同じ
    32		cout << x.c_var << " " << x.i_var << endl;
    33		cout << y.c_var << " " << y.i_var << endl;
    34		cout << z.c_var << " " << z.i_var << endl;
    35		cout << w.c_var << " " << w.i_var << endl;
    36		Example *px, *pw;
    37		px = &x;
    38		pw = px;
    39		(*pw).i_var = 60;
    40		cout << "x のアドレス px を pw に代入  px,pw\n";
    41		cout << (*px).c_var << " " << (*px).i_var << endl;
    42		cout << (*pw).c_var << " " << (*pw).i_var << endl;
    43	}	return 0;
    			
    01 行目~ 08 行目

      C++ におけるクラスの定義です.クラス外部から常に参照できるためには,public 指定を行い,関数の外側で変数を定義する必要があります.c_var をすべてのオブジェクトで共通な変数とするため,const 指定により変更不可能にしています.また,C++ においては,Example.c_var のような参照方法は利用できません.

    19 行目~ 35 行目

      w に x を代入した場合の結果です.オブジェクト x のコピーが w に代入されるため,x と w は異なるオブジェクトとなり,いずれを変更しても,片方はその影響を受けません.また,28 行目~ 30 行目は,関数に対するポインタを利用して関数呼び出しを行っています.Python における,30, 31 行目と似た方法です.

    36 行目~ 42 行目

      x のアドレス px を pw に代入した場合の結果です.いずれも同じオブジェクトを指すポインタとなりますので,いずれの変数を介して値を変更しても同じ結果になります.

      このプログラムを実行すると,以下に示すような結果が得られます.
    最初の状態  x,y,z
    12345 10
    12345 20
    12345 30
    ***w に x を代入***
    w の値変更  x,y,z,w
    12345 10
    12345 20
    12345 30
    12345 40
    x の値変更  x,y,z,w
    12345 50
    12345 20
    12345 30
    12345 40
    x のアドレス px を pw に代入  px,pw
    12345 60
    12345 60
    				

  2. 初期化

      クラスに __init__() メソッドが定義されている場合,クラスのインスタンスを生成すると最初にこのメソッドが呼ばれます.C++ におけるコンストラクタに対応するメソッドです.
    # -*- coding: UTF-8 -*-
    class Complex() :
    	def __init__(self, re, im = 0.0) :
    		self.re = re
    		self.im = im
    	def add(self, v) :
    		x = Complex(self.re + v.re, self.im + v.im)
    		return x
    ----------------------------------
    # -*- coding: UTF-8 -*-
    from function import Complex
    x = Complex(1, 2)
    y = Complex(3)
    z = x.add(y)
    print((z.re, z.im))   # (4, 2.0)
    			

    C++ の場合

    class Complex {
    	public:
    		double re, im;
    		Complex() {}   // コンストラクタ
    		Complex(double re1, double im1 = 0.0) {   // コンストラクタ
    			re = re1;
    			im = im1;
    		}
    		Complex add(Complex v) {
    			Complex x;
    			x.re = re + v.re;
    			x.im = im + v.im;
    			return x;
    		}
    };
    int main()
    {
    	Complex x(1, 2), y(3);
    	Complex z = x.add(y);
    	cout << "(" << z.re << ", " << z.im << ")\n";
    	return 0;
    }
    			

  3. 継承

      派生クラスを定義する方法は簡単です.以下に示すように,基底クラスとなるクラス名を括弧内に並べるだけです(多重継承が可能).また,BaseClassName.methodname(self, arguments) という形で,基底クラスのメソッドも簡単に呼び出すことができます.
    class DerivedClassName ( BaseClassName1, BaseClassName2, ・・・ ):
    			
      下の例では,Num を基底クラスとした 2 つの派生クラス,Int 及び Complex を定義しています.C++ の場合は,型宣言の存在,コンストラクタが継承されない,などの理由から,かなり異なったプログラムになります.
    # -*- coding: UTF-8 -*-
    class Num :
    	name = "数値"
    	def __init__(self, v) :
    		self.v = v
    	def add(self, v) :
    		x = Num(self.v + v)
    		return x
    class Int(Num) :
    	name = "整数"
    class Complex(Num) :
    	name = "複素数"
    		# 親クラスのコンストラクタを呼びたい場合は Num.__init__(self, 値)
    	def __init__(self, re, im = 0.0) :
    		self.re = re
    		self.im = im
    	def add(self, v) :
    		x = Complex(self.re + v.re, self.im + v.im)
    		return x
    ----------------------------------
    # -*- coding: UTF-8 -*-
    from function import *
    x = Int(10)   # 基底クラス Num のコンストラクタ __init__ の継承により,変数 v に値が設定される
    print(x.v)   # 10
    y = x.add(20)   # 親クラス Num のメソッド add の継承
    print(y.v)   # 30
    u = Complex(1, 2)   # クラス Complex のコンストラクタ __init__ の利用
    print((u.re, u.im))   # (1, 2)
    v = Complex(3)
    print((v.re, v.im))   # (3, 0.0)
    w = u.add(v)   # クラス Complex のメソッド add の利用
    print((w.re, w.im))   # (4, 2.0)
    u.re = (Num.add(Num(u.re), 10)).v   # 基底クラスのメソッド add の利用
    print((u.re, u.im))   # (11, 2)
    print(u.name)   # クラス Complex のクラス変数 name の値(複素数)
    print(Num.name)   # 基底クラス Num のクラス変数 name の値(数値)
    			

    C++ の場合

    class Num {
    	public:
    		string name;
    		int vi;
    		double vd;
    		Num() {   // コンストラクタ
    			vi = 0;
    			vd = 0.0;
    			name = "";
    		}
    		Num(int vi1) {   // コンストラクタ
    			vi = vi1;
    			name = "数値";
    		}
    		Num(double vd1) {   // コンストラクタ
    			vd = vd1;
    			name = "数値";
    		}
    		int add(int v) {
    			int x = vi + v;
    			return x;
    		}
    		double add(double v) {
    			double x = vd + v;
    			return x;
    		}
    };
    class Int : public Num {
    	public:
    		string name;
    		int v;
    		Int(int v1) {   // コンストラクタ
    			v = v1;
    			name = "整数";
    		}
    };
    class Complex : public Num {
    	public:
    		string name;
    		double re, im;
    		Complex() {}   // コンストラクタ
    		Complex(double re1, double im1 = 0.0) {   // コンストラクタ
    			re = re1;
    			im = im1;
    			name = "複素数";
    		}
    		Complex add(Complex v) {
    			Complex x;
    			x.re = re + v.re;
    			x.im = im + v.im;
    			return x;
    		}
    };
    int main()
    {
    	Int x = Int(10);   // コンストラクタにより,Int クラスの変数 v に値が設定
    	cout << x.v << endl;   // 10
    	Int y = x.add(20);   // メンバー関数 add の継承
    	cout << y.v << endl;   // 30
    	Complex u(1, 2);   // Complex クラスのコンストラクタ
    	cout << "(" << u.re << ", " << u.im << ")\n";   // (1, 2)
    	Complex v(3);
    	cout << "(" << v.re << ", " << v.im << ")\n";   // (3, 0)
    	Complex w = u.add(v);   // オーバーライドされたメンバー関数 add の利用
    	cout << "(" << w.re << ", " << w.im << ")\n";   // (4, 2)
    	u.re = Num(u.re).add(10.0);   // 基底クラスのメソッド add の利用(10ではだめ)
    	cout << "(" << u.re << ", " << u.im << ")\n";   // (11, 2)
    	cout << u.name << endl;   // 複素数
    	cout << x.name << endl;   // 整数
    	return 0;
    }
    			

  4. インスタントメソッド,クラスメソッド,スタティックメソッド

      今まで使用してきたメソッドはインスタントメソッドと呼ばれ,クラスのインスタント(オブジェクト)固有の処理を行うためのメソッドです.これ以外に,Python には,クラスメソッド,及び,スタティックメソッドが存在します.クラスメソッド,スタティックメソッドともに,インスタンス変数にはアクセスできません.

      クラスメソッドは,「 @classmethod 」を付与して関数を定義します.インスタンスメソッドの場合は,第一引数にオブジェクト自身 self が渡されますが,クラスメソッドの場合は,第一引数にクラスが渡されます.従って,クラスメソッドは渡ってきたクラスを使ってクラス変数にアクセスすることができます.継承時には,第一引数に子クラスが渡されるため,クラス変数は子クラスのクラス変数の値となります.

      スタティックメソッドは,「 @staticmethod 」を付与して関数を定義します.スタティックメソッドは,クラスが渡されないため,クラス変数にアクセスするためには,クラスを記述する必要があります.クラスを記述することによってクラス変数にアクセスするため,継承時においても,記述したクラスが親クラスか子クラスかによって参照先が変わります.

      下の例では,上で示した例における基底クラス Num にクラスメソッドとスタティックメソッドが追加してあります.クラスメソッドの場合は,クラスが引数として渡されますので,30,31 行目,いずれの場合においても,Complex クラスにおけるクラス変数 name の値「複素数」が出力されます.しかし,スタティックメソッドの場合は( 32,33 行目),基底クラス Num のスタティックメソッドがそのまま継承されるため,基底クラスのクラス変数 name の値「数値」がそのまま出力されます.
    01	# -*- coding: UTF-8 -*-
    02	class Num :
    03		name = "数値"
    04		def __init__(self, v) :
    05			self.v = v
    06		def add(self, v) :
    07			x = Num(self.v + v)
    08			return x
    09				# クラスメソッド
    10		@classmethod
    11		def class_method(cls) :
    12			print(cls.name)
    13				# スタティックメソッド
    14		@staticmethod
    15		def static_method() :
    16			print(Num.name)
    17	class Int(Num) :
    18		name = "整数"
    19	class Complex(Num) :
    20		name = "複素数"
    21		def __init__(self, re, im = 0.0) :
    22			self.re = re
    23			self.im = im
    24		def add(self, v) :
    25			x = Complex(self.re + v.re, self.im + v.im)
    26			return x
    ----------------------------------
    27	# -*- coding: UTF-8 -*-
    28	from function import *
    29	x = Complex(1, 2)
    30	Complex.class_method()
    31	x.class_method()
    32	Complex.static_method()
    33	x.static_method()
    			

静岡理工科大学 菅沼ホーム 目次 索引