Python3エンジニア認定基礎試験 第9章 クラス 予想問題と解説
Python3エンジニア認定基礎試験「第9章 クラス」の予想問題を22問収録しています。各問を解説つきで掲載し、全問は演習画面で解けます。出題は公式シラバスの範囲内です。
この章の要点
クラスの定義とインスタンス、継承とプライベート変数、スコープと名前空間、イテレータとジェネレータなどを扱う分野です。全22問(基礎7問・標準12問・応用3問)を収録しています。
- クラスの定義とインスタンス
- 継承とプライベート変数
- スコープと名前空間
- イテレータとジェネレータ
情報源と編集区分
公式範囲を確認し、問題数・難易度・確認点は当サイトが集計・編集。
試験概要(公式)確認しておきたいポイント
当サイトの作問時に観察した、取り違えやすい論点です(公式の統計ではありません)。
- メソッドの最初の引数 self の扱い。公式チュートリアル9.4は「この名前付けは単なる慣習でしかありません。self という名前は、Python では何ら特殊な意味を持ちません」と述べており、予約語ではない。ただし慣習に従わないと読みにくくなるとも述べている。
- クラス変数とインスタンス変数の混同。どちらに属する値かで、他のインスタンスへの影響が変わる。
- 最初の引数を省略できると思い込むこと。インスタンスのメソッドは最初の引数でインスタンス自身を受け取るため省略できない。
この章に関連する比較記事
混同しやすい2つの概念を並べて整理した記事です。
よくある質問
Q第9章 クラスは何問収録されていますか?
現在22問を収録しています。内訳は基礎7問・標準12問・応用3問です。
Q第9章 クラスの頻出論点は何ですか?
クラスの定義とインスタンス、継承とプライベート変数、スコープと名前空間、イテレータとジェネレータなどが出題対象です(当サイト収録問題に基づく編集部集計)。
Q間違えた問題はどう復習すればいいですか?
誤答した選択肢のどこがずれていたかを解説で確認し、関連用語がある場合は用語集で定義に戻ってから再演習することをおすすめします。
クラスのメソッドの最初の引数について、正しい説明はどれか。
答えと解説を見る
正解は「呼び出したインスタンス自身を受け取る引数で、慣習として self と名付ける」である。インスタンスのメソッドは最初の引数でそのインスタンス自身を受け取り、その名前は慣習として self が使われる。self という名前は言語の予約語ではないが、慣習に従うのが読みやすさの面で望ましい。クラス自身を受け取る形は別の種類のメソッドの話で、名前が固定されているわけでもない。最初の引数を書かずに省略することはできず、戻り値を入れるための引数でもない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
クラス定義の中で __spam のように先頭に2つのアンダースコアを持つ識別子を使ったとき、Pythonが行う処理として正しいものはどれか。
答えと解説を見る
正解は「_クラス名__spam の形式に置き換える名前修飾(マングリング)を行う」である。公式チュートリアルは、先頭に2個以上、末尾に1個以下のアンダースコアを持つ識別子は_classname__spamの形にテキスト置換されると説明しており、classnameは先頭のアンダースコアを取り除いた現在のクラス名である。この仕組みはサブクラスが内部のメソッド呼び出しを壊さずにメソッドをオーバーライドできるようにするためのものであり、Pythonにはオブジェクトの外から一切アクセスできない真にプライベートな変数は存在しない。名前修飾は主に不慮の名前衝突を防ぐためのもので、確信犯的にアクセスすることは依然として可能である。単に先頭のアンダースコアを取り除くだけの処理ではなく、構文エラーにもならない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
Pythonにおける名前空間とスコープの説明として、正しい組み合わせはどれか。
答えと解説を見る
正解は「名前空間は名前からオブジェクトへの対応付けで、スコープはその名前空間へ直接アクセスできるプログラム上の領域である」である。公式チュートリアルは、名前空間を名前からオブジェクトへの対応付け、スコープをある名前空間へ直接アクセスできるPythonプログラムのテキスト上の領域と定義している。ここで直接アクセスとは、名前の前に修飾を付けず参照したとき、その名前空間から名前を探すことをいう。したがって両者は関係するが同じ概念ではなく、定義を入れ替えた説明は誤りである。また、名前空間はオブジェクトを置くメモリ領域そのものではなく、スコープもオブジェクトの寿命を表す語ではない。名前空間をモジュール名だけ、スコープをクラス内部だけに限定する説明も、それぞれの定義を不当に狭めている。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
yield文を含むジェネレータに対してnext()を繰り返し呼んだときの動作として、正しいものはどれか。
答えと解説を見る
正解は「以前に中断した処理を再開し、ローカル変数と実行状態を呼び出しの間で保持する」である。公式チュートリアルは、ジェネレータを通常の関数のように書き、データを返すときにyield文を使うと説明している。next()が呼び出されるたびに、ジェネレータは以前に中断した処理を再開し、すべてのデータ値と最後に実行した文を記憶する。このため、毎回関数の先頭へ戻ってローカル変数を初期化するわけではなく、最初から全結果をリストにまとめて返す動作でもない。また、ジェネレータでは__iter__()と__next__()が自動で作成されるため、利用者がこの2つを手作業で追加する必要はない。処理が終了するとStopIterationも自動的に送出される。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
class Team: members = [] のように定義し、__init__ では self.name = name だけを行い、メソッド add_member では self.members.append(person) を実行する設計にした。異なる名前で作成した複数のTeamインスタンスでadd_memberを呼んだときに起こることとして、正しいものはどれか。
答えと解説を見る
正解は「membersはクラス変数としてすべてのインスタンスで共有されるため、あるインスタンスのadd_memberで追加した要素が他のインスタンスのmembersにも現れる」である。公式チュートリアルは、クラス変数を『そのクラスのすべてのインスタンスによって共有される属性』、インスタンス変数を『それぞれのインスタンスについて固有のデータ』と説明しており、class文の直下でmembers = []のように書いた変数はクラス変数になる。self.membersに対してappendのような変更操作を行うと、共有された同じリストオブジェクトが変更されるため、他のインスタンスから見ても変化が現れる。一方、self.name = nameのように__init__の中でselfへ代入した変数はインスタンス変数であり、インスタンスごとに固有のデータになるため、他のインスタンスのnameには影響しない。公式チュートリアルは、リストや辞書のような可変オブジェクトをクラス変数にすると、全インスタンスで共有されてしまう場合があると注意している。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
インスタンスx に対する x.f() という呼び出しと、メソッドオブジェクトの仕組みについて、正しい説明はどれか。
答えと解説を見る
正解は「x.f()はMyClass.f(x)と厳密に等価であり、インスタンスの非データ属性が関数オブジェクトとして見つかると、インスタンスと関数への参照がメソッドオブジェクトにパックされる」である。公式チュートリアルは、x.f()という呼び出しがMyClass.f(x)と厳密に等価であると述べている。インスタンスの非データ属性が参照されたとき、そのインスタンスのクラスが検索され、見つかった属性が関数オブジェクトであれば、インスタンスへの参照と関数オブジェクトへの参照がメソッドオブジェクトにパックされると説明されている。このメソッドオブジェクトはm.__self__でインスタンスを、m.__func__で対応する関数オブジェクトを参照する属性を持つ。xを渡す必要がないという説明や、m.__func__とm.__self__の対応を入れ替えた説明、メソッドオブジェクトが関数オブジェクトへの参照を持たないという説明は、いずれも公式チュートリアルの記載と一致しない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
class DerivedClassName(Base1, Base2, Base3): のように複数の基底クラスを指定したとき、属性の探索順序について正しい説明はどれか。
答えと解説を見る
正解は「深さ優先・左から右の順で探索され、まずBase1とその基底クラスを再帰的に探索し、見つからなければBase2を探索する。ただし同じクラスが継承階層に複数回現れる場合は2度探索しない」である。公式チュートリアルは、class DerivedClassName(Base1, Base2, Base3):のような定義における属性の探索が深さ優先・左から右で行われ、まずBase1とその基底クラスを再帰的に探索し、見つからなければBase2を探索すると説明している。また、同じクラスが継承階層で複数回現れる場合(ダイアモンド継承)は2度探索しないとし、協調的なsuper()呼び出しに対応するためメソッド解決順序(MRO)は動的なアルゴリズムで線形化されると述べている。Base3から先に探索するという説明や、3つを同時に探索して最後に書かれたものを優先するという説明、探索順序が定義されていないという説明は、いずれも公式チュートリアルの記載と一致しない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
関数の中で名前を参照するとき、実行中に直接アクセスできる入れ子のスコープを探される順に並べたものとして、正しいものはどれか。
答えと解説を見る
正解は「最も内側のローカルな名前、外側の関数の名前、モジュールのグローバルな名前、ビルトイン名の順」である。公式チュートリアル9.2は、最初に探される最も内側のスコープがローカルな名前を持ち、次に外側の関数のスコープが近いほうから順に探され、その次がモジュールのグローバルな名前、一番外側で最後に検索されるのがビルトイン名だと説明している。グローバルな名前やビルトイン名を先に探す順序ではなく、外側の関数のスコープもグローバルより先に探される。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
入れ子になった関数の内側から、外側の関数のスコープにある変数を再束縛(別の値を束縛し直す)したい。そのために使う文として、正しいものはどれか。
答えと解説を見る
正解は「nonlocal文」である。公式チュートリアル9.2は、最も内側のスコープの外側にある変数に再束縛するにはnonlocal文を使うと説明している。nonlocalと宣言されなかった変数は読み出し専用となり、そのような変数への書き込みは新しいローカル変数を最も内側のスコープで作るだけで、外側のスコープの値は変化しない。global文はモジュールのグローバルスコープの変数を対象にするものであり、外側の関数のスコープの変数を対象にするものではない。del文は名前への束縛を取り除く文である。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
関数scope_testの中でspamに"test spam"を代入した後、内側の関数を順に呼ぶ。do_localは宣言なしでspamに"local spam"を代入し、do_nonlocalはnonlocal spamと宣言して"nonlocal spam"を代入し、do_globalはglobal spamと宣言して"global spam"を代入する。3つを呼び終えたとき、「scope_test内のspam」と「モジュールレベルのspam」の値の組み合わせとして、正しいものはどれか。
答えと解説を見る
正解は「scope_test内は"nonlocal spam"、モジュールレベルは"global spam"」である。公式チュートリアル9.2.1の例では、デフォルトのローカルな代入はscope_test上のspamへの束縛を変更せず、nonlocal代入はscope_test上のspamへの束縛を変更し、global代入はモジュールレベルの束縛を変更する。出力は、ローカル代入の後が"test spam"、nonlocal代入の後が"nonlocal spam"、global代入の後もscope_test内は"nonlocal spam"のままで、モジュールレベルは"global spam"になる。global代入の前にはモジュールレベルのspamには何も束縛されていなかった。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
クラスCに__init__(self, realpart, imagpart)が定義されているとき、x = C(3.0, -4.5)を実行したときの動作として、正しいものはどれか。
答えと解説を見る
正解は「新しく作られたインスタンスに対して__init__()が自動的に呼び出され、3.0と-4.5が引数として渡される」である。公式チュートリアル9.3.2は、クラスが__init__()メソッドを定義している場合、クラスをインスタンス化すると新しく作られたクラスインスタンスに対して自動的に__init__()を呼び出すと説明している。また、クラスのインスタンス化操作に渡された引数は__init__()に渡される。明示的に呼び出す必要はなく、クラスはインスタンス化のために関数記法で呼び出せる。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
class Dogの中でkind = 'canine'と書き、__init__の中でself.name = nameと書いた。d = Dog('Fido')、e = Dog('Buddy')としたとき、正しい説明はどれか。
答えと解説を見る
正解は「kindは全インスタンスで共有されるクラス変数で、nameはインスタンスごとに固有のインスタンス変数である」である。公式チュートリアル9.3.5は、インスタンス変数はそれぞれのインスタンスについて固有のデータのためのもの、クラス変数はそのクラスのすべてのインスタンスによって共有される属性やメソッドのためのものだと説明している。この例では、d.kindもe.kindも'canine'で共有され、d.nameは'Fido'、e.nameは'Buddy'とそれぞれ固有である。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
クラスMyClassのインスタンスxに対して、MyClassにはcounterという名前の属性が定義されていない状態でx.counter = 1を実行した。この動作として、正しいものはどれか。
答えと解説を見る
正解は「データ属性は最初に代入された時点で生まれるため、インスタンスxにcounter属性が作られる」である。公式チュートリアル9.3.3は、データ属性は、ローカル変数と同じように、最初に代入された時点で湧き出てくると説明し、x.counter = 1を実行して値を使った後にdel x.counterで跡を残さず削除できる例を示している。事前の宣言は必要なく、代入したインスタンスだけに属性が作られる。なお、代入で作られた属性は、公式チュートリアルの例のようにdel x.counterで削除でき、その後は跡が残らない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
isinstance(obj, int)がTrueになる条件として、公式チュートリアルの説明に合うものはどれか。
答えと解説を見る
正解は「obj.__class__がintまたはintの派生クラスである場合に限る」である。公式チュートリアル9.5は、isinstance()でインスタンスの型が調べられ、isinstance(obj, int)はobj.__class__がintやintの派生クラスの場合に限りTrueになると説明している。リテラルかどうかや値の変換可否は条件ではなく、派生クラスもTrueになる。isinstance()は、継承関係も含めて型を調べるため、派生クラスのインスタンスにも使える。リテラルかどうかや、値が変換できるかどうかは判定の条件ではない。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
issubclass(bool, int)とissubclass(float, int)の結果の組み合わせとして、公式チュートリアルの説明に合うものはどれか。
答えと解説を見る
正解は「issubclass(bool, int)はTrue、issubclass(float, int)はFalse」である。公式チュートリアル9.5は、issubclass()でクラスの継承関係が調べられ、boolはintのサブクラスなのでissubclass(bool, int)はTrue、floatはintのサブクラスではないのでissubclass(float, int)はFalseになると説明している。issubclass()は、クラス同士の継承関係を調べるための組み込み関数で、isinstance()がインスタンスの型を調べるのと対になる。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
派生クラスで上書きしたメソッドの中から、基底クラスの同名のメソッドを直接呼び出す簡単な方法として、公式チュートリアルが示しているものはどれか。
答えと解説を見る
正解は「BaseClassName.methodname(self, arguments)と書く」である。公式チュートリアル9.5は、派生クラスで上書きしたメソッドは基底クラスの同名のメソッドを置き換えるのではなく拡張したい場合があり、基底クラスのメソッドを直接呼び出す簡単な方法として、単にBaseClassName.methodname(self, arguments)を呼び出すと説明している。この呼び出し方が動作するのは、基底クラスがグローバルスコープのBaseClassNameという名前でアクセスできるときだけである。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
for文がコンテナオブジェクトの要素を順に処理する仕組みと、ループの終了について、正しい説明はどれか。
答えと解説を見る
正解は「コンテナに対してiter()を呼び、返されたイテレータの__next__()で要素を1つずつ取り出し、要素が尽きるとStopIteration例外でループが終了する」である。公式チュートリアル9.8は、for文は裏でコンテナオブジェクトに対してiter()関数を呼び、その関数は要素に1つずつアクセスする__next__()メソッドが定義されたイテレータオブジェクトを返すと説明している。これ以上要素がない場合、__next__()メソッドはStopIteration例外を送出し、その通知を受けてforループは終了する。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
自作のクラスにイテレータとしての振る舞いを持たせたい。公式チュートリアルの説明に合うものはどれか。
答えと解説を見る
正解は「__next__()メソッドを持つオブジェクトを返す__iter__()メソッドを定義する。クラス自身が__next__()を定義している場合は、__iter__()は単にselfを返してよい」である。公式チュートリアル9.8は、自作のクラスにイテレータとしての振る舞いを追加するには、__next__()メソッドを持つオブジェクトを返す__iter__()メソッドを定義すればよいと説明し、クラスが__next__()を定義している場合は__iter__()は単にselfを返すことも可能だと述べている。要素が尽きたときは__next__()でStopIterationを送出する。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
ジェネレータが、クラスを使ったイテレータより簡潔に書ける理由として、公式チュートリアルの説明に合うものはどれか。
答えと解説を見る
正解は「__iter__()と__next__()が自動で作成され、ローカル変数と実行状態が自動で保存され、終了時にStopIterationが自動で送出されるから」である。公式チュートリアル9.9は、ジェネレータの定義がコンパクトになるのは__iter__()メソッドと__next__()メソッドが自動で作成されるからであり、呼び出しごとにローカル変数と実行状態が自動的に保存され、終了時には自動的にStopIterationが送出されると説明している。これらの機能により、通常の関数を書くのと同じ労力でイテレータを生成できる。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
ジェネレータ式の構文について、公式チュートリアルの説明に合うものはどれか。
答えと解説を見る
正解は「リスト内包表記に似た構文で、角括弧ではなく丸括弧で囲む」である。公式チュートリアル9.10は、単純なジェネレータは式として簡潔にコーディングでき、その式はリスト内包表記に似た構文を使うが、角括弧ではなく丸括弧で囲うと説明している。ジェネレータ式は、関数の中でジェネレータをすぐに使いたいような状況のために用意されている。括弧の種類が違うだけで、リスト内包表記に似た構文で書ける点が要点である。ジェネレータ式は、関数の中でジェネレータをすぐに使いたい状況のために用意されている。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
Pascalの「レコード」やC言語の「構造体」のように、名前つきのデータ要素を一まとめにするデータ型がほしいとき、公式チュートリアルが慣用的な手法として挙げているものはどれか。
答えと解説を見る
正解は「dataclassesを使う」である。公式チュートリアル9.7は、Pascalのレコード(record)やC言語の構造体(struct)のように名前つきのデータ要素を一まとめにするデータ型があると便利なことがあり、慣用的な手法としてこの目的にdataclassesを使用すると説明している。例として、@dataclassを付けたクラスEmployeeにname・dept・salaryを宣言し、Employee('john', 'computer lab', 1000)でインスタンスを作る形が示されている。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)
インスタンスのメソッドオブジェクトmが持つ属性について、正しい説明はどれか。
答えと解説を見る
正解は「m.__self__はmの属しているインスタンスオブジェクトで、m.__func__はそのメソッドに対応する関数オブジェクトである」である。公式チュートリアル9.7は、インスタンスメソッドオブジェクトにも属性があり、m.__self__はメソッドm()の属しているインスタンスオブジェクト、m.__func__はそのメソッドに対応する関数オブジェクトだと説明している。9.3.4の説明のとおり、メソッドオブジェクトはインスタンスへの参照と関数オブジェクトをパックしたものである。
https://docs.python.org/ja/3/tutorial/classes.html (Python公式チュートリアル 第9章 クラス)