プログラミング演習・14章版 第13章 クラスとオブジェクト
教材コード

クラスとオブジェクト

学生の名前と点数を一件だけ扱うなら二つの変数で足りる.同じ形の学生を何件も作り,各学生の点数を更新する操作もまとめたくなったとき,クラスが役に立つ.

クラス定義とインスタンス

知識

クラス(class)は属性と操作をまとめた型です.インスタンス(instance)はクラスを呼び出して作った個々の値です.__init__ はインスタンス生成時に呼ばれ,self.name のような属性を設定します.同じクラスから作った二つのインスタンスは,通常それぞれ別の属性を持つ.

身近な例として,学生証の共通書式がクラスであり,名前を書き込んだ一枚一枚の学生証がインスタンスです.書式は共通でも,甲さんの学生証の名前を変えても乙さんの名前は変わりません.

クラスと別々のインスタンス
クラスと別々のインスタンス

class は「分類・組」を意味する英語です.instance は「一つの事例」を表します.関連語は object(オブジェクト,個々の対象)です.発表では “This class creates student objects” といいます.

クラスから別々のインスタンスを作る
そのまま演習知識 · そのまま演習
chapter-13_code_1.py
class Student:
    def __init__(self, name):
        self.name = name

a = Student('A')
b = Student('B')
print(a.name, b.name)
a.name = 'C'
print(b.name)
ヒントを見る

このコードでは a、b の値を追ってください。インスタンスごとの属性と、クラスに共通する定義を区別します。別のインスタンスを作った場合に、どの値が独立して変わるかを確認します。

演習の解説を見る

Student('A') は一件目を作り,__init__ の name に 'A' を渡す.self.name はその一件目の属性となります.b は別の一件なので,a.name を変えても b.name は 'B' のままです.

実行結果の例
A B
B
虫食い知識 · 虫食い
chapter-13_code_3.py
class Student:
    def ___(self, name):
        ___.name = name
ヒントを見る

最初の空欄はインスタンス作成時の初期化メソッド名、次はそのインスタンス自身を表す引数名です。

演習の解説を見る

順に __init__,self です.__init__ の前後の下線はそれぞれ二つです.引数 name は呼び出し中だけの変数,self.name は作ったインスタンスの属性です.

一行ずつ追う・比べる

class Student: では共通の型を定義するだけで,学生の実物はまだません.def __init__(self, name): は生成時の処理を定めます.self.name = name は渡された名前を,今作っている学生の属性へ保存します.a = Student('A') ではじめて一件目ができ,b = Student('B') で別の一件ができます.だから出力は A B,続いて B です.

名前 何を表すか
Student 共通の型・設計図.
a,b 別々のインスタンス.
name 初期化のときだけ渡される引数.
self.name 特定の一件に保存される属性.

よくある誤りは name = name と書いて保存したつもりになることです.左側も関数内の名前なので,インスタンスには残りません.self.name = name と書きます.また a = b は新しい学生を作らず同じ学生への別名を作ります.二件必要なら Student(...) を二回呼ぶ.

メソッドと self

知識

メソッド(method)はクラスの中で定義し,インスタンスを通して呼ぶ関数です.c.add() の呼び出しでは,呼び出し元の c が self に渡されます.そのため self.value += 1 は c の状態を更新します.

身近な例として,カウンターの本体には数字が書かれ,横に「一つ増やす」ボタンが付いていると考えよう.ボタンがメソッドです.self は「今ボタンを押したこのカウンター」を示します.別のカウンターの数字は動きません.

Counter の状態変化
Counter の状態変化

method は「方法・手順」という意味です.self は「自分自身」で,関連語は attribute(属性)です.発表では “The add method updates this instance” といいます.

そのまま演習知識 · そのまま演習
chapter-13_code_4.py
class Counter:
    def __init__(self):
        self.value = 0

    def add(self):
        self.value += 1

c = Counter()
c.add()
c.add()
print(c.value)
ヒントを見る

このコードでは c の値を追ってください。インスタンスごとの属性と、クラスに共通する定義を区別します。別のインスタンスを作った場合に、どの値が独立して変わるかを確認します。

演習の解説を見る

生成直後は 0.一回目の add で 1,二回目で 2 となります.self.value += 1 は self.value = self.value + 1 の省略形です.

