初心者のためのpython入門

自分がつまづいたところやつまづきやすいところを中心に書いていきます。また、役に立つライブラリの紹介などをしていきます。

カプセル化[2]

カプセル化の続き

今回は前回に続き、カプセル化について説明します。今回はプライベートなクラス変数とプライベートなメソッドについて説明します。

クラス変数

クラス変数は、クラスに属する変数です。今まで定義してきた変数は、インスタンスに属するメンバ変数です。クラス変数とメンバ変数の違いは、クラス変数は全てのインスタンスで共有されるということです。例を見てみましょう。

# hero.py
class Hero():
    number = 0; # クラス変数

    def __init__(self, name):
        self.name = name # 通常のメンバ変数
        Hero.number += 1

    def get_class_number(self): # クラス変数の値を返す関数
        return Hero.number 



hero1 = Hero("ヨシヒコ")
print(hero1.get_class_number())
hero2 = Hero("ヨシピコ")
print(hero2.get_class_number())
# 実行結果 (python hero.py)
1
2

上記の実行結果から、新規のインスタンスを作成する度にクラス変数であるnumberの値がインクリメントされていることがわかります。このことはクラス変数がインスタンスで共有されていることを意味しています。
クラス変数にアクセスする方法は、クラス名.クラス変数名(今回はHero.number)です。インスタンス名.クラス変数名でアクセスすることもできます。しかし、インスタンス変数でクラス変数を隠匿する可能性があるので使わない方が良いです。

プライベートクラス変数

プライベートクラス変数の定義の仕方は名前の先頭にダブルアンダースコアを付けるだけです。

class Hero():
    __number = 0; # クラス変数

    def __init__(self, name):
        self.name = name # 通常のメンバ変数
        Hero.__number += 1

    def get_class_number(self): # ゲッター
        return Hero.__number



hero1 = Hero("ヨシヒコ")
print(hero1.get_class_number())
hero2 = Hero("ヨシピコ")
print(hero2.get_class_number())
print(Hero.__number)
# 実行結果 (python hero.py)
1
2
Traceback (most recent call last):
  File "hero.py", line 17, in <module>
    print(Hero.__number)
AttributeError: type object 'Hero' has no attribute '__number'

外部から直接アクセスするとエラーが発生しました。プライベートクラス変数を外部で使用したい場合は前回のようにゲッターを定義しましょう。ちなみに、内部で使う場合もゲッターを利用したほうがプログラム的には良いです。

クラスメソッド

クラスメソッドとは、クラスに属するメソッドです。今まで定義してきたものは全てインスタンスメソッドです。クラスメソッドの特徴は、第一引数にselfを使わずにclsを使う点です。これにより、クラス変数をメソッド中で定義できます。また、インスタンス変数へのアクセスはできません。

# hero.py
class Hero():
    __number = 0; # クラス変数

    def __init__(self, name):
        self.name = name # 通常のメンバ変数
        Hero.__number += 1

    @classmethod
    def print_class_numbers(cls): # クラス変数の値を表示する
        print(cls.__number)

    @classmethod
    def set_class_variable(cls, money): # moneyクラス変数に値を代入する関数
        cls.money = money

hero1 = Hero("ヨシヒコ")
Hero.print_class_numbers()
hero2 = Hero("ヨシピコ")
Hero.print_class_numbers()

Hero.set_class_variable(100)
print(Hero.money)
# 実行結果 (python hero.py)
1
2
100

クラスメソッドを定義するには、メソッドの定義の真上に @classmethodを書きます。上記の例から、クラスメソッドがクラス変数を定義したり、クラス変数にアクセスできることがわかります。

プライベートメソッド

インスタンスメソッドもクラスメソッドもプライベートメソッドにすることが可能です。

class Hero():
    number = 0; # クラス変数

    def __init__(self, name):
        self.name = name # 通常のメンバ変数
        Hero.number += 1

    def __get_instance_variable(self): # プライベートインスタンスメソッド
        return self.name

    def print_instance_variable(self): # インスタンスメソッド
        print(self.__get_instance_variable())

    @classmethod
    def __get_class_variable(cls): # プライベートクラスメソッド
        return cls.number

    @classmethod
    def print_class_variable(cls): # クラスメソッド
        print(cls.__get_class_variable())

hero = Hero("ヨシヒコ")
hero.print_instance_variable()
Hero.print_class_variable()

# hero.__get_instance_variable()
# Hero.__get_class_variable()
# 実行結果 (python hero.py)
ヨシヒコ
1

上記のような結果になります。コメントアウトしたコードを実行すると両方とも例外が発生します。

まとめ

カプセル化をすることにより外部からのアクセスを禁止することができました。(実際には、非推奨の方法でアクセスすることもできます。)

最後に

お疲れ様でした。次回は継承あたりの説明をしようと思います。