今回は、第19回から第25回までの演習を総括し、解説と解答を掲載していきます。


第19回「オブジェクト指向について学ぼう(1)」の解答解説

演習1

問題

タプルに比べて構造体が優れている点を述べてください。

解答

タプルは順序でデータを管理するのに対し、構造体は要素ごとに名前でデータを管理するため。たとえばタプルに要素を追加すると参照する順が変わってバグになる可能性があるが、構造体はそれがない。

演習2

問題

以下の用語について説明してください。

  • クラス
  • インスタンス
  • インスタンス変数

記事の本文から探すだけではなく、検索するなどしていろいろな情報を得てもらいたいです。

解答

省略します。

演習3

問題

生徒の成績を保持するクラスを作ります。以下の条件でクラスを作成してください。

  • クラス名 Score
  • 生徒の名前を保持するインスタンス変数 name を作成
  • 国数英の変数であるmath、english、japaneseを作成

解説

インスタンス変数はクラスのインスタンスごとに持つ変数です。一般的にはクラスの__init__関数(コンストラクタ)の中で宣言と初期化をします。

解答

class Score:
    def __init__(self):
        self.name = ''
        self.math = 0
        self.english = 0
        self.japanese = 0

s = Score()
s.name = 'taro'
s.math = 100
s.english = 100
s.japanese = 100

演習4

問題

数学mathの成績が一番良い生徒の名前を表示するプログラムを書いてください。なお、以下にテスト用のコードがありますので必要であればコピペして使ってください。

taro = Score()
taro.name = 'taro'
taro.math = 60
taro.english = 70
taro.japanese = 80

jiro = Score()
jiro.name = 'jiro'
jiro.math = 80
jiro.english = 70
jiro.japanese = 90

saburo = Score()
saburo.name = 'saburo'
saburo.math = 50
saburo.english = 30
saburo.japanese = 60

ヒント:リストから最大値を探すプログラムを説明したことがあります。生徒のリストを作成すれば同じ要領で処理できるはずです。

解説

生徒の点数を比較していき、最大の生徒のインスタンスを保持しておけば大丈夫です。最大値を探すアルゴリズムとほとんど同じです。

解答

math_max_student = taro
for student in [taro, jiro, saburo]:
    if(math_max_student.math < student.math):
        math_max_student = student

print(math_max_student.name)
# jiro

演習5(必須ではない)

問題

「名前付きタプル(namedtuple)」という型を使うとCの構造体に近いことができます。これについて調べて、演習3~4の内容を名前付きタプルで実現してください。

解説

namedtupleは以下のように使います。

from collections import namedtuple

Point = namedtuple('Point', 'x y')

p1 = Point(11, 22)
print(p1.y)
# 22

print(p1[0])
# 11

print(p1)
# Point(x=11, y=22)

p2 = Point(y = 22, x = 11)
print(p2)
# Point(x=11, y=22)

まずnamedtupleのインポートをし、「型の定義」をしています。上記だとx, yというフィールドを持つPointという型を作っています。初期化はコンストラクタに似たかたちでフィールドの値を引数で与えます。フィールドへのアクセスは名前か従来のタプルのような形で順序を指定します。初期化時に名前を使うことも可能です。ただ、注意して欲しいのはあくまでもタプルなので、再代入できないということです。

from collections import namedtuple
Point = namedtuple('Point', 'x y')
p1 = Point(11, 22)
p1.x = 55

# Traceback (most recent call last):

# File "/Users/yuichi/Desktop/test.py", line 4, in 

# p1.x = 55

# AttributeError: can't set attribute

解答

まず、Scoreを作成します。

from collections import namedtuple

Score = namedtuple('Score', 'name math english japanese')
taro = Score('taro', 10, 20, 30)
print(taro)
# Score(name='taro', math=10, english=20, japanese=30)

そして次に数学の成績が一番高い生徒名を表示するプログラムです。

from collections import namedtuple
Score = namedtuple('Score', 'name math english japanese')

taro = Score('taro', 60, 70, 80)
jiro = Score('jiro', 80, 70, 90)
saburo = Score('saburo', 50, 30, 60)

math_max_student = taro
for student in [taro, jiro, saburo]:
    if(math_max_student.math < student.math):
        math_max_student = student

print(math_max_student.name)

クラスで作ったものとまったく同じコードで大丈夫でした。


第20回「オブジェクト指向について学ぼう(2)」の解答解説

演習1

問題

以下のクラスにmath、english、japaneseの最高得点の科目名と点数を表示するメソッドを作成してください。

class Score:
    def __init__(self):
        self.name = ''
        self.math = 0
        self.english = 0
        self.japanese = 0

解説

特にありません。科目数が増えたら解答例よりエレガントな比較をすべきかもしれません。

解答

class Score:
    def __init__(self):
        self.name = ''
        self.math = 0
        self.english = 0
        self.japanese = 0

    def print_max(self):
        if(self.math > self.japanese and self.math > self.english):
            print('math: ' + str(self.math))
        elif(self.english > self.japanese):
            print('english: ' + str(self.english))
        else:
            print('japanese: ' + str(self.japanese))

s = Score()
s.math = 50
s.english = 70
s.japanese = 60

s.print_max()

# english: 70

演習2

問題

演習1で作成したクラスでのデータの初期化に、コンストラクタを利用してください。

解説

コンストラクタはメソッド__init__です。これに引数を加えると、インスタンスの初期化時にパラメータを与えられるようになります。

解答

class Score:
    def __init__(self, name, math, english, japanese):
        self.name = name
        self.math = math
        self.english = english
        self.japanese = japanese

    def print_max(self):
        if(self.math > self.japanese and self.math > self.english):
            print('math: ' + str(self.math))
        elif(self.english > self.japanese):
            print('english: ' + str(self.english))
        else:
            print('japanese: ' + str(self.japanese))