実行結果の例
2
虫食い知識 · 虫食い
chapter-13_code_6.py
def add(self):
    ___.value += 1
ヒントを見る

value はこのメソッドを呼んだインスタンスの属性です。第1引数を使って、変更する相手を指定します。値を返す行ではありません。

演習の解説を見る

self を入れます.value += 1 とすると,インスタンスの属性ではなく,関数内に存在しない局所変数を読もうとしてエラーになります.

状態の変化を一行ずつ追う

c = Counter() の直後に c.value は0です.一回目の c.add() は c を self として実行し,値を1へ変えます.二回目も同じインスタンスを更新するため2になります.この状態はメソッドを抜けても c に残る.

実行直後 c.value d.value
二つの Counter を生成 0 0
c.add() 1 0
もう一度 c.add() 2 0

ここで d = Counter() を追加したと考えます.c.add() では d.value は0のままです.self は「どのカウンターのボタンを押したか」を示す目印です.

c.add(c) と書くと self を二度渡したことになります.通常の呼び出しは c.add() です.定義には self が必要だが,インスタンスから呼ぶときは Python が対応付ける.

継承と上書き

知識

継承(inheritance)は既存のクラスを基に子クラスを定義する仕組みです.子に同名のメソッドを定義すると,親の実装より子の実装が選ばれます.これをオーバーライド(override,上書き)といいます.継承は「子は親の一種」と説明できる関係で使います.

身近な例として,「動物は声を出す」という共通の約束があるとします.猫は動物の一種で,猫らしい声を出します.猫を作るときは動物という土台を受け継ぎ,「声を出す」部分だけ猫用に変えられます.

継承とメソッドの上書き
継承とメソッドの上書き

inheritance は「受け継ぐこと」に由来します.override は「上から覆う」に由来します.関連語は parent class(親クラス)と child class(子クラス)です.発表では “Cat overrides Animal’s speak method” といいます.

親クラスから子クラスへの継承
継承と上書きを考えるための白紙図
継承と上書きを書き込んだ図
そのまま演習知識 · そのまま演習
chapter-13_code_7.py
class Animal:
    def speak(self):
        return '声'

class Cat(Animal):
    def speak(self):
        return 'にゃー'

print(Animal().speak())
print(Cat().speak())
ヒントを見る

子クラスに同名のメソッドがある場合と、親からそのまま受け継ぐ場合を分けます。呼び出すインスタンスの型から使う定義を追います。

演習の解説を見る

Animal() では親のメソッドを使います.Cat() では同名の子のメソッドが選ばれます.子の speak を消せば,親から受け継いだ speak により 声 が返る.

実行結果の例
声
にゃー

単にコードを再利用したいという理由だけで継承すると,親子関係が不自然になります.例えば「学生が名前を持つ」ことだけを理由に Student を Name の子にしません.

虫食い知識 · 虫食い
chapter-13_code_9.py
class Cat(___):
    def speak(self):
        return 'にゃー'
ヒントを見る

括弧内には、Cat が機能を受け継ぐ既存の親クラス名を置きます。子クラス自身の名前ではありません。

演習の解説を見る

Animal を入れます.丸括弧内が継承元です.

どのメソッドが実行されるか

class Animal: で親を定義し,class Cat(Animal): で猫を動物の子として定義します.Animal().speak() では親の speak を実行し「声」を返します.Cat().speak() では Cat 側に同名のメソッドがあるので「にゃー」を返します.猫側を削ると親のメソッドが使われます.

呼び出し 選ばれる定義 結果
Animal().speak() Animal 側 声
Cat().speak() Cat 側 にゃー
Cat 側を削った後 Animal 側 声

子クラスに speak を書いても親の定義そのものは消えません.親のインスタンスでは引き続き親の定義が使われます.上書きは子に対するメソッド選択を変えます.

特殊メソッドと表示

知識

特殊メソッド(special method)は Python の組み込み操作とクラスを結び付ける.__str__ は str(インスタンス) が呼ばれたときに文字列を返し,print の表示にも使われます.必ず文字列を返します.

身近な例として,持ち物に「人に見せるときの名札」を付けるようなものだ.Point(2, 3) という点をそのまま見せると分かりにくいので,(2, 3) と書いた名札を作ります.

special は「特別な」,string は「文字列」です.関連語は repr(開発時の表現)です.発表では “The str method defines the readable display” といえます.

