オブジェクト指向型言語(OOL)
中間試験 教科書ノート

東京都立大学 システムデザイン学部 情報科学科/西内信之 先生 | 第2回〜第7回 完全対応

6/9 (火) 8:50〜
中間試験
第2〜7回
出題範囲
持込不可・対面
2号館A講義室
この教科書の使い方 各テーマは なぜ必要か → 定義 → イメージ(図)→ 具体例 → 試験で書ける形 の順に並べています。0から読んでも理解でき、用語の定義とPythonコードを自分で再現できれば満点を狙える構成です。色は「クラス 継承 ポリモーフィズム カプセル化」で固定。赤いボックス=試験で問われるポイントです。
INTRODUCTION

この科目の全体地図

この科目は「プログラムをどう作ると、変更に強く・再利用しやすく・分かりやすくなるか」を学びます。前半(第2〜3回)で考え方(概念)を、後半(第4〜7回)でそれを Python で実装する方法を学ぶ、という一本道です。

機械語 アセンブリ 高級言語 構造化言語 オブジェクト指向
図0:言語は「人間に分かりやすく・安全に」進化してきた。右へ行くほど高機能。
試験ポイント「構造化プログラミングで解決できたこと」と「残った課題」、そして「それをOOPがどう解決するか」はほぼ必出。残った課題=グローバル変数貧弱な再利用性
2
第2回 (4/21)

オブジェクト指向の歴史・特徴・概念

2-1. 言語の進化(なぜOOが生まれたか)

段階内容特徴・解決したこと
機械語0/1のバイナリ。16進(0-9,A-F)で表記。1940年代・真空管コンピュータが直接解釈。人間には極めて難しい
アセンブリ言語「ニーモニック」(命令コード)をアセンブラで機械語へ変換記号で書ける最初の一歩。専門家向け・少し間違うと暴走
高水準(高級)言語コンパイラで翻訳し高速実行複数の機械語をまとめた「高級な」文法
構造化言語基本三構造でGOTOレス、サブルーチン導入スパゲッティコード回避・サブルーチンの独立性強化
オブジェクト指向型言語(OOL)データとコードをオブジェクトにまとめるグローバル変数問題・貧弱な再利用を解決
定義基本三構造=順次構造・選択構造・繰り返し構造。これだけでどんな処理も書け、GOTO文の乱用(=スパゲッティコード)を排除できる。
定義ソフトウェア危機=プログラムが大規模化して構造が急速に複雑になり、開発担当者が離れると誰も扱えず、機能追加に多大なコストがかかる問題。
残った課題構造化でも、①グローバル変数(どこで参照・変更されるか分かりにくい)②貧弱な再利用 は解決できなかった。→ これをOOPが解決する。

2-2. オブジェクト指向(OOP)とは

互いに密接な関連を持つデータ(変数)とコード(メソッド)をひとつにまとめてオブジェクトとし、役割の異なるオブジェクトを相互に作用させてプログラム全体を構築するソフトウェア開発手法。

2-3. クラス 継承 ポリモーフィズム = 三大要素

要素働き目的覚え方(暗記)
クラス変数とメソッドをまとめて部品化整理整頓まとめて、隠して、たくさん作る
継承重複するクラス定義を共通化無駄を省くクラスの共通部分を別クラスにまとめる
ポリモーフィズムメソッドを呼び出す側を共通化無駄を省く共通メインルーチンを作る
進化したOOPの機能パッケージ(クラスをまとめる)/例外処理(戻り値とは別にメソッドからエラーを受け取る)/ガベージコレクション(不要インスタンスを削除しメモリを再利用)。

2-4. 機能中心 vs モノ中心(カレーの例)

① 構造化(機能中心) 材料を切る → 火をおこす → 煮る → …(一直線) 手順をそのまま記述。簡単だが再利用×・変更が他に波及。 ② オブジェクト指向(モノ中心) 切る人 火の人 煮る人 リーダ 役割ごとの「人(オブジェクト)」が連携。再利用◯・変更に強い。
図2:同じ「カレーを作る」でも、構造化は手順の列、OOは役割を持つオブジェクトの協調。
具体例(カレー)OOPなら「肉を変えたい」=豚を牛に差し替えるだけ、「切り方を変えたい」=切る人に新しい切り方を覚えさせるだけ。変更の影響が小さく、再利用できる(火をおこせる人がいれば魚も焼ける)。デメリットは学習難易度が高いこと。
3
第3回 (4/28)

