見出し画像

【Python】SimplePrograms line 12 Classes

プログラム

12行プログラムです。

class BankAccount(object):
    def __init__(self, initial_balance=0):
        self.balance = initial_balance
    def deposit(self, amount):
        self.balance += amount
    def withdraw(self, amount):
        self.balance -= amount
    def overdrawn(self):
        return self.balance < 0
my_account = BankAccount(15)
my_account.withdraw(50)
print (my_account.balance, my_account.overdrawn())

Bank Account:銀行口座
amount:金額
balance:残高
deposit:入金
withdraw:引き出し
overdrawn:赤字

実行結果

-35 True

解説

クラスです。
ようやくですね。

Pythonでのクラス定義を引いておきましょう。

classdef ::= [decorators] "class" classname [inheritance] ":" suite
inheritance ::= "(" [argument_list] ")"
classname ::= identifier

https://docs.python.org/ja/3/reference/compound_stmts.html#class-definitions

いきなりわからないのが[decorators]。
Pythonのドキュメントをあたってみましたが、容易にはわかりそうになかったのでひとまずはスキップします。

後は、inheritanceを () で書き連ねてクラス名を記すだけ。

1行目

class BankAccount(object):

クラス「object」を継承してクラス「BankAccount」を定義します。

クラス「object」って何?
という感じですが、Pythonドキュメントですぐに見つからない。
なんとなく、「全てのクラスはobjectを継承しなければならない」的な感じだけども。
と思ったらドキュメントに次のようにありました。

継承リストのないクラスは、デフォルトで、基底クラス object を継承する

https://docs.python.org/ja/3/reference/compound_stmts.html#class-definitions

やはり、全てのクラスはobjectを継承することになりそうです。
いずれ、objectについて調べておきたい(希望)。

2行目、3行目

def __init__(self, initial_balance=0):
        self.balance = initial_balance

関数の定義だと思いますが、この「_init_」(半角文字のアンダースコア二文字だと書式指定に変換されてしまうので、全角のアンダースコア一文字で記載)というパターンはなんでしょうね。
次の行を実行した時に呼び出されているようです。

my_account = BankAccount(15)

ここでは明示的に関数「_init_」は呼び出していないのですが、それでも呼び出されている。
どうやら、そういう関数があるようです。
調べてみたところ、Pythonの次のドキュメントに記載されていました。

「_init_」以外にも多々あるようです。
とりあえず「_init_」だけについて引用してみます。

インスタンスが (_new_() によって) 生成された後、それが呼び出し元に返される前に呼び出されます。引数はクラスのコンストラクタ式に渡したものです。基底クラスとその派生クラスがともに _init_() メソッドを持つ場合、派生クラスの _init_() メソッドは基底クラスの _init_() メソッドを明示的に呼び出して、インスタンスの基底クラス部分が適切に初期化されること保証しなければなりません。例えば、 super()._init_([args...]) 。

https://docs.python.org/3/reference/datamodel.html#object.__init__

_new_() と _init_() は連携してオブジェクトを構成する (_new_() が作成し、 _init_() がそれをカスタマイズする) ので、 _init_() から非 None 値を返してはいけません; そうしてしまうと、実行時に TypeError が送出されてしまいます。

https://docs.python.org/3/reference/datamodel.html#object.__init__

んん?

基底クラスの _init_() メソッドを明示的に呼び出さなければならない?

このサンプルでは基底クラスである「object」の「_init_() メソッド」を呼び出しているようには見えません。

クラス「object」が「_init_」メソッドを持っていない、とか?

test = object()
print(test.__dir__())

実行結果

['__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__', '__doc__']

「_init_」メソッドは持っているよう。

試しに。
これでも実行可能。

class BankAccount(object):
    def __init__(self, initial_balance=0):
        self.balance = initial_balance
        super().__init__()

「object」の「_init_」が何をしているのかわからないのももどかしい。

さらに、「super」。
継承元のクラスを指すらしいのだけど。
こちらが更に悩ましい。
が、テストプログラムが大きくなりすぎたので別途。

ついでに『_init_() から非 None 値を返してはいけません; そうしてしまうと、実行時に TypeError が送出されてしまいます』も、イマイチ意味不明。

4、5、6、7、8、9行目

def deposit(self, amount):
    self.balance += amount
def withdraw(self, amount):
    self.balance -= amount
def overdrawn(self):
    return self.balance < 0

特に語るべきことなし。
入金は残高を増やして、
引き出しは残高を減らして、
赤字かどうかをチェックできます。

10、11、12行目

my_account = BankAccount(15)
my_account.withdraw(50)
print (my_account.balance, my_account.overdrawn())

こちらも特に語るべきことはないですね。

残高15で銀行口座を作って、
50を引き出して、
赤字かどうかを表示しています。


今回のクラスそのものは簡単ではあったけど、クラスについてのわからないことは多数残った。

・公開レベルは設定できるのか。
・秘匿関数は作れるのか。
・メンバ変数が生成されるタイミングはいつか。
・継承レベルは設定できるのか。
・継承元の全ての関数に自由にアクセスできるのか。
・ポリモーフィズムはどうやって実現するのか。
・抽象クラスは作れるのか。
・シングルトンパターンはどうやって作るのか。
・decorators とは何か。
・super とは何か。
・_init_() から非 None 値を返してはいけないことの意味は何か。

などなど。

別途一つずつ解決するかなぁ。

この記事が気に入ったらサポートをしてみませんか?