そのまま演習知識 · そのまま演習
chapter-13_code_10.py
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f'({self.x}, {self.y})'

print(Point(2, 3))
ヒントを見る

インスタンスごとの属性と、クラスに共通する定義を区別します。別のインスタンスを作った場合に、どの値が独立して変わるかを確認します。

演習の解説を見る

print は点を表示する文字列を必要とします.そこで __str__ が呼ばれ,二つの属性を埋め込んだ文字列を返します.

実行結果の例
(2, 3)
虫食い知識 · 虫食い
chapter-13_code_12.py
def ___(self):
    return f'({self.x}, {self.y})'
ヒントを見る

インスタンスを文字列として表示するときに使われる特殊メソッドです。座標の文字列表現を返す役割から名前を確認します。

演習の解説を見る

__str__ を入れます.return する値が整数などであれば TypeError になります.

表示までの手順を追う

Point(2, 3) を呼ぶと,__init__ が属性に2と3を保存します.print は表示用の文字列を求めるため __str__ を呼ぶ.return f'({self.x}, {self.y})' が文字列 '(2, 3)' を返し,これが画面に表示されます.

メソッド 役割
__init__ 生成時の初期状態を決めます.
__str__ 人が読むための表示文字列を返します.
move など 呼び出したときに操作を行います.

__str__ の中で print(...) だけを実行しても文字列は返りません.return を使います.return self.x は数値を返すため不適切です.

図で理解する

クラスとインスタンス
クラスとインスタンス
継承と上書き
継承と上書き
インスタンスを作る:考える前
インスタンスを作る:考える前
インスタンスを作る:書き込んだ後
インスタンスを作る:書き込んだ後

練習して確かめる

クラス定義とインスタンス

知識

クラスは同じ形のデータを作る設計図で、インスタンスは具体的な1件。属性に状態を持ち、メソッドに操作をまとめる。

chapter-13_code_13.py
class Student:
    def __init__(self, name):
        self.name = name
s = Student('A')
print(s.name)
そのまま演習クラス定義とインスタンス · そのまま演習

Student を2件作り、名前が別々に保存されるか確かめる。

ヒントを見る

別の Student を作ると、別のインスタンスに名前が保存されます。新しいインスタンスを別の変数で受け取ります。

演習の解説

各インスタンスが自分の name 属性を持つ。

虫食いクラス定義とインスタンス · 虫食い

空欄を埋め、コードを実行してください。

chapter-13_code_14.py
class Student:
    def ___(self, name):
        self.name = name
ヒントを見る

インスタンスを作ったときに呼ばれ、受け取った name を属性へ保存する初期化メソッドです。

虫食いの解説
chapter-13_code_15.py
class Student:
    def __init__(self, name):
        self.name = name

__init__ を補います。

メソッドと self

知識

メソッドはインスタンスに結びついた関数。self はそのインスタンスを指し、状態を更新するときに使います。

chapter-13_code_16.py
class Counter:
    def __init__(self): self.value = 0
    def add(self): self.value += 1
c = Counter()
c.add()
print(c.value)
そのまま演習メソッドと self · そのまま演習

add を2回呼び、value の変化を確かめる。

ヒントを見る

add の呼び出し一回ごとに同じ c の属性が更新されます。新しい Counter を作り直す必要はありません。

演習の解説

最初は0で、2回呼べば2になります。

虫食いメソッドと self · 虫食い

空欄を埋め、コードを実行してください。

chapter-13_code_17.py
def add(self):
    ___ .value += 1
ヒントを見る

ドットの前には、value の持ち主となるインスタンスを表す第1引数名を置きます。属性の値を1だけ増やします。

虫食いの解説
chapter-13_code_18.py
def add(self):
    self.value += 1

self を補います。

継承と上書き

知識

継承した子クラスは親クラスのメソッドを使えます。同じ名前のメソッドを子側で定義すると上書きされます。共通の振る舞いが本当にあるときに使います。

chapter-13_code_19.py
class Animal:
    def speak(self): return '声'
class Cat(Animal):
    def speak(self): return 'にゃー'
print(Cat().speak())
そのまま演習継承と上書き · そのまま演習

Cat の speak を消し、親の speak が使われることを確かめる。

ヒントを見る