クラス・継承・ポリモーフィズム+カプセル化

3-1. オブジェクトを考える3つの観点

  • 概念:オブジェクトは「責任」の集合
  • 仕様:オブジェクトは「起動できるメソッド」の集合
  • 実装:オブジェクトは「コードとデータ、その相互の演算処理」
具体例(セミナー講師)構造化では講師が全学生の移動先を調べて指示する(責任が集中)。OOPでは学生オブジェクト自身が「今いる教室」「次の教室」を知り自分で移動。講師は「次の教室へ行きなさい」と告げるだけ=責任の分散。

3-2. クラスとインスタンス

定義クラス=これから作る「モノの設計図」。属性(データ=名詞)機能(メソッド=動詞)をまとめたもの。インスタンス=設計図から作られ、実際に働く「実体(モノ)」。作ることをインスタンス化という。
ロボットクラス (設計図) 属性:名前・色・ID 機能:挨拶・手を振る インスタンス化 ロボット1名前=X(実体) ロボット2名前=Y(実体) 1つの設計図からいくつでも実体を作れる(量産)
図3:クラス(設計図)→ インスタンス化 → インスタンス(実体)。値を変えて量産できる。

3-3. カプセル化(情報隠蔽)

定義オブジェクトの内部構造(変数)を隠蔽し、公開されたインターフェース経由でのみ外部から操作させる仕組み。Java/C++では private(隠す)/public(公開)で区別。Pythonには基本的にこの区別はない。
オブジェクト 内部の変数・回路 (隠す=private) ボタン(公開) ユーザ
図4:テレビのリモコン。内部回路(隠す)を知らなくてもボタン(公開)で操作でき、誤操作・改造を防ぐ。

3-4. 継承

定義モノの種類(クラス)の共通点と相違点を体系的に整理する仕組み。共通のメソッド・変数を共通クラスに定義して再利用する。全体集合=親クラス(スーパークラス)、部分集合=子クラス(サブクラス)。親子の関係を is-a 関係 と呼ぶ(「乗用車 is-a 車」)。
車(親) 乗用車(子) トラック(子) is-a
図5:親ほど抽象的・シンプル。子は親の機能を引き継ぎ(再利用)、追加やオーバーライド(上書き)で拡張する。

3-5. ポリモーフィズム

定義処理対象のインスタンス(メソッド)が変わっても、処理する側を変更しなくてよい仕組み。語源:poly(たくさんの)+ morph(かたち)+ ism(考え方)=「たくさんのかたちの考え方」。
同じ命令「発進」 車A → ゆっくり発進 車B → 急加速で発進 目的は同じ処理は違う
図6:「同じボタン(メソッド)」で、どの車(インスタンス)も“それぞれの方法”で発進する。呼ぶ側は統一できる。
4
第4回 (5/12)

Python基礎:変数・条件・繰り返し・型

4-1. 算術演算子と変数

演算子意味
+ - * /加減乗除4 / 2 → 2.0
%剰余(余り)5 % 3 → 2
**べき乗6 ** 2 → 36
定義変数=値を入れる箱。変数 = 値 は「右辺を左辺に代入」する意味で、「等しい」ではない。
apple_price = 100
orange_price = 200
total = apple_price + orange_price
print(total)   # 300

4-2. 予約語・命名規則(PEP-8)

  • Pythonには 33個の予約語if for while def class True False None and or not import…)があり、変数名には使えない。
  • 変数に使える文字=英大文字・小文字・数字・_(アンダースコア)。ただし1文字目に数字は不可予約語は不可、組み込み関数名と被らないよう注意。
試験ポイント(命名規則)関数名・変数名・メソッド名=すべて小文字、単語区切りは _snake_case(例 my_function)。クラス名=大文字始まり、各単語の頭も大文字 → PascalCase(例 MyClass)。

コメント

print("こんにちは")   # 1行コメントは # 以降
"""
複数行コメントは
ダブルクォート3つで囲む
"""

4-3. 条件分岐(if / elif / else)