s = Score('taro', 50, 70, 60)
s.print_max()

# english: 70

演習3

問題

手続き型言語に比べて、なぜオブジェクト指向型言語が優れているかを調べてください。

解答

省略します。


第21回「オブジェクト指向について学ぼう(3)」の解答解説

演習1

問題

クラスのコンストラクタやメソッドの第一引数がなぜselfであるか説明してください。selfの実体がなにか説明してください。

解答

名前はselfでなくても構いませんが、メソッドの第一引数は、そのインスタンス自体と決まっています。ただ、selfという変数名にするというのは暗黙のルールに近いため、特に理由がなければselfとするのがよいと思います。

演習2

問題

以下について説明してください。

  • グローバル変数
  • 他のモジュールのグローバル変数を使う方法
  • 関数内のローカル変数
  • クラス変数
  • インスタンス変数

解答

省略します。

演習3

問題

クラスベースのオブジェクト指向とプロトタイプベースのオブジェクト指向について調べてください。

解答

省略します。


第24回「オブジェクト指向について学ぼう(6)」の解答解説

演習1

問題

以下のクラスを継承し、オリジナルのクラスのインスタンス変数 a をプリントするメソッドを実装してください。

class ClassA:
    def __init__(self):
        self.a = 'hello'

解説

継承されるクラスを親クラス、それを継承したクラスを子クラスと呼びます。子クラスは親クラスが持つインスタンス変数やメソッドにアクセスできます。

解答

class ClassA:
    def __init__(self):
        self.a = 'hello'

class ClassB(ClassA):
    def __init__(self):
        ClassA.__init__(self)

    def print_a(self):
        print(self.a)

b = ClassB()
b.print_a()

# hello

演習2

問題

以下のクラスを継承し、オリジナルのクラスのインスタンス変数a, bを足した値をプリントするメソッドを実装してください。なお、継承したクラスのコンストラクタも引数を2つ受け取り、それを親クラスの初期化に利用してください。

class ClassA:
    def __init__(self, a, b):
        self.a = a
        self.b = b

解説

子クラスから親クラスのコンストラクタをどう呼び出すかが重要です。文法的なものなので覚えてください。

解答

class ClassA:
    def __init__(self, a, b):
        self.a = a
        self.b = b

class ClassB(ClassA):
    def __init__(self, a, b):
        ClassA.__init__(self, a, b)

    def print_sum(self):
        print(self.a + self.b)

b = ClassB(3, 6)
b.print_sum()

# 9

演習3

問題

listを継承し、コンストラクタに与えた引数のタイプだけ格納できるリストクラスを作成してください。たとえば、

ml = MyList('a')

とすると、文字列型しか受け取らなくなります。

class MyList(list):
    def __init__(self, var):
        self.store_type = type(var)

    def append(self, item):
        if(type(item) == self.store_type):
            list.append(self, item)

ml = MyList('a')
ml.append('a')
ml.append('b')
ml.append(1)
print(len(ml))

解説

本文にあった「Intだけを格納するList」を改良すれば作れます。コンストラクタで「ある型のデータ」を受け取り、その型と同じ型のみ受け取るようにすればよいです。

解答

class MyList(list):   
    def __init__(self, var):
        self.store_type = type(var)
        list.__init__(self)

    def append(self, elem):
        if(self.store_type == type(elem)):
            list.append(self, elem)
        else:
            print('error: "' + str(elem) + '" is not ' + str(self.store_type ))

ml1 = MyList('hello')
ml1.append('abc')
ml1.append(1)
print(ml1)

ml2 = MyList(0)
ml2.append('abc')
ml2.append(1)
print(ml2)

第25回「オブジェクト指向について学ぼう(7)」の解答解説

演習1

問題

ポリモーフィズムとは何か説明してください。この記事だけでなく、いろいろなソースをあたっていただきたいと思います。

解答

省略します。

演習2

問題

多重継承のメリットとデメリットについて調べてください。

解答

多重継承を行うと2つのクラスの特徴を引き継げるため、コードの再利用という面では役に立ちます。ただ、2つも継承してしまうと複雑さが増し、たとえば「同じインスタンス名、メソッド名が重複したらどうする」といったことも考える必要が生まれてきます。

私は極力多重継承を使いません。多重継承が必要なら「ひとつは継承し、ひとつはコンポジションする」などとしてもよいかもしれませんね。

演習3

問題

継承とコンポジションのそれぞれのメリット・デメリットについて調べてください。『Effective Java』に載っているこのトピックは非常に有名なので、調べれば情報がでてくると思います。

解答

省略します。


次回はコラム回となります。Pythonのプログラムの配布方法について扱います。

執筆者紹介

伊藤裕一(ITO Yuichi)

シスコシステムズでの業務と大学での研究活動でコンピュータネットワークに6年関わる。専門はL2/L3 Switching とデータセンター関連技術およびSDN。TACとしてシスコ顧客のテクニカルサポート業務に従事。社内向けのソフトウェア関連のトレーニングおよびデータセンタとSDN関係の外部講演なども行う。

もともと仮想ネットワーク関連技術の研究開発に従事していたこともあり、ネットワークだけでなくプログラミングやLinux関連技術にも精通。Cisco社内外向けのトラブルシューティングツールの開発や、趣味で音声合成処理のアプリケーションやサービスを開発。

Cisco CCIE R&S, Red Hat Certified Engineer, Oracle Java Gold,2009年度 IPA 未踏プロジェクト採択

詳細(英語)はこちら