子クラスの speak をなくすと、親クラスの同名メソッドを受け継ぎます。Cat を作る呼び出しは残します。

演習の解説

上書きを外すと継承した『声』が返る。

虫食い継承と上書き · 虫食い

空欄を埋め、コードを実行してください。

chapter-13_code_20.py
class Cat(___):
    def speak(self): return 'にゃー'
ヒントを見る

ここは継承元を指定する場所です。speak がどのクラスのメソッドを置き換えるかを確認します。

虫食いの解説
chapter-13_code_21.py
class Cat(Animal):
    def speak(self): return 'にゃー'

Animal を補います。

特殊メソッド

知識

__str__ などの特殊メソッドは、組み込み操作を自作クラスに対応させる。print(インスタンス) の表示内容を定義できます。

chapter-13_code_22.py
class Point:
    def __init__(self, x, y): self.x, self.y = x, y
    def __str__(self): return f'({self.x}, {self.y})'
print(Point(2, 3))
そのまま演習特殊メソッド · そのまま演習

Point の x=5、y=8 に変え、表示結果を確かめてください。

ヒントを見る

作成時の二つの引数が、二つの属性へ順に保存されます。文字列表現を作る特殊メソッドは変更しません。

演習の解説

__str__ の返した文字列が表示されます。

虫食い特殊メソッド · 虫食い

空欄を埋め、コードを実行してください。

chapter-13_code_23.py
def ___(self): return f'({self.x}, {self.y})'
ヒントを見る

オブジェクトを print したときの文字列表現を定義する特殊メソッドです。二重の下線を含む名前を確認してください。

虫食いの解説
chapter-13_code_24.py
def __str__(self): return f'({self.x}, {self.y})'

__str__ を補います。

章末演習(10問)

