coLinux日記

coLinuxはフリーソフトを種として、よろずのシステムとぞなれりける。

SimplePrograms で Python を学ぶ その12

2024-05-16 20:54:00 | Python
SimplePrograms - Python Wiki
https://wiki.python.org/moin/SimplePrograms

の 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())


Python のクラス定義ですね。
https://docs.python.org/ja/3/reference/compound_stmts.html#class-definitions
クラス定義は、複合文「複合文には、他の文 (のグループ) が入ります」の中で説明されているので、インデントによって中身を記述するわけです。説明によれば、

class classname(argment_list):

なので、 (argment_list) が継承リストで
「継承リストは通常、基底クラスリストを与えます」
だそうです。プログラムでは classname が 「BankAccount」 で、argment_list が 「object」 ですね。
「継承リストのないクラスは、デフォルトで、基底クラス object を継承する」
なので、(object) は省略できるようです。

さらに、クラス定義の中身の先頭にある __init__() は、
https://docs.python.org/ja/3/reference/datamodel.html#object.__init__
によると、

object.__init__(self[....])

なので、このクラスのインスタンス(クラスから作られたオブジェクト)を作るときに初期化するものを指定するための基底クラス object のメソッドですね。
この部分は丸ごとまねするのが良さそうです。

プログラムの見た目から、Python では、クラス定義内の self の部分を生成したインスタンスの名前に置き換えて使うようです。 

クラスを使うときにはオブジェクト指向を理解しておくのが前提なので、
オブジェクト指向超入門
http://www.rsch.tuis.ac.jp/~ohmi/software-intro/objectoriented.html
あたりで復習するとして、ここでは、クラスのインスタンスを生成すると、クラスで定義されているデータとメソッドが使えるようになるので、データとメソッドという用語を使います。(ということは、SimplePrograms が示すように、Pythonを学ぶ場合、同時にオブジェクト指向を理解する必要があるということらしいです。import でそれを感じましたが常識なんですね。)

ここで言っているデータは、Python では、
「インスタンス属性は、メソッドの中で self.name = value とすることで設定できます」
なので、「インスタンス属性(instance attribute)」と言うみたいです。

まず、クラスのデータから確かめて見ましょう。

>>> class Atest(object):
...      def __init__(self,initial_adata=0):
...         self.adata = initial_adata
...
>>>

クラス Atest を定義して、__init__ でデータを設定するのがねらいです。
このクラスのインスタンスを生成するには他の言語で一般的な new とか使わずに Python では classname そのもの(「( )」は必要?)
を変数に代入すれば良いみたいです。

>>> obj_01 = Atest() # クラス Atest のインスタンスを生成
>>> obj_01.adata
0

引数を指定しないと生成されたインスタンス obj_01 の データ adata が 0 となりますね。
つまり、__init__() の initial_adata=0 は、デフォルトで、0 を initial_adata に代入するということです。

>>> obj_02 = Atest(100)
>>> obj_02.adata
100
>>>

引数 100 を指定してインスタンス obj_02 を生成すると、そのデータ adata が 100 になりました。
つまり、__init__() の initial_adata に 指定した引数がデフォルト値の代わりに代入されて使用されるわけです。 ちなみに存在しないデータに対しては、
>>> obj_02.bdata
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Atest' object has no attribute 'bdata'. Did you mean: 'adata'?
>>>
「bdata なんて無いよ、adata じゃないの」と言ってくれるんですね。

また、インスタンスだけなら、
>>> obj_02
<__main__.Atest object at 0xf788eb10>
>>>
なので、「クラスAtest の object」と理解されているようです。

ついでに、2つの引数を指定してインスタンスを生成してみます。

>>> class Btest(object):
...      def __init__(self,initial_adata=0,initial_bdata=0):
...         self.adata = initial_adata
...         self.bdata = initial_bdata
...         self.tdata = self.adata + self.bdata
...
>>> obj_03 = Btest(10,100)
>>> obj_03.adata
10
>>> obj_03.bdata
100
>>> obj_03.tdata
110
>>>

複数引数も大丈夫ですね。

続いて、メソッドの定義を見てみましょう。プログラムをまねしてメソッドを定義してみます。
Plus というメソッドを作って、そのメソッドに引数を渡してみます。
対話型の Pyton は、矢印キーで入力履歴が再生できるので積極的に利用するとチェックも簡単です。

>>> class Ctest(object):
...      def __init__(self,initial_adata=0,initial_bdata=0):
...         self.adata = initial_adata
...         self.bdata = initial_bdata
...      def Plus(self, amount):
...         self.pdata1 = self.adata + self.bdata
...         self.pdata2 = self.pdata1 + amount
...
>>> obj_04 = Ctest(10,100)
>>> obj_04.pdata1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Ctest' object has no attribute 'pdata1'. Did you mean: 'adata'?
>>> obj_04.Plus(10000)
>>> obj_04.pdata1
110
>>> obj_04.pdata2
10110
>>>

インスタンス obj_04 を作成し、最初のエラーは、まだ Plus メソッドを実行していなかったので、データ pdata1 が作成されていなかったからですね。
obj_04.Plus(10000)
で、引数に 10000 を与えて Plus を実行すると、pdata1 と pdata2 が生成されて、
obj_04 のデータとして参照できるようになることが分かりました。メソッドの引数は、
def Plus(self, amount): の amount に代入されてその中で使えるようになるのですね。
インスタンスのデータは、そのメソッド等で値が代わったりすると、その後それが参照できるわけです。

クラスの簡単なデータ生成はこれの応用でできそうなので、この形式は丸ごと覚えた方がよさそうです。

いままで試してみたことで、このプログラムが理解できるようになりました。
BankAccount クラスのインスタンス my_account を生成するときに引数 15 を与えたので、
my_account のデータ balance は 15 なります。
my_account のメソッド withdraw に引数 50 を与えて実行したので、
そのデータ balance は -35 (15 - 50 だから) になります。
最後に print 文で、そのデータ balance と メソッド overdrawn() の実行結果が表示されるのですね。
ということは、メソッド定義内で、
reture 戻り値
を使うと メソッドの戻り値とできるわけです。
overdrawn() の戻り値は、条件式で、 balance が 負なら True 、そうでなければ False? ですね。
確認のため、プログラムに最後の3行を複製して修正し、条件式が偽になるものを追加して実行してみます。

$ ./prog-012-02.py
-35 True
10 False
$

(10 False は追加した3行で出力されました。) やはり条件が正しくないと、 False でした。

おまけで、チュートリアルのクラスの説明の中で、クラスのデータがリストの場合があったので追記しておきます。

class Dog:

   def __init__(self, name):
      self.name = name
      self.tricks = [] # creates a new empty list for each dog

   def add_trick(self, trick):
      self.tricks.append(trick)

__init__() でリストを生成しておかないと、複数のインスタンスで共通のリストになっちゃうようです。注意したいところですね。

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« SimplePrograms で Python を... | トップ | SimplePrograms で Python を... »
最新の画像もっと見る

コメントを投稿

Python」カテゴリの最新記事