試験ポイント条件式の末尾はコロン :、本体はインデント(字下げ)。これを忘れるとエラー。
if 条件式1:
    処理a
elif 条件式2:      # elif は else if の略
    処理b
else:
    処理c
比較演算子>>=<<===(等しい)!=(等しくない)

4-4. 繰り返し(for / range)

for i in range(開始値, 終了値, 増減量):
    処理
試験ポイント(range)終了値は含まない(0〜終了値−1)。開始値(既定0)・増減量(既定1)は省略可。range(5) は 0,1,2,3,4。

4-5. 型とリスト

  • 数値型:整数 / 浮動小数点(-2.0)/ 複素数(8+9j
  • 文字列型'Hello' "Good bye"論理型True False
  • リスト型a = [50, 80, 100]インデックスは0始まり
  • 辞書型 {}(見出し:データ)/タプル型 ()(書き換え不可)/集合型 {}(順序なし・重複なし)
a = [50, 80, 100, 40, 40] 50 80 100 40 40 a[0] a[1] a[2] a[3] a[4]
図7:リストの添字は0から始まる。2次元は [[1,2,3],[4,5,6]] のようにリストの中にリスト。
5
第5回 (5/19)

Python:関数

定義関数=いくつかの処理を1つにまとめ、後から呼び出せるようにする機能(例:全自動洗濯機の「ボタン1つ」)。
def add(x, y):        # x, y は仮引数
    """xとyの和を返す"""   # docstring(説明文)
    return x + y      # 返り値(戻り値)

result = add(2, 3)    # 2, 3 は実引数 → result は 5
用語意味
仮引数関数を定義しているところに書く引数
実引数関数を呼び出すところに書く引数
返り値(戻り値)return で関数から返すデータ
試験ポイント(ローカル変数)関数の中で定義した変数はローカル変数で、その関数の中でしか使えない(外からは見えない)。変数はまずローカルを基準に考える。
組み込み関数定義せずに使える関数。print() len()(長さ・要素数)max() min() sorted()(並べ替え)range() など。
docstringクラスや関数の説明文。三重引用符 """…""" で1行簡潔に(72文字まで/PEP 257)。
6
第6回 (5/26)

Python:クラス実装

6-1. クラスを使うと何がうれしい?

  • 整理できる:スマホ=全体プログラム、フォルダ=クラス、アプリ=関数 のように分類して探しやすい。
  • 量産できる:1つの設計図(クラス)から、変数を変えていくつでもインスタンスを作れる。

6-2. 書式・インスタンス化

class クラス名():       # 授業ではクラス名に括弧を付ける
    変数の定義
    def メソッド名(self):
        処理

ins = クラス名()        # インスタンス化
ins.変数               # 変数の参照
ins.メソッド名()        # メソッドの呼び出し

6-3. self(最重要)

試験ポイント(self)クラス内メソッドの第1引数は必ず self。これは「仮のインスタンス」を表す。メソッド呼び出し時、インスタンスが自動的に第1引数として渡されるため、書かないとエラーになる。呼び出す側では self は指定不要(第2引数以降を渡す)。self.変数 / self.メソッド() で内部にアクセスできる。
class SampleClass():
    def s_method1(self, message):
        print(message)
    def s_method2(self):
        self.s_method1("Hello World!")   # 内部メソッドを self で呼ぶ

ins = SampleClass()
ins.s_method2()      # ⇒ Hello World!

6-4. コンストラクタ __init__

定義インスタンス生成時に最初に1度だけ自動実行される初期化用の特殊メソッド。前後がアンダースコア2つ(__init__)。第1引数は self、第2引数以降で初期値を受け取り、self.x = 引数インスタンス変数を設定する。
class Example():
    def __init__(self, a, b):
        self.num1 = a
        self.num2 = b
    def print_tot(self):
        print(self.num1 + self.num2)

myinstance = Example(1, 2)
myinstance.print_tot()   # 3
7
第7回 (6/2)

Python:継承・クラス変数とインスタンス変数

7-1. クラス変数 と インスタンス変数

種類性質クラス外部からクラス内で
インスタンス変数インスタンスごとに独立インスタンス.変数self.変数
クラス変数全インスタンス共通クラス名.変数インスタンス.変数self.変数(読み出し時のみ)
クラス クラス変数 (全員で共有) インスタンスA自分専用の変数 インスタンスB自分専用の変数 同名ならインスタンス変数が優先!
図8:クラス変数=みんなで共有、インスタンス変数=各自専用。両方に同名があるとインスタンス変数が優先

7-2. 継承の書式とオーバーライド

class 親クラス名():
    ...
class 子クラス名(親クラス名):    # 括弧の中に親クラス名 ← これが子クラスの目印
    ...
  • 親子の見分け方:クラス名の括弧の中にクラス名が書かれているかどうか。
  • オーバーライド=親で定義したメソッドを、子で同名メソッドとして上書き。
  • 子から親メソッドを呼ぶsuper().メソッド名()(Python3)/super(子クラス, self).メソッド名()(Python2)。
2つの作り方パターンA=先に親クラスを作ってから継承で子を作る。パターンB=必要なクラスを作り、共通部分を抜き出して親クラスにまとめ、子を作る。

7-3. 総合例(Animal → Dog / Snake)

class Animal():
    def __init__(self, legs):
        self.legs = legs
    def walk(self):
        print("歩く")
    def cry(self):
        print("鳴く")
    def get_legs_num(self):
        print(self.legs)

class Snake(Animal):
    def __init__(self, num):
        print("へびです")
        super().__init__(num)    # 親のコンストラクタを呼ぶ
    def walk(self):              # オーバーライド
        print("はう")
    def cry(self):
        print("シャー")

nyoro = Snake(0)
nyoro.walk()           # はう
nyoro.cry()            # シャー
nyoro.get_legs_num()   # 0
PAST ASSIGNMENT

過去の中間レポート課題+模範解答

最重要これは実際に配布された中間レポート課題(原本)です。中間試験はこの3パターン(関数/クラス/継承)が軸になる可能性が非常に高い。何も見ずに最後まで手書きで再現できることを最終目標にしてください。

課題(1) 関数 ドリンク会計

① 請求金額を計算する関数 cal_bill。仮引数は「コーヒーの数」「ジュースの数」、返り値は請求金額。コーヒー=500円、ジュース=400円。
② 会計情報を表示する関数 bill_inf。実引数は「テーブル番号(1〜10)」と「請求金額(=cal_billの返り値)」。実行すると「テーブル△の請求金額は、〇〇〇円です。」と表示。
③ テーブル番号10・コーヒー3杯・ジュース2杯で bill_inf を実行。
模範解答を見る
def cal_bill(coffee, juice):
    """コーヒーとジュースの数から請求金額を返す"""
    return coffee * 500 + juice * 400

def bill_inf(table, price):
    """テーブル番号と請求金額を受け取り会計を表示"""
    print(f"テーブル{table}の請求金額は、{price}円です。")

bill_inf(10, cal_bill(3, 2))
# → テーブル10の請求金額は、2300円です。(500*3 + 400*2 = 2300)

f"..."(f文字列)が未習なら print("テーブル" + str(table) + "の請求金額は、" + str(price) + "円です。") でも可。

課題(2) クラス 銀行口座 BankAccount

BankAccountクラスを作成。データ=口座番号(数値)・所有者名(文字列)・残高(数値)をコンストラクタの引数で持たせる。メソッド=「預け入れ」「引き出し」「詳細表示(全データ表示)」。
② インスタンス化して、口座番号=学修番号、名前=自分の苗字、残高=50000 で作成し、預け入れ・引き出しを自由な金額で実行。
模範解答を見る
class BankAccount():
    def __init__(self, number, name, balance):
        self.number = number
        self.name = name
        self.balance = balance
    def deposit(self, amount):       # 預け入れ
        self.balance += amount
    def withdraw(self, amount):      # 引き出し
        self.balance -= amount
    def show(self):                  # 詳細表示
        print(f"口座番号:{self.number} 名義:{self.name} 残高:{self.balance}円")

acc = BankAccount(12345678, "平本", 50000)
acc.deposit(10000)    # 60000
acc.withdraw(3000)    # 57000
acc.show()            # 口座番号:12345678 名義:平本 残高:57000円

課題(3) 継承 円 → 円柱(Circle → Cylinder)

① 円を表す Circleクラス。コンストラクタで半径を引数に取り、面積を計算するメソッドを作成(返り値=面積)。
Circle継承して Cylinderクラスを作成。コンストラクタで半径と高さを引数に取り、半径は親のコンストラクタを呼んで設定。体積を計算するメソッドは親の面積メソッドを利用(面積×高さ)。
③ 半径5・高さ10でインスタンス化し、体積を表示。
模範解答を見る
class Circle():
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return self.radius ** 2 * 3.14      # 半径×半径×円周率

class Cylinder(Circle):
    def __init__(self, radius, height):
        super().__init__(radius)            # 親に半径を設定(重要)
        self.height = height
    def volume(self):
        return self.area() * self.height    # 親のareaを利用(面積×高さ)

cy = Cylinder(5, 10)
print(cy.volume())     # 785.0(= 5*5*3.14 * 10)
採点で差がつく子のコンストラクタで super().__init__(radius) を呼び、体積計算で self.area()(親メソッド)を再利用している点が「継承を理解しているか」の核心。ここを自力で書けるように。
課題の原本(スクリーンショット)を見る
中間レポート課題の原本
GLOSSARY

用語集(定義問題そのまま対策)

用語定義(太字を落とさず書く)
オブジェクト指向関連するデータとメソッドをオブジェクトにまとめ、オブジェクト同士の相互作用でプログラムを構築する開発手法
クラスモノの設計図。属性(データ)と機能(メソッド)をまとめたもの
インスタンスクラスから作られ、実際に動作する実体(モノ)
カプセル化内部構造を隠蔽し、公開インターフェース経由で操作を制御する仕組み(情報隠蔽)
継承クラスの共通点・相違点を整理し、共通部分を親クラスにまとめて再利用する仕組み
is-a関係継承の親子関係。「子 is-a 親」(例:乗用車 is-a 車)
オーバーライド親クラスのメソッドを、子クラスで同名メソッドとして上書きすること
ポリモーフィズム処理対象が変わっても、処理する側を変更しなくてよい仕組み(多態性)
ソフトウェア危機大規模化で複雑になり、担当者が離れると扱えず機能追加に多大なコストがかかる問題
selfクラス内メソッドの第1引数。仮のインスタンスを表す(必須)
コンストラクタ生成時に1度だけ自動実行される初期化用の特殊メソッド __init__
クラス変数 / インスタンス変数全インスタンス共通の変数 / インスタンスごとに独立した変数(同名はインスタンス変数優先)
CHEAT SHEET

最重要 早見表

  • クラス整理整頓/「まとめて、隠して、たくさん作る」
  • 継承class 子(親):/オーバーライド/super()
  • ポリモーフィズム呼ぶ側を共通化/poly+morph+ism
  • カプセル化情報隠蔽(Pythonにprivate/public区別なし)
  • 構造化の残課題=グローバル変数・貧弱な再利用 → OOPが解決
  • self=第1引数・必須/__init__=生成時1度だけ
  • クラス変数=全体共通/インスタンス変数=個別(同名はインスタンス変数優先
  • rangeは終了値を含まない/リストの添字は0始まり
  • 命名:関数・変数=snake_case、クラス=PascalCase
PRACTICE

予想問題+解答

毎回の宿題(練習・演習問題)が出題パターンの軸。クリックで解答が開きます。

概念編

Q1. 言語の進化を5段階で並べ、構造化で解決できたことと残った課題を述べよ。

解答を見る機械語→アセンブリ言語→高水準(高級)言語→構造化言語→オブジェクト指向型言語。構造化で解決=スパゲッティコード回避・共通サブルーチンによる再利用。残った課題=グローバル変数問題貧弱な再利用性(OOPが解決)。

Q2. 三大要素を挙げ、目的と覚え方を説明せよ。

解答を見るクラス(整理整頓/まとめて隠してたくさん作る)、継承(無駄を省く/共通部分を別クラスにまとめる)、ポリモーフィズム(無駄を省く/呼び出す側を共通化)。

Q3. カプセル化・is-a関係・インスタンス・ポリモーフィズム・ソフトウェア危機を説明せよ。

解答を見る用語集(上)の定義の通り。特にカプセル化=情報隠蔽(公開インターフェース経由で操作)、is-a=継承の親子、ポリモーフィズム=処理する側を変えなくてよい仕組み。

Q4. 構造化とOOPを「カレー」または「セミナー講師」の例で比較せよ。

解答を見る構造化=手順を一直線に記述(簡単だが再利用×・変更が波及)。OOP=役割を持つオブジェクトが連携(再利用◯・変更の影響が小さいが学習難易度が高い)。講師の例なら、OOPでは学生オブジェクト自身が移動し、講師は指示するだけ。

Python実装編

P1. 整数を入力し、偶数なら Even、奇数なら Odd を表示。

解答を見る
n = int(input())
if n % 2 == 0:
    print("Even")
else:
    print("Odd")

P2. 1〜100の3の倍数の和(for+if)。

解答を見る
total = 0
for i in range(1, 101):
    if i % 3 == 0:
        total += i
print(total)   # 1683

P3. for の2重ループで「*」を階段状に5行表示。

解答を見る
for i in range(1, 6):
    for j in range(i):
        print("*", end="")
    print()

P4. 二次方程式の解を返す関数 quadratic(a,b,c)(複数解)。

解答を見る
def quadratic(a, b, c):
    d = b**2 - 4*a*c
    x1 = (-b + d**0.5) / (2*a)
    x2 = (-b - d**0.5) / (2*a)
    return x1, x2

print(quadratic(2, -13, 15))   # (5.0, 1.5)

P5. 身長cm・体重kgからBMIを返す関数 calc_bmi。

解答を見る
def calc_bmi(height_cm, weight_kg):
    """身長cmと体重kgからBMIを返す"""
    h = height_cm / 100
    return weight_kg / (h ** 2)

print(calc_bmi(170, 65))

P6. Humanクラス(weight=65、run で-5、eat で+5して表示)。tanakaで実行。

解答を見る
class Human():
    weight = 65
    def run(self):
        self.weight -= 5
        print(self.weight)
    def eat(self):
        self.weight += 5
        print(self.weight)

tanaka = Human()
tanaka.run()   # 60
tanaka.eat()   # 65

P7. Staffクラス(基本給5000+ボーナス)。salaryメソッド。abe(10000),suga(20000)。

解答を見る
class Staff():
    def __init__(self, bonus):
        self.bonus = bonus
    def salary(self):
        return 5000 + self.bonus

abe = Staff(10000)
print(abe.salary())    # 15000

P8. Healthクラス(身長・体重をコンストラクタ引数に)でBMIを返す cal_bmi。

解答を見る
class Health():
    def __init__(self, height, weight):
        self.height = height
        self.weight = weight
    def cal_bmi(self):
        h = self.height / 100
        return self.weight / (h ** 2)

aso = Health(160, 65)
print(aso.cal_bmi())

P9. Animalを継承したDog(cryをオーバーライド)。pochiで全メソッド実行。

解答を見る
class Animal():
    legs = 4
    def walk(self):
        print("歩く")
    def cry(self):
        print("鳴く")
    def get_legs_num(self):
        print(self.legs)

class Dog(Animal):
    def __init__(self):
        print("犬です")
    def cry(self):
        print("わんわん")

pochi = Dog()
pochi.walk(); pochi.cry(); pochi.get_legs_num()

P10. クラス変数とインスタンス変数の違いと、同名のときの優先順位。

解答を見るクラス変数は全インスタンス共通(クラス名.変数)、インスタンス変数は個別(インスタンス.変数)。両方に同名があればインスタンス変数が優先される。
FINAL CHECK

直前チェックリスト

  • 言語進化の5段階と、構造化の残課題(グローバル変数・再利用)を言える
  • 三大要素を「目的+覚え方」で説明できる
  • カプセル化・is-a・ポリモーフィズムを自分の言葉で説明できる
  • クラスとインスタンスの違い(設計図と実体)を図で描ける
  • if / for / range の書式(コロン・インデント)を手書きできる
  • range が終了値を含まない/添字が0始まりを覚えた
  • 命名規則(snake_case / PascalCase)を区別できる
  • クラス定義・インスタンス化・self を手書きできる
  • __init__ でインスタンス変数を初期化できる
  • 継承 class 子(親):・オーバーライド・super() を書ける
  • クラス変数とインスタンス変数の違い・優先順位を説明できる
↑ 表紙に戻る