ここには解答を載せていません。

  1. 章末演習章末演習 1

    書名と価格を持つクラス Book を定義し,2冊の情報と価格の合計を表示します。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で次の2つのインスタンスを作ります。
    • b1 = Book("Python入門", 2400)
    • b2 = Book("データ分析", 3100)

    処理条件

    • class Book: を定義し,__init__(self, title, price) で受け取った値をインスタンスの属性 title(文字列)と price(整数)に保存します。
    • リスト [b1, b2] を for 文で先頭から順に処理し,属性を参照して1冊ずつ表示します。
    • 合計は変数 total を0で始め,同じ for 文の中で += によって足します。組み込み関数 sum と dataclasses モジュールは使いません。

    出力

    • 1冊につき1行,「書名: 価格円」と表示します。コロンは半角で,その後に半角空白1個を置く。「円」は全角で,数値の直後に空白なしで続ける。
    • 最後の行は「合計」,半角空白,合計金額,半角空白,「円」の順に表示します。
    • 出力は全3行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    Python入門: 2400円
    データ分析: 3100円
    合計 5500 円
    
    ヒントを見る

    クラスの定義(初期化で何を属性として残すか),インスタンスの作成,属性を読んで表示・合計する部分,の順に組み立てよう。初期化メソッドの引数は,self を通して代入しないとメソッドの終了とともに消えてしまう。

  2. 章末演習章末演習 2

    数を数えるクラス Counter を作り,2つのインスタンスが別々の値を持つことを確かめる。

    入力

    • キーボード入力なし(input() は使わない)。
    • 2つのインスタンス a = Counter() と b = Counter() をプログラム内で作ります。

    処理条件

    • __init__(self) で属性 count を0にします。count はクラス直下に書かず,インスタンスの属性として持つ。
    • increment(self, step=1) は count を step だけ増やす。引数を省略したときは1増える。step には正の整数だけを渡す。
    • reset(self) は count を0に戻す。どのメソッドも値を返さず,メソッドの中では表示しません。
    • 次の順に操作する:a.increment(),a.increment(),a.increment(5),b.increment(),1行目を表示,a.reset(),2行目を表示。

    出力

    • 各行は「a: aの値 b: bの値」とします。a と b は半角小文字,コロンは半角で,コロンの後と数値の後に半角空白を1個ずつ置く。
    • 1行目はリセット前,2行目は a.reset() の後の値。
    • 出力は全2行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    a: 7 b: 1
    a: 0 b: 1
    
    ヒントを見る

    引数を省略したときの値は,呼び出す側ではなくメソッドの仮引数の側に書いておきます。a を操作したのに b の値まで変わるなら,値をクラス全体で共有する場所に置いていないか確認しましょう。

  3. 章末演習章末演習 3

    幅と高さを持つクラス Rectangle に,値を返すメソッドと状態を変えるメソッドを作り分ける。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で r1 = Rectangle(3, 4) と r2 = Rectangle(5, 5) を作ります。幅・高さ・倍率はすべて正の整数とします。

    処理条件

    • __init__(self, width, height) で属性 width と height に保存します。
    • area() は面積を,perimeter() は周の長さを整数で返します。is_square() は幅と高さが等しければ True,そうでなければ False を返します。
    • scale(self, k) は幅と高さをそれぞれ k 倍に書き換え,値は返しません。
    • どのメソッドの中でも print は使わず,表示はクラスの外で行います。
    • [r1, r2] を for 文で処理して1・2行目を表示し,その後 r1.scale(2) を実行して3行目を表示します。

    出力

    • 1・2行目は「幅x高さ 面積:値 周:値 正方形:値」とします。x は半角小文字,コロンは半角でコロンの後に空白を置きません。項目の間は半角空白1個。真偽値は True/False と表示します。
    • 3行目は「拡大後 幅x高さ 面積:値」とします。
    • 出力は全3行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    3x4 面積:12 周:14 正方形:False
    5x5 面積:25 周:20 正方形:True
    拡大後 6x8 面積:48
    
    ヒントを見る

    4つのメソッドを「結果を返すもの」と「自分の属性を書き換えるもの」に分けてから書こう。面積を属性として保存してしまうと,拡大した後に古い値が残ることに注意します。

  4. 章末演習章末演習 4

    入出金の履歴を残す口座クラス Account を作り,残高不足の出金を正しく拒否します。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で acc = Account("山田", 1000) を作ります。金額はすべて正の整数とします。

    処理条件

    • __init__(self, owner, balance=0) で属性 owner(名義),balance(残高),history(空のリスト)を用意します。history はインスタンスごとに新しいリストにします。
    • deposit(self, amount) は残高に加え,history に文字列「入金 金額」を追加します。
    • withdraw(self, amount) は,amount が残高以下なら残高から引いて「出金 金額」を追加し True を返す(残高ちょうどの出金は成功し,残高は0になる)。残高より大きければ残高を変えずに「出金失敗 金額」を追加し False を返します。
    • 文字列中の区切りは半角空白1個。メソッドの中では表示しません。
    • 操作順:acc.deposit(500),ok1 = acc.withdraw(2000),ok2 = acc.withdraw(300)。

    出力

    • 1行目「出金1回目: 戻り値」,2行目「出金2回目: 戻り値」,3行目「残高: 残高」,4行目「履歴:」を表示します。コロンは半角で,値がある行はコロンの後に半角空白1個を置く。
    • 5行目以降に history の要素を先頭から1行に1つずつ表示します。
    • 出力は全7行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    出金1回目: False
    出金2回目: True
    残高: 1200
    履歴:
    入金 500
    出金失敗 2000
    出金 300
    
    ヒントを見る

    出金できるかどうかは,残高を減らす前に判定します。失敗の場合を先に処理して戻ってしまうと,成功の場合の処理を深く字下げせずに書けます。境界(残高と同じ額)がどちらに入るかを比較演算子で確かめましょう。

  5. 章末演習章末演習 5

    親クラス Animal と,鳴き声だけを上書きする子クラス Dog・Cat を作り,同じメソッド呼び出しで異なる結果が出ることを確かめる。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で pets = [Dog("ポチ"), Cat("タマ"), Animal("カメ吉")] を作ります。

    処理条件

    • Animal は __init__(self, name) で属性 name を保存し,speak() は文字列 "……"(全角の三点リーダー「…」を2個)を返します。
    • Animal の introduce() は「名前: 鳴き声」の文字列を返します。鳴き声は文字列を直接書かず,speak() を呼び出して得る。
    • Dog と Cat は Animal を継承し,speak() だけを定義してそれぞれ "ワン","ニャー" を返します。子クラスには __init__ も introduce も書きません。
    • pets を for 文で処理し,各要素の introduce() の戻り値を表示します。メソッドの中では表示しません。

    出力

    • 1匹につき1行,「名前: 鳴き声」と表示します。コロンは半角で,その後に半角空白1個を置く。
    • 出力は全3行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    ポチ: ワン
    タマ: ニャー
    カメ吉: ……
    
    ヒントを見る

    自己紹介の文の組み立ては親クラスに1回だけ書きます。その中で鳴き声を得るときに自分自身のメソッドを呼ぶと,実際のインスタンスが子クラスなら子クラスの定義が使われます。

  6. 章末演習章末演習 6

    社員クラス Employee を継承して手当付きの Manager を作り,super() で親の処理を再利用して給与を計算します。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で staff = [Employee("青木", 250000), Manager("井上", 300000, 50000), Employee("上田", 230000)] を作ります。金額はすべて整数。

    処理条件

    • Employee は __init__(self, name, base) で属性 name と base を保存し,salary() は base を返します。
    • Manager(Employee) の __init__(self, name, base, allowance) は,super().__init__(name, base) で名前と基本給を設定してから属性 allowance を保存します。
    • Manager の salary() は super().salary() の結果に allowance を足して返します。self.base を直接足す書き方はしません。
    • staff を for 文で処理して各人を表示し,合計は変数 total に += で足します。組み込み関数 sum は使いません。

    出力

    • 1人につき1行,「名前 給与」と表示する(半角空白1個で区切る。カンマ区切りや「円」は付けない)。
    • 最後の行は「合計 合計額」とします。
    • 出力は全4行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    青木 250000
    井上 350000
    上田 230000
    合計 830000
    
    ヒントを見る

    子クラスでは親と同じ代入を書き直さず,親の初期化を呼んだ後で増えた属性だけを足します。給与の計算も同じ考え方で,親の計算結果に差分を加える。

  7. 章末演習章末演習 7

    平面上の点を表すクラス Point に特殊メソッドを定義し,+・==・print を自作クラスで使えるようにします。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で a = Point(1, 2),b = Point(3, 4) を作ります。座標は整数とします。

    処理条件

    • __init__(self, x, y) で属性 x,y を保存します。
    • __str__ は「(x, y)」の形の文字列を返す(半角かっこ,半角カンマの後に半角空白1個)。
    • __add__(self, other) は座標どうしを足した新しい Point を返します。self と other の属性は書き換えません。
    • __eq__(self, other) は x と y が両方等しいときだけ True を返します。__ne__ は定義しない(Python は __eq__ の結果を反転して != を求める)。
    • 次の順に表示する:p = a + b として print(p),print(a),print(p == Point(4, 6)),print(Point(0, 0) != Point(0, 1))。

    出力

    • 1行目は和の点,2行目は足す前の a(変化していないことの確認),3・4行目は比較の結果を True/False で表示します。
    • 出力は全4行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    (4, 6)
    (1, 2)
    True
    True
    
    ヒントを見る

    `+` の結果は「新しい点」なので,足し算のメソッドでは自分の属性を書き換えず,計算した座標から新しいインスタンスを作って返します。表示用のメソッドは print するのではなく文字列を返す点にも注意しましょう。

  8. 章末演習章末演習 8

    品名と個数を辞書で管理する在庫クラス Inventory を作り,len() と print() に対応させる。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で inv = Inventory() を作ります。個数はすべて正の整数とします。

    処理条件

    • __init__ で空の辞書を属性 items に用意する(キー:品名,値:個数)。
    • add(name, qty) は個数を加える。未登録の品名なら新しく登録します。
    • remove(name, qty) は,現在の個数(未登録なら0とみなす)が qty 以上なら減らして True を返し,0個になった品名は辞書から削除します。足りなければ何も変えずに False を返します。
    • __len__ は品目数を返します。__str__ は品名を sorted の順(文字コード順)に並べ,「品名:個数」をカンマと半角空白「, 」で区切った文字列を返します。在庫が空なら空文字列を返します。
    • collections モジュールは使いません。
    • 操作順:add("ペン", 10),add("ノート", 3),add("ペン", 5),r1 = remove("ノート", 3),r2 = remove("ペン", 20),r3 = remove("鉛筆", 1),add("消しゴム", 2)(各メソッドは inv. を付けて呼ぶ)。

    出力

    • 1〜3行目は r1〜r3 を「ノート3個の削除: 値」「ペン20個の削除: 値」「鉛筆1個の削除: 値」と表示します。
    • 4行目は len(inv) を使って「品目数: 値」,5行目は inv をそのまま埋め込んで「在庫: 文字列」と表示します。コロンは半角で,その後に半角空白1個を置く。
    • 出力は全5行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    ノート3個の削除: True
    ペン20個の削除: False
    鉛筆1個の削除: False
    品目数: 2
    在庫: ペン:15, 消しゴム:2
    
    ヒントを見る

    未登録の品名を個別の if で分けなくても,辞書から値を取り出すときに既定値を指定できるメソッドを使えば,加算と在庫確認を同じ形の式で書けます。0個になったときの削除を忘れると品目数が合わなくなります。

  9. 章末演習章末演習 9

    図形の親クラス Shape に比較と表示の特殊メソッドを置き,子クラス Circle・Rect を面積順に並べる。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で shapes = [Rect(3, 4), Circle(1), Rect(2, 2), Circle(2)] を作ります。

    処理条件

    • Shape には __lt__(self, other)(面積が小さいほうを「小さい」とする)と __str__(「名前 面積 値」の文字列を返す)だけを定義します。
    • Circle(r) は __init__ で属性 name を "円",r を半径とし,area() は math.pi * r ** 2 を返します。
    • Rect(w, h) は __init__ で属性 name を "長方形",w・h を幅・高さとし,area() は w * h を返します。
    • sorted(shapes) で並べ替える。key= 引数は使わず,__lt__ による比較で並べること。面積が等しい図形はこのデータにはありません。
    • 並べた順に図形を表示し,丸める前の面積を変数 total に += で足します。

    出力

    • 図形1個につき1行,「名前 面積 値」と表示します。区切りは半角空白1個。面積は f-string の書式 .2f で小数第2位まで表示する(整数の面積も「4.00」のように表示する)。
    • 最後の行は「合計 値」とし,値は同じく .2f で表示します。
    • 出力は全5行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    円 面積 3.14
    長方形 面積 4.00
    長方形 面積 12.00
    円 面積 12.57
    合計 31.71
    
    ヒントを見る

    並べ替えは要素どうしを「小なり」で比べて行われます。比較と表示を親クラスに書き,その中で各図形自身の面積メソッドを呼べば,子クラスには面積の求め方だけを書けばよいでしょう。合計は表示用に丸めた値ではなく元の面積で足そう。

  10. 章末演習章末演習 10

    生徒クラス Student と,生徒をまとめて管理する Classroom を作り,平均点の順位表を表示します。

    入力

    • キーボード入力なし(input() は使わない)。
    • プログラム内で room = Classroom() を作り,次の3人を room.add(...) で順に加える。
    • Student("伊藤", [80, 90, 70])
    • Student("加藤", [95, 85, 90])
    • Student("小林", [60, 75, 72])

    処理条件

    • Student(name, scores) は属性 name と scores(1個以上の整数のリスト)を持ち,average() は平均点を返す(sum と len を使ってよい)。
    • Classroom は __init__ で空のリストを用意し,add(student) で生徒を加え,__len__ で人数を返します。
    • ranking() は (平均点, 名前) のタプルのリストを平均点の高い順に並べて返します。このデータに同じ平均点の生徒はいません。
    • 順位の番号は第12章の enumerate(start=1)で付ける。番号を自分で数える変数は使いません。

    出力

    • 1行目は len(room) を使って「人数: 値」と表示する(コロンは半角,後ろに半角空白1個)。
    • 2行目以降は順位の高い順に「順位位 名前 平均点」と表示します。区切りは半角空白1個,平均点は .1f で小数第1位まで表示します。
    • 出力は全4行。実行例と同じ順序・同じ文字(半角・全角,大文字・小文字を含む)で表示します。実行例にない見出し・空行・余分な空白・デバッグ用の表示は出さない(assert は何も表示しないので自己確認に使ってよい)。

    実行例

    人数: 3
    1位 加藤 90.0
    2位 伊藤 80.0
    3位 小林 69.0
    
    ヒントを見る

    タプルどうしの比較は先頭の要素から行われます。どの値を先頭に置けば並べ替えがそのまま順位順になるかを考えよう。enumerate が返す組をそのまま分解すれば,順位と中身を同時に受け取れます。