仮の宿 学習室

クラウド・AI・Python CLOUD / AI / PYTHON

関数・モジュール・入出力

講義 3 本・確認問題 30 問 | 本試験では「Python3基礎」(0問)の一部 | 最終更新 2026-09-24

この章で学ぶこと
目次
  1. 関数を定義して呼び出す
  2. スコープとモジュール
  3. ファイルの読み書きとJSON
  4. 確認問題(30問)
  5. 演習ツール

1. 関数を定義して呼び出す

位置引数とキーワード引数、デフォルト引数、*args と **kwargs の受け取り方が分かり、ミュータブルなデフォルト引数がなぜ前の呼び出しを覚えているのかを説明できるようになります。

関数は、いくつかの手順にひとつの名前を付けて、あとから何度でも呼び出せるようにする道具です。def のあとに関数名と丸括弧を書き、括弧の中に受け取る値の名前(仮引数)を並べ、字下げした本体に処理を書きます。呼び出す側は本体の中身を知らなくてよく、どんな値を渡せば何が返ってくるかだけを知っていれば使えます。この「呼び出し側と中身を切り離す」ことが関数のいちばんの目的で、変更したいときに直す場所がひとつで済むようになります。

値の渡し方には、書いた順に対応させる位置引数と、名前を指定して渡すキーワード引数の2つがあります。仮引数に等号で値を書いておくとデフォルト引数になり、呼び出し側が省略したときにその値が使われます。ひとつの呼び出しの中では位置引数を先に、キーワード引数をあとに書きます。定義のほうにも決まりがあり、デフォルト値を持つ仮引数のうしろにデフォルト値を持たない仮引数を置くことはできません。これを書くと実行前の構文解析の段階で SyntaxError となり、そのファイルは1行も実行されません。実行してみるまで気づかない種類の誤りではなく、その場で止まる誤りです。

個数を決めずに受け取りたいときは、仮引数の前に星印を付けます。星1つの仮引数には、対応しきれなかった位置引数がタプルとしてまとめて入ります。星2つの仮引数には、対応しきれなかったキーワード引数が辞書としてまとめて入ります。名前は args と kwargs が慣習ですが、決まりではありません。大事なのは受け取る型のほうで、余りが1つも無かったときは空のタプルと空の辞書になり、None にはなりません。星1つと星2つを両方書くときは、必ず星1つを先に書きます。

呼び出す側でも星印が使えます。リストやタプルの前に星1つを付けると、その中身が位置引数として1つずつ配られます。辞書の前に星2つを付けると、キーと同じ名前の仮引数へ値が配られます。定義側の星は「散らばった値を1つにまとめる」働き、呼び出し側の星は「1つにまとまった値を散らばらせる」働きで、向きがちょうど逆になっています。同じ記号なので混同しやすい場所です。星を付け忘れるとリストそのものが最初の仮引数にまるごと入ってしまい、エラーにならないまま結果だけがおかしくなります。

ここからが、この節でいちばん間違えやすいところです。デフォルト値の式は、関数を呼ぶたびに評価されるのではなく、def の行が実行されたときに1回だけ評価され、その結果が関数オブジェクトに保存されます。保存先は __defaults__ という属性で、タプルとして持たれます。デフォルト値が数値や文字列のような変更できない値であれば、1回しか評価されなくても困りません。ところがリストや辞書のような変更できる値を書くと、その1つのオブジェクトがすべての呼び出しで共有されます。本体の中で append すると、次に呼んだときも前回の要素が残ったままになります。__defaults__ を表示すると、中身が増えていく様子をそのまま確認できます。

同じ理由から、デフォルト値に外側の変数を書いた場合も、def を実行した時点の値が焼き付けられます。あとでその変数を書き換えても、関数のデフォルト値は変わりません。この2つはどちらも「デフォルト値は定義時に1回だけ評価される」というひとつの規則から出てきます。ミュータブルなデフォルト引数を避けたいときの定石は、デフォルト値を None にしておき、本体の先頭で None かどうかを確かめて、None なら新しい空のリストを作ることです。こうすると呼び出しごとに別のリストが作られ、呼び出し側が明示的にリストを渡したときはそれを使う、という両方の要求を満たせます。

戻り値は return で返します。return を1度も通らずに関数が終わると、戻り値は None になります。print で表示しているだけの関数を代入して使おうとしてつまずくのは、この None が原因です。return のうしろにカンマ区切りで複数の値を書くと、それらはタプルにまとめられて1つの値として返ります。呼び出し側で同じ数の名前に代入すれば、まとめて受け取れます。返り値が複数あるように見えても、Python が返しているのはあくまで1つのタプルです。

関数まわりの小道具も押さえておきます。lambda は名前を持たない小さな関数を式の中で作る書き方で、本体には式を1つだけ書きます。sorted の key のように、関数を引数として渡したいときによく使います。関数本体の最初に文字列リテラルを置くと docstring になり、__doc__ 属性から読み出せます。仮引数や戻り値にコロンや矢印で型を書くのがアノテーションで、__annotations__ という辞書に記録されます。ただしアノテーションは記録されるだけで、実行時に型が検査されることはありません。int と書いた仮引数に文字列を渡しても、そのまま動いてしまいます。検査したければ別の道具を使います。

def calc(a, b=10, *rest, **opts) を、いろいろな書き方で呼んだときに各仮引数へ入る値
呼び出しabrestopts
calc(7)710(){}
calc(7, 8)78(){}
calc(7, 8, 9)78(9,){}
calc(7, mode='fast')710(){'mode': 'fast'}
calc(b=8, a=7)78(){}
calc(*[7, 8, 9])78(9,){}
calc(**{'a': 7, 'b': 8})78(){}
def calc(a, b=10, *rest, **opts):    return (a, b, rest, opts) print(calc(7))                 # (7, 10, (), {})print(calc(7, 8, 9))           # (7, 8, (9,), {})print(calc(7, mode='fast'))    # (7, 10, (), {'mode': 'fast'})

用語

位置引数
呼び出し側で書いた順に、定義側の仮引数へ左から順に対応づけられる引数。
キーワード引数
仮引数の名前を指定して渡す引数。順序を入れ替えて書ける。ひとつの呼び出しでは位置引数より後ろに書く。
デフォルト引数
仮引数に等号で既定の値を書いておき、呼び出し側が省略したときに使われるようにしたもの。
SyntaxError
構文の誤りで実行前に検出される例外。デフォルト値を持つ仮引数のうしろに持たない仮引数を置くと発生し、そのファイルは1行も実行されない。
*args
定義側で星1つを付けた仮引数。対応しきれなかった位置引数がタプルとしてまとめて入る。余りが無ければ空のタプル。
**kwargs
定義側で星2つを付けた仮引数。対応しきれなかったキーワード引数が辞書としてまとめて入る。余りが無ければ空の辞書。
アンパック呼び出し
呼び出し側でリストの前に星1つ、辞書の前に星2つを付けて、中身を引数として配る書き方。定義側の星とは働きが逆向き。
__defaults__
関数オブジェクトがデフォルト値を保持している属性。タプルで持たれ、デフォルト値がリストなら中身の変化がそのまま見える。
ミュータブルなデフォルト引数
リストや辞書をデフォルト値に書いてしまうこと。1つのオブジェクトが全呼び出しで共有され、前の呼び出しの変更が残る。
デフォルト値の評価時点
デフォルト値の式が評価されるのは def を実行したときの1回だけ。呼び出しのたびに評価し直されることはない。
None を既定にする書き方
デフォルト値を None にし、本体の先頭で None なら新しい空のリストを作る定石。呼び出しごとに別のオブジェクトになる。
戻り値
return で返す値。return を通らずに終わった関数の戻り値は None になる。
複数の戻り値
return のうしろにカンマ区切りで値を並べると、1つのタプルにまとめられて返る。返っているのはあくまで1つの値。
lambda
名前を持たない小さな関数を式の中で作る書き方。本体には式を1つだけ書く。型は通常の関数と同じ function。
docstring
関数本体の先頭に置いた文字列リテラル。__doc__ 属性から読み出せる説明文になる。
アノテーション
仮引数や戻り値に書く型の注記。__annotations__ に記録されるだけで、実行時に型が検査されることはない。

例題

例題:次の関数を collect('a')、collect('b') の順に呼ぶと、それぞれ何が返るか。また collect.__defaults__ はどうなっているか。 def collect(item, box=[]): box.append(item) return box
答えと考え方 1回目の collect('a') は ['a'] を返し、2回目の collect('b') は ['b'] ではなく ['a', 'b'] を返す。そのあと collect.__defaults__ を見ると (['a', 'b'],) になっている。理由は、デフォルト値の [] が def の行を実行したときに1回だけ作られ、その同じリストが関数オブジェクトの __defaults__ に保存されるからである。呼び出しのたびに新しい空リストが作られるわけではないので、append した結果がそのまま次の呼び出しに引き継がれる。避けるには box=None を既定にして、本体の先頭で box が None なら box = [] とする。こうすれば呼び出しごとに別のリストが作られ、呼び出し側が自分のリストを渡したときはそれを使う、という両方が成り立つ。
例題:LIMIT = 100 と書いたあと def cap(n=LIMIT): return n と定義し、続けて LIMIT = 200 に書き換えた。このとき cap() と cap(LIMIT) はそれぞれ何を返すか。
答えと考え方 cap() は 100 を返し、cap(LIMIT) は 200 を返す。デフォルト値の式 LIMIT は def の行が実行された瞬間に評価され、そのときの値である 100 が関数オブジェクトに保存される。あとから変数 LIMIT を 200 に書き換えても、保存済みのデフォルト値は書き換わらない。一方 cap(LIMIT) は呼び出しの時点で LIMIT を読むので、そのときの値である 200 が渡る。ミュータブルなデフォルト引数の罠と、この「定義時に1回だけ評価される」性質は、別々の話に見えて同じひとつの規則から出ている。デフォルト値の欄に書いた式は、関数の一部ではなく定義の一部だと考えるとよい。

出典・根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)/一般社団法人Pythonエンジニア育成推進協会「Python 3 エンジニア認定基礎試験 出題範囲」(2026年8月確認)

2. スコープとモジュール

名前が LEGB の順に探されることを理解し、global と nonlocal の使い分け、UnboundLocalError の起こる理由、import と __name__ の仕組み、仮想環境の判定までを押さえます。

プログラムの中に同じ名前の変数が複数あっても混ざらないのは、名前を有効にする範囲が決まっているからです。この範囲をスコープと呼びます。ある名前を読もうとしたとき、Python は決まった順序で入れ物を探します。まず実行中の関数の中(Local)、次に自分を囲んでいる関数の中(Enclosing)、次にモジュールの最上位(Global)、最後に組み込み(Built-in)です。頭文字を取って LEGB と呼びます。見つかった時点で探索は終わるので、内側で同じ名前を使えば外側の名前は隠れます。逆にいえば、内側で定義していない名前は自動的に外側から借りてくることになります。

読むだけなら外側の名前をそのまま使えますが、書き換えようとすると話が変わります。関数の中で名前に代入すると、その名前は新しくローカルな名前として作られ、外側の同名の変数はまったく影響を受けません。外側のモジュール最上位の変数を関数の中から本当に書き換えたいときは、関数の先頭で global 宣言をします。global を書いた名前への代入は、ローカルを作らずにモジュール最上位の変数へ届きます。

書き換えたい相手がモジュール最上位ではなく、ひとつ外側の関数の変数である場合には nonlocal を使います。関数の中に関数を書き、内側から外側の作業用の変数を更新したいときの道具です。global が「いちばん外まで飛ぶ」のに対して、nonlocal は「ひとつ外の関数の枠に届く」と覚えると混同しにくくなります。nonlocal は対応する名前が外側の関数に存在していないと使えず、モジュール最上位の変数を指すこともできません。

ここでつまずきやすいのが UnboundLocalError です。Python は関数の本体をコンパイルする時点で、その関数の中に代入がある名前をすべてローカル変数だと決めてしまいます。判定されるのは関数全体に対してであって、行の順番は関係ありません。したがって、関数の後ろのほうで代入している名前を、その前の行で読もうとすると、外側に同じ名前があってもローカルとして扱われ、まだ値が入っていない状態で読まれることになります。これが UnboundLocalError です。よく出るのは、モジュール最上位のカウンタを関数の中で total = total + 1 のように更新しようとする形で、右辺の total を読む時点でローカル扱いになっているために失敗します。global 宣言を足すか、値を引数で受け取って戻り値で返す形に直します。

スコープの話が済んだら、次はファイル単位のまとまりであるモジュールです。Python のソースファイル1つがそのままモジュール1つで、拡張子を除いたファイル名がモジュール名になります。import mymod と書くと mymod.pub のように前置きを付けて使う形になり、from mymod import hello と書くと名前をそのまま持ち込めます。長い名前は import mymod as mm や from mymod import hello as h のように as で別名を付けられます。どの書き方でも、モジュールの本体が実行されるのは最初の1回だけです。

from mymod import * と書くと、下線で始まらない名前がまとめて持ち込まれます。ただしモジュール側に __all__ という文字列のリストが定義してあれば、持ち込まれるのはそこに挙げた名前だけになります。__all__ は「星印で持ち出してよい名前の一覧」を作者が宣言するための仕組みです。星印による取り込みは、どの名前が入ってきたのかがコードから読み取れなくなり、既存の名前を上書きしてしまうこともあるため、対話環境の外では避けるのが普通です。

モジュールには __name__ という属性があり、import されたときにはモジュール名が入ります。一方、そのファイルを python3 で直接実行したときには、同じ __name__ に '__main__' が入ります。この違いを使うのが if __name__ == '__main__': という書き方です。この下に書いた処理は、直接実行したときだけ動き、他のファイルから import されたときには動きません。動作確認用のコードや、コマンドとして呼ばれたときの入口を、ライブラリとしての中身と同じファイルに同居させるための定番の書き方です。

モジュールを探す場所は sys.path というリストに並んでいます。ただのリストなので実行中に追加することもできますが、置き場所を工夫するほうが健全です。一度読み込んだモジュールは sys.modules という辞書にキャッシュされ、同じモジュールを二度 import してもファイルは読み直されません。二度目の import は、キャッシュされている同じモジュールオブジェクトを結び付け直すだけです。コマンドラインから渡された値は sys.argv というリストで受け取り、その先頭にはスクリプト名そのものが入ります。したがって引数を2つ渡したときの sys.argv の長さは3になります。

最後に仮想環境です。python3 -m venv でプロジェクトごとの独立した環境を作ると、その中の pip でインストールしたパッケージは他のプロジェクトに影響しません。いま自分が仮想環境の中で動いているかどうかは、sys.prefix と sys.base_prefix を見比べれば分かります。仮想環境の中では sys.prefix がその環境のディレクトリを指し、sys.base_prefix は元になった Python のディレクトリを指すため、2つは食い違います。仮想環境の外では両方が同じ値になります。pip の版やインストール先の具体的な文字列は環境ごとに違うので、覚えるのはこの食い違いの有無だけで十分です。

LEGB の4つの段と、そこへ代入するために必要な宣言
段指す範囲代入に必要な宣言
Localいま実行中の関数の中不要(代入すればローカルが作られる)
Enclosing自分を囲んでいる外側の関数の中nonlocal
Globalモジュールの最上位global
Built-inlen や print などの組み込み代入すると同名のグローバルが作られる
BASE = 'module' def show():    BASE = 'function'    def deeper():        return BASE      # Enclosing を読む    return deeper() print(show())            # functionprint(BASE)              # module

用語

スコープ
名前が有効になる範囲。同じ名前が別の場所にあっても混ざらないのはスコープが分かれているため。
LEGB
名前を探す順序。Local、Enclosing、Global、Built-in の頭文字。見つかった時点で探索は終わる。
Enclosing
自分を囲んでいる外側の関数のスコープ。関数の中に関数を書いたときにだけ現れる段。
global
関数の中からモジュール最上位の変数へ代入するための宣言。書かずに代入すると新しいローカル変数ができる。
nonlocal
内側の関数から、ひとつ外側の関数の変数へ代入するための宣言。モジュール最上位の変数は指せない。
UnboundLocalError
関数の中に代入がある名前を、値が入る前に読んだときに起こる例外。代入が後ろの行にあっても関数全体でローカル扱いになる。
モジュール
Python のソースファイル1つ分のまとまり。拡張子を除いたファイル名がそのままモジュール名になる。
import
モジュールを読み込む文。import mymod なら前置き付きで使い、from mymod import hello なら名前をそのまま持ち込む。
as
import した名前に別名を付ける書き方。import mymod as mm や from mymod import hello as h のように使う。
from ... import *
下線で始まらない名前をまとめて持ち込む書き方。どの名前が入ったか読み取れなくなるため通常は避ける。
__all__
モジュール側に置く文字列のリスト。星印による取り込みで持ち出される名前を、この一覧に限定できる。
__name__
モジュールが持つ属性。import されたときはモジュール名、直接実行したときは '__main__' が入る。
if __name__ == '__main__':
直接実行したときだけ動かしたい処理を置く定番の書き方。import されたときは実行されない。
sys.path
モジュールを探すディレクトリを並べたリスト。実行中に要素を追加することもできる。
sys.modules
読み込み済みモジュールのキャッシュ辞書。同じモジュールを二度 import してもファイルは読み直されない。
sys.argv
コマンドラインから渡された値のリスト。先頭にスクリプト名が入るので、引数2つなら長さは3になる。
venv
プロジェクトごとに独立した Python 環境を作る標準の仕組み。python3 -m venv で作成する。
sys.prefix と sys.base_prefix
仮想環境の中では両者が食い違い、外では一致する。仮想環境で動いているかの判定に使える。

例題

例題:モジュール最上位に total = 0 があるとき、次の関数を呼ぶと何が起こるか。直すにはどうすればよいか。 def bump(): total = total + 1
答えと考え方 UnboundLocalError となり、cannot access local variable 'total' where it is not associated with a value というメッセージが出る。関数の中に total への代入があるため、Python はコンパイルの時点で total をこの関数のローカル変数だと決めてしまう。すると右辺の total を読む段階で、まだ値の入っていないローカル変数を読むことになり、最上位の total = 0 は参照されない。直し方は2つある。ひとつは関数の先頭に global total と書いて、最上位の変数を書き換える意思を明示すること。もうひとつは def bump(total): return total + 1 のように値を引数で受け取り、戻り値で返して呼び出し側で代入することで、こちらのほうが副作用が無く読みやすい。
例題:geo.py の先頭に __all__ = ['area', 'PI'] と宣言し、PI と UNIT と area() と _round2() を定義してある。別のファイルで from geo import * と書いたとき、使えるようになる名前はどれか。
答えと考え方 使えるようになるのは area と PI の2つだけで、UNIT も _round2 も入ってこない。星印による取り込みは、通常なら下線で始まらない名前をすべて持ち込むが、モジュール側に __all__ があるときはその一覧に挙がっている名前だけに絞られる。UNIT は下線で始まっていないが __all__ に無いので入らず、_round2 は下線で始まっているうえに __all__ にも無いので二重に入らない。関数か変数かで扱いが変わることはなく、area が入って PI が入らないといったこともない。UNIT を使いたければ from geo import UNIT のように名前を指定するか、import geo として geo.UNIT と書けばよい。__all__ が効くのは星印による取り込みのときだけで、名前を指定した取り込みや前置き付きのアクセスを禁止するものではない。

出典・根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)/Python Software Foundation「Python チュートリアル 6. モジュール」Python 3.14 ドキュメント(2026年8月確認)/Python Software Foundation「Python チュートリアル 12. 仮想環境とパッケージ」Python 3.14 ドキュメント(2026年8月確認)

3. ファイルの読み書きとJSON

open のモードの違い、with 文が保証すること、read と readlines と for 文の読み分け、そして json での往復で型がどう変わるかを、実行結果とともに押さえます。

ファイルを扱うときは、まず open でファイルオブジェクトを作ります。第1引数がパス、第2引数がモードで、モードを省略すると読み込み専用の 'r' になります。'r' は既存のファイルを読むためのもので、ファイルが無ければ FileNotFoundError になります。'w' は書き込み用で、同じ名前のファイルがあれば中身を切り詰めて空にしてから書き始めます。'a' も書き込み用ですが、既存の内容は残し、末尾に付け足します。'x' は新規作成専用で、すでに同名のファイルがあると FileExistsError になります。読み書きの両方をしたいときは 'r+' を使い、文字列ではなくバイト列として扱いたいときは 'rb' や 'wb' のように b を添えます。

書き込み用に開いたファイルから読もうとしたり、読み込み用に開いたファイルへ書こうとしたりすると、io.UnsupportedOperation という例外になります。メッセージはそれぞれ not readable、not writable です。文法として通ってしまう書き方なので、開くときのモードを取り違えたことに気づくのは実行してからになります。逆にいえば、モードは「そのファイルオブジェクトに何を許すか」を宣言している場所だといえます。

開いたファイルは必ず閉じなければなりません。閉じ忘れを防ぐ標準の書き方が with 文です。with のブロックを抜けるときにファイルは自動的に閉じられ、閉じたかどうかは closed 属性で確かめられます。ブロックの中で例外が発生して途中で抜けた場合でも、閉じる処理は必ず実行されます。カンマで区切れば複数のファイルを1つの with 文でまとめて開くこともでき、その場合はどれも同じように閉じられます。閉じたあとのファイルオブジェクトを読もうとすると ValueError となり、I/O operation on closed file. と知らされます。

読み方には使い分けがあります。read は残り全部を1つの文字列として返します。ファイルオブジェクトは現在位置を覚えているので、読み切ったあとにもう一度 read を呼ぶと、位置が末尾にあるため空文字列が返ります。何も表示されないのを見て「読み込みに失敗した」と誤解しやすい場面です。readline は1行だけを返し、readlines は全行をリストにして返します。どちらの場合も行末の改行文字はそのまま残ることに注意してください。改行を落としたければ rstrip を使います。

ファイルオブジェクトはそれ自体が反復可能なので、for line in f と書けば1行ずつ取り出せます。この書き方は全体をメモリに読み込まないので、大きなファイルでも安全です。行の内容は readlines のときと同じく改行付きで渡ってきます。行を集めたリストが本当に必要な場合を除けば、for 文で回すほうが素直です。

書き込みは write で行います。write は書き込んだ文字数を戻り値として返すので、代入して受け取ると長さが分かります。改行は自動では付かないので、行を区切りたいときは自分で改行文字を書きます。print も file 引数を渡せばファイルへ出力でき、こちらは末尾に改行が付きます。現在位置は tell で取得でき、seek で移動できます。seek(0) で先頭に戻せば、もう一度最初から読み直せます。

プログラムの中のデータをそのままファイルに残したいときは json が便利です。json.dumps は Python のオブジェクトを JSON の文字列にし、json.loads は JSON の文字列を Python のオブジェクトに戻します。ファイルを相手にするときは json.dump と json.load を使います。対応関係はおおむね素直で、辞書はオブジェクト、リストは配列、True は true、None は null になります。

ただし往復で型が変わる場面があるので注意が必要です。タプルは JSON の配列に変換されますが、読み戻したときはリストになります。JSON に配列とタプルの区別が無いため、元がどちらだったかという情報は残りません。辞書のキーも同じで、整数をキーにした辞書を dumps すると、キーは文字列に変換されます。読み戻したときのキーは文字列のままで、整数には戻りません。集合はそもそも JSON に対応する型が無く、dumps しようとすると TypeError となり、Object of type set is not JSON serializable と知らされます。集合を残したいときは、あらかじめリストへ変換しておきます。

文字の扱いにも既定があります。json.dumps は ensure_ascii の既定値が真になっているため、日本語などの非 ASCII 文字はエスケープされた形で出力されます。これは JSON として正しく、読み戻せば元の文字に戻りますが、人が目で読むファイルとしては扱いにくくなります。ensure_ascii に偽を渡せば、日本語をそのままの文字で出力できます。indent を渡せば字下げ付きで整形され、sort_keys を渡せばキーを並べ替えて出力できます。

open のモードと、そのファイルオブジェクトで許される操作
モード対象ファイルが無いとき読み書き
'r'(既定)FileNotFoundError可不可
'w'新しく作られる不可可(既存の内容は切り詰め)
'a'新しく作られる不可可(末尾に追記)
'x'新しく作られる不可可(既にあると FileExistsError)
'r+'FileNotFoundError可可
'rb'FileNotFoundError可(bytes)不可
path = '/tmp/memo.txt' with open(path, 'w') as f:    f.write("alpha\nbeta\n")   # 戻り値は 11 with open(path) as f:    for line in f:        print(repr(line))       # 'alpha\n' のあと 'beta\n'

用語

open
ファイルオブジェクトを作る組み込み関数。第2引数のモードを省略すると読み込み専用の 'r' になる。
モード 'r'
読み込み専用。ファイルが無ければ FileNotFoundError になる。書き込もうとすると io.UnsupportedOperation。
モード 'w'
書き込み用。同名のファイルがあれば中身を切り詰めて空にしてから書き始める。
モード 'a'
書き込み用。既存の内容を残したまま、末尾に付け足す。
モード 'r+'
読み書きの両方ができるモード。対象のファイルは存在している必要がある。
モード 'rb'
バイト列として読むモード。read の戻り値は str ではなく bytes になる。
io.UnsupportedOperation
モードで許されていない操作をしたときの例外。not readable や not writable というメッセージが付く。
with 文
ブロックを抜けるときに必ず後始末を行う構文。例外で抜けた場合もファイルは閉じられる。カンマ区切りで複数まとめられる。
closed
ファイルオブジェクトが閉じられているかを表す属性。with を抜けたあとは真になる。
read
現在位置から残り全部を1つの文字列として返す。読み切ったあとに再度呼ぶと空文字列が返る。
readline
1行だけを返す。行末の改行文字は残る。
readlines
全行をリストにして返す。各要素の行末には改行文字が残る。
write
文字列を書き込み、書き込んだ文字数を戻り値として返す。改行は自動では付かない。
seek と tell
tell は現在位置を返し、seek は位置を移動する。seek(0) で先頭に戻せる。
json.dumps と json.loads
Python のオブジェクトを JSON 文字列にするのが dumps、JSON 文字列を Python に戻すのが loads。
タプルの往復
タプルは JSON の配列になり、読み戻すとリストになる。タプルには戻らない。
キーの文字列化
辞書のキーは JSON では文字列になる。整数キーは dumps で文字列になり、読み戻しても整数には戻らない。
ensure_ascii
json.dumps の引数。既定は真で非 ASCII 文字をエスケープする。偽にすると日本語がそのままの文字で出力される。

例題

例題:同じファイルに対して、'w' で first を書き、続けて 'w' で second を書き、最後に 'a' で third を書いた。読み出すと何が入っているか。
答えと考え方 secondthird が入っている。'w' で開くと、その時点で既存の内容の有無に関わらずファイルは長さ0に切り詰められるからである。順に追うと、最初の 'w' で first が入り、2回目の 'w' で開いた瞬間に first が消えてそこへ second が書かれる。最後の 'a' は追記なので second を残したまま末尾に third が足され、secondthird になる。切り詰めが起きるのは開いた瞬間であって書き込んだ瞬間ではないので、'w' で開いただけで何も書かずに閉じればファイルは空になる。追記したいのに 'w' で開いてしまい、前回の実行結果が毎回消えるというのはよくある事故である。書き足すつもりなら 'a'、作り直すつもりなら 'w' と、意図をモードで表明する。なお 'w' も 'a' も読み込みは許されないので、書いた直後に同じオブジェクトから読み返すことはできない。
例題:d = {'name': '山田', 'tags': ('x', 'y')} を json.dumps したあと json.loads で読み戻すと、tags の型は何になるか。また日本語をそのままの文字で出力するにはどうするか。
答えと考え方 tags はリストになる。JSON には配列しか無くタプルという型が存在しないため、dumps の時点でタプルは配列に変換され、loads で読み戻すときも配列はリストとして復元される。元がタプルだったという情報はどこにも残らないので、往復させると型が変わってしまう。タプルのまま扱いたければ、読み戻したあとに自分で tuple() を適用する必要がある。日本語については、json.dumps の ensure_ascii の既定値が真なので、そのままでは山田がエスケープされた形で出力される。ensure_ascii に偽を渡せば、日本語がそのままの文字で書き出される。読み戻した結果はどちらでも同じ文字列になる。

出典・根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)/Python Software Foundation「Python 標準ライブラリ json --- JSON エンコーダーとデコーダー」Python 3.14 ドキュメント(2026年8月確認)

確認問題(30問)

四肢択一。「正解と解説」を開くと、正解の理由と他の選択肢が違う理由を確認できます。

問1|引数の受け取り

星印の付いた仮引数を持つ次の関数を、位置引数だけで呼び出した。表示される内容はどれか。

def f(a, b=2, *args, **kwargs):    return (a, b, args, kwargs) print(f(1, 3, 4, 5))
  1. (1, 2, (3, 4, 5), {})
  2. (1, 3, (4, 5), {})
  3. (1, 3, [4, 5], {})
  4. (1, 3, (4, 5), None)
正解と解説
正解:B. (1, 3, (4, 5), {})

位置引数は左から順に対応づけられるので、a に 1、b に 3 が入る。b にはデフォルト値 2 があるが、値が渡されているのでそちらが優先される。対応しきれなかった 4 と 5 は星1つの args にタプルとしてまとめられ、(4, 5) になる。キーワード引数は1つも渡していないので kwargs は空の辞書 {} になり、None にはならない。b にデフォルト値の 2 が残ると考えると (1, 2, (3, 4, 5), {}) になるが、位置引数はデフォルト値より優先される。args に入るのはリストではなくタプルなので、角括弧で表示されることもない。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問2|キーワード指定

定義に無い名前をキーワードで渡した次の呼び出しについて、表示される内容はどれか。

def f(a, b=2, *args, **kwargs):    return (a, b, args, kwargs) print(f(1, x=9))
  1. (1, 9, (), {})
  2. (1, 2, (9,), {})
  3. (1, 2, {'x': 9}, {})
  4. (1, 2, (), {'x': 9})
正解と解説
正解:D. (1, 2, (), {'x': 9})

位置引数は 1 だけなので a に 1 が入り、b は渡されていないのでデフォルト値の 2 のままになる。x という名前の仮引数は定義に無いため、キーワード引数 x=9 は星2つの kwargs に {'x': 9} として入る。位置引数の余りは無いので args は空のタプル () になる。x=9 が b に入ると考えると (1, 9, (), {}) になるが、キーワード引数は名前が一致する仮引数にしか入らない。9 が args に入ると考えると (1, 2, (9,), {}) になるが、名前を付けて渡した値が位置引数の余りに回ることはない。星1つが辞書を、星2つがタプルを受け取ると取り違えると、args に {'x': 9} が入り kwargs が空になる形と見えてしまう。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問3|既定の空リスト

デフォルト値に空のリストを書いた次の関数を3回呼んだ。表示される3行はどれか。

def add(x, lst=[]):    lst.append(x)    return lst print(add(1))print(add(2))print(add(3))
  1. 上から順に [1] / [1, 2] / [1, 2, 3] と表示される
  2. 上から順に [1] / [2] / [3] と表示される
  3. 上から順に [1] / [1, 2] / [2, 3] と表示される
  4. 上から順に None / None / None と表示される
正解と解説
正解:A. 上から順に [1] / [1, 2] / [1, 2, 3] と表示される

デフォルト値の [] は def の行を実行したときに1回だけ作られ、その同じリストが関数オブジェクトに保存される。呼び出しのたびに新しい空リストが作られるわけではないので、append の結果が次の呼び出しに残り、[1]、[1, 2]、[1, 2, 3] と伸びていく。呼び出しごとに新しいリストになると考えると [1] / [2] / [3] になるが、それは lst=None を既定にして本体で作り直した場合の挙動である。本体を return lst.append(x) と書き違えると、append の戻り値である None が3回並ぶ。なお3行に分けず print(add(1), add(2), add(3)) と1行で書くと、3つとも同じリストを指しているため、すべて評価し終えたあとの [1, 2, 3] が3回並んで表示される。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問4|既定値の共有

3回呼び出したあとに関数オブジェクトの属性を表示した。表示される内容はどれか。

def add(x, lst=[]):    lst.append(x)    return lst add(1)add(2)add(3)print(add.__defaults__)
  1. 出力は ([],) のようになる
  2. 出力は ([1, 2, 3],) のようになる
  3. 出力は [[1, 2, 3]] のようになる
  4. 出力は ((1, 2, 3),) のようになる
正解と解説
正解:B. 出力は ([1, 2, 3],) のようになる

関数オブジェクトはデフォルト値を __defaults__ という属性にタプルで保持している。仮引数のうちデフォルト値を持つのは lst だけなので、要素が1つのタプルになり、末尾にカンマが付いた形で表示される。その中身は def の行で1回だけ作られたリストそのもので、3回の呼び出しで append された結果がそのまま残るため ([1, 2, 3],) になる。1度も呼び出していない状態なら ([],) だが、呼び出しの影響がここに現れるのがミュータブルなデフォルト引数の怖さである。__defaults__ 自体はタプルなので角括弧では表示されず、中身のリストがタプルに変わることもない。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問5|既定値の評価

デフォルト値に変数を書いたあとで、その変数を書き換えた。表示される2行はどれか。

i = 5 def h(x=i):    return x i = 10print(h())print(h(i))
  1. 5 のあとに 10
  2. 10 のあとに 10
  3. 5 のあとに 5
  4. 10 のあとに 5
正解と解説
正解:A. 5 のあとに 10

デフォルト値の式は、関数を呼ぶたびに評価されるのではなく、def の行が実行された時点で1回だけ評価される。その時点の i は 5 なので、5 が関数オブジェクトに保存される。あとから i を 10 に書き換えても、保存済みのデフォルト値は変わらないので h() は 5 を返す。一方 h(i) は呼び出しの時点で i を読んで渡すため、そのときの値である 10 が返る。デフォルト値が呼び出しのたびに評価されると考えると2行とも 10 になり、引数で渡した値もデフォルト値に置き換わると考えると2行とも 5 になる。ミュータブルなデフォルト引数の罠も、この同じ規則から生じている。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問6|引数の並び順

次のコードをファイルに保存して python3 で実行した。何が起こるか。

def g(a=1, b):    return a + b print(g(2, 3))
  1. 呼び出しの時点で TypeError となり、そこで実行が止まる
  2. 5 が表示され、そのまま最後まで正常に実行を終える
  3. 定義の時点で SyntaxError となり、1行も実行されない
  4. 4 が表示され、そのまま最後まで正常に実行を終える
正解と解説
正解:C. 定義の時点で SyntaxError となり、1行も実行されない

デフォルト値を持つ仮引数のうしろに、デフォルト値を持たない仮引数を置くことはできない。この規則は構文の段階で検査されるため、ファイルを実行するとまず SyntaxError となり、non-default argument follows default argument というメッセージが出る。構文解析はファイル全体に対して実行前に行われるので、print の行も含めて1行も実行されない。呼び出し方の誤りで起こる TypeError とは、検出される段階がまったく違う。g(2, 3) 自体は仮引数の数と合っているので、定義が正しければ 5 が表示されるはずだが、そこまで到達しない。正しく書くなら def g(b, a=1) のように、デフォルト値を持つものを後ろへ回す。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問7|関数の戻り値

return の有無が異なる2つの関数を、次のように呼び出した。表示される内容はどれか。

def one():    pass def two():    return 1, 2 print(one(), type(two()).__name__)
  1. () list
  2. None tuple
  3. None list
  4. () tuple
正解と解説
正解:B. None tuple

one は return を1度も通らずに終わるので、戻り値は None になる。空のタプルや空文字列が返るわけではない。two は return のうしろにカンマ区切りで 1 と 2 を並べているが、これは2つの値を返しているのではなく、(1, 2) というタプル1つを返している。したがって type(two()).__name__ は tuple になる。複数の戻り値がリストにまとめられると考えると list になるが、カンマで並べた式が作るのはタプルである。表示している関数の戻り値を代入して使おうとしてつまずくのは、この None が原因であることが多い。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問8|アンパック呼出

リストの前に星印を付けて呼び出す次のコードについて、表示される内容はどれか。

def f(a, b=2, *args, **kwargs):    return (a, b, args, kwargs) vals = [1, 2, 3]print(f(*vals))
  1. ([1, 2, 3], 2, (), {})
  2. (1, 2, [3], {})
  3. (1, 2, (3,), {})
  4. (1, 2, (3,), None)
正解と解説
正解:C. (1, 2, (3,), {})

呼び出し側でリストの前に星1つを付けると、その中身が位置引数として1つずつ配られる。したがって a に 1、b に 2 が入り、余った 3 が星1つの args にタプルとしてまとめられて (3,) になる。要素が1つのタプルなので末尾にカンマが付く。キーワード引数は渡していないので kwargs は空の辞書 {} であり、None にはならない。星を付け忘れて f(vals) と書くと、リストそのものが最初の仮引数に入って ([1, 2, 3], 2, (), {}) になり、エラーにならないまま結果だけが変わってしまう。args に入るのはタプルなので角括弧で表示されることもない。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問9|型注釈の扱い

注釈に書いた型とは異なる値で次の関数を呼び出した。何が起こるか。

def ann(a: int, b: str = "x") -> bool:    return a print(ann("文字列", 999))print(ann.__annotations__['a'])
  1. 1行目で TypeError となり、注釈と違う型は渡せないと知らされる
  2. 1行目で ValueError となり、int に変換できないと知らされる
  3. 1行目に 文字列 と出て、2行目に int と出る
  4. 1行目に 文字列 と出て、2行目に <class 'int'> と出る
正解と解説
正解:D. 1行目に 文字列 と出て、2行目に <class 'int'> と出る

アノテーションは関数オブジェクトの __annotations__ という辞書に記録されるだけで、実行時に型が検査されることはない。a に int と書いてあっても文字列を渡せるし、b に str と書いてあっても整数を渡せる。戻り値に bool と書いてあっても、実際に返っているのは受け取った文字列そのものである。したがって1行目には 文字列 がそのまま表示される。2行目の __annotations__['a'] に入っているのは int という型オブジェクトなので、print すると <class 'int'> という形で表示される。int という3文字だけが出るわけではない。型を実際に検査したいときは、別の検査ツールを使うか、本体で自分で確かめる必要がある。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)

問10|既定値の直し方

デフォルト値を None にして書き直した次のコードについて、表示される3行はどれか。

def add2(x, lst=None):    if lst is None:        lst = []    lst.append(x)    return lst print(add2(1))print(add2(2))print(add2(3, [9]))
  1. 上から順に [1] / [1, 2] / [9, 3] と表示される
  2. 上から順に [1] / [2] / [3] と表示される
  3. 上から順に [1] / [2] / [9, 3] と表示される
  4. 上から順に [1] / [2] / [3, 9] と表示される
正解と解説
正解:C. 上から順に [1] / [2] / [9, 3] と表示される

デフォルト値を None にしておき、本体の先頭で None かどうかを確かめて新しい空リストを作るのが定石である。None は変更できない値なので、1回しか評価されなくても共有の問題は起こらない。lst を省略した1回目と2回目は、そのつど別の空リストが作られるため [1] と [2] になり、前の呼び出しの要素は残らない。3回目は呼び出し側が [9] を渡しているので lst is None が偽になり、渡されたリストの末尾へ 3 が足されて [9, 3] になる。書き直す前の lst=[] のままだと、省略した2回目に前回の要素が残って [1, 2] になる。呼び出し側が渡したリストを無視して新しいリストを作ってしまうと [1] / [2] / [3] になり、この定石の利点である「渡されたときはそれを使う」が失われる。末尾に足す append ではなく先頭に入れる insert(0, x) にすると、3回目が [3, 9] になる。

根拠:Python Software Foundation「Python チュートリアル 4. その他の制御フローツール(関数の定義)」Python 3.14 ドキュメント(2026年8月確認)/一般社団法人Pythonエンジニア育成推進協会「Python 3 エンジニア認定基礎試験 出題範囲」(2026年8月確認)

問11|名前の探索順

Python が変数名を探す LEGB の順序として正しいものはどれか。

  1. 組み込み、モジュール最上位、外側の関数、実行中の関数 の順に探す
  2. 実行中の関数、モジュール最上位、外側の関数、組み込み の順に探す
  3. 実行中の関数、外側の関数、モジュール最上位、組み込み の順に探す
  4. モジュール最上位、組み込み、実行中の関数、外側の関数 の順に探す
正解と解説
正解:C. 実行中の関数、外側の関数、モジュール最上位、組み込み の順に探す

LEGB は Local、Enclosing、Global、Built-in の頭文字で、いちばん内側から外側へ向かって探すことを表している。まず実行中の関数の中を見て、次に自分を囲んでいる外側の関数の中、次にモジュールの最上位、最後に len や print などの組み込みを見る。見つかった時点で探索は終わるので、内側で同じ名前を使えば外側の名前は隠れる。外側から内側へ探すという順序にすると、関数の中で作ったローカル変数がモジュール最上位の同名の変数に負けることになり、実際の挙動と合わない。Global と Enclosing の順番を入れ替える誤りも多いが、外側の関数のほうがモジュール最上位より内側にある。

根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)

問12|ローカル代入

関数の中で外側と同じ名前に代入した次のコードについて、表示される2行はどれか。

x = 'global' def f():    x = 'local'    print(x) f()print(x)
  1. local のあとに global
  2. global のあとに global
  3. local のあとに local
  4. global のあとに local
正解と解説
正解:A. local のあとに global

関数の中で名前に代入すると、その名前は新しいローカル変数として作られ、モジュール最上位の同名の変数はまったく影響を受けない。関数の中の print(x) はローカルの x を読むので local と出るが、関数を抜けたあとの print(x) はモジュール最上位の x を読むので global と出る。関数内の代入が外まで届くと考えると2行とも local になるが、それには関数の先頭で global x と宣言する必要がある。逆に関数内の代入が無視されて外の値が読まれると考えると2行とも global になるが、代入した以上ローカルは確かに作られている。この「読むのは外まで届くが、書くのは届かない」という非対称が Python のスコープの基本である。

根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)

問13|nonlocal

内側の関数から外側の変数を書き換えようとする次のコードで、表示される内容はどれか。

def outer():    n = 1    def inner():        nonlocal n        n = 2    inner()    return n def outer2():    n = 1    def inner():        n = 2    inner()    return n print(outer(), outer2())
  1. 1 1
  2. 2 1
  3. 2 2
  4. 1 2
正解と解説
正解:B. 2 1

outer の inner には nonlocal n と書いてあるので、n への代入はひとつ外側の関数 outer の n に届く。inner を呼んだあと outer が返す n は 2 になる。outer2 の inner には宣言が無いので、n = 2 は inner のローカル変数を新しく作るだけで終わり、outer2 の n は 1 のまま変わらない。したがって 2 1 が表示される。両方に届くと考えると 2 2、両方に届かないと考えると 1 1 になるが、違いを生んでいるのは nonlocal の1行だけである。なお nonlocal は外側の関数に同名の変数が存在していないと使えず、モジュール最上位の変数を指すこともできない。最上位の変数を書き換えたいときは global を使う。

根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)

問14|未束縛エラー

後ろの行に代入がある名前を、その前の行で読んだ。次のコードを実行すると何が起こるか。

z = 100 def bad():    print(z)    z = 1 bad()
  1. 100 が表示され、そのあと最後まで正常に実行を終える
  2. 1 が表示され、そのあと最後まで正常に実行を終える
  3. None が表示され、そのあと最後まで正常に実行を終える
  4. UnboundLocalError となり、何も表示されずに止まる
正解と解説
正解:D. UnboundLocalError となり、何も表示されずに止まる

Python は関数の本体をコンパイルする時点で、その関数の中に代入がある名前をすべてローカル変数だと決めてしまう。判定は関数全体に対して行われ、行の順番は関係ない。この bad では後ろの行に z = 1 という代入があるため、z はこの関数のローカル変数として扱われる。すると print(z) の時点ではまだ値が入っていないので UnboundLocalError となる。この例外のメッセージの文面は Python の版によって変わるが、型名が UnboundLocalError であることは変わらない。最上位の z = 100 があっても参照されない。代入より前の行だから外側が読まれると考えると 100 になるが、Python はそう動かない。値の入っていないローカル変数が None になることもない。

根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)

問15|global宣言

global 宣言のある関数と無い関数を並べた次のコードで、表示される内容はどれか。

n = 10 def f():    global n    n = 20    return n def g():    n = 30    return n print(f(), g(), n)
  1. 10 30 10
  2. 20 30 30
  3. 20 30 20
  4. 20 30 10
正解と解説
正解:C. 20 30 20

f には global n と書いてあるので、n = 20 はモジュール最上位の n を書き換える。f 自身は 20 を返し、最上位の n も 20 になる。g には宣言が無いので、n = 30 は g のローカル変数を作るだけで、最上位の n には影響しない。g は自分のローカルの 30 を返す。最後の print が読む n は最上位のもので、f が書き換えたあとなので 20 である。g の代入まで外に届くと考えると 20 30 30 になり、global が効かないと考えると 20 30 10 になる。global 宣言は「この名前への代入を最上位へ届ける」という指示であって、値を読むだけなら宣言は要らない点にも注意する。

根拠:Python Software Foundation「Python チュートリアル 9. クラス(Python のスコープと名前空間)」Python 3.14 ドキュメント(2026年8月確認)

問16|__all__の効果

次の mymod.py を用意し、別のファイルで from mymod import * と書いた。呼び出し側に持ち込まれる名前はどれか。

# mymod.py__all__ = ['pub']pub = 'P'other = 'O' def hello():    return 'H'
  1. pub と other と hello の3つがすべて持ち込まれる
  2. pub と hello が持ち込まれ、other は持ち込まれない
  3. pub だけが持ち込まれ、other と hello は持ち込まれない
  4. other と hello が持ち込まれ、pub は持ち込まれない
正解と解説
正解:C. pub だけが持ち込まれ、other と hello は持ち込まれない

星印による取り込みは、通常なら下線で始まらない名前をすべて持ち込むが、モジュール側に __all__ という文字列のリストがあると、そこに挙げた名前だけに絞られる。この例では __all__ に pub しか書かれていないので、other も hello も持ち込まれない。other は下線で始まっていないが、__all__ がある以上その一覧が優先される。関数だから特別扱いされるということもないので、hello も同じく持ち込まれない。__all__ が効くのは星印による取り込みのときだけで、from mymod import hello のように名前を指定した取り込みや、import mymod としてから mymod.hello() と書くやり方は妨げられない。

根拠:Python Software Foundation「Python チュートリアル 6. モジュール」Python 3.14 ドキュメント(2026年8月確認)

問17|__name__の値

次の mymod2.py について、python3 mymod2.py で直接実行した場合と、別のファイルから import mymod2 した場合の __name__ の値の組み合わせはどれか。

# mymod2.pyprint('mymod2:', __name__) if __name__ == '__main__':    print('直接実行')
  1. 直接実行では mymod2、import では __main__ になる
  2. 直接実行では __main__、import では mymod2 になる
  3. 直接実行でも import でも、どちらも __main__ になる
  4. 直接実行でも import でも、どちらも mymod2 になる
正解と解説
正解:B. 直接実行では __main__、import では mymod2 になる

そのファイルを python3 で直接実行したときは、__name__ に '__main__' が入る。他のファイルから import されたときは、拡張子を除いたファイル名である 'mymod2' が入る。したがって直接実行では mymod2: __main__ と 直接実行 の2行が出て、import されたときは mymod2: mymod2 の1行だけが出る。if __name__ == '__main__': は、この違いを利用して「直接実行したときだけ動かしたい処理」を切り分ける定番の書き方である。組み合わせを逆にすると、import したときに動作確認用の処理が動いてしまうことになり、この書き方の目的と合わない。どちらの場合も同じ値になるのであれば、そもそも切り分けができない。

根拠:Python Software Foundation「Python チュートリアル 6. モジュール」Python 3.14 ドキュメント(2026年8月確認)

問18|sys.argv

次の args.py を python3 args.py foo bar というコマンドで実行したとき、表示される2行はどれか。

# args.pyimport sys print(len(sys.argv))print(sys.argv[1:])
  1. 2 のあとに ['foo', 'bar']
  2. 3 のあとに ['args.py', 'foo']
  3. 2 のあとに ['args.py', 'foo']
  4. 3 のあとに ['foo', 'bar']
正解と解説
正解:D. 3 のあとに ['foo', 'bar']

sys.argv はコマンドラインから渡された値を並べたリストで、その先頭にはスクリプト名そのものが入る。この例では ['args.py', 'foo', 'bar'] のような3要素のリストになるので、len は 3 である。添字1以降を切り出した sys.argv[1:] は、スクリプト名を除いた ['foo', 'bar'] になる。渡した引数の個数がそのまま長さになると考えると 2 になるが、先頭のスクリプト名を数え忘れている。sys.argv[1:] が ['args.py', 'foo'] になると考えるのは、スライスの開始位置を取り違えた場合である。引数を1つも渡さずに実行すると長さは 1 で、sys.argv[1:] は空のリストになる。

根拠:Python Software Foundation「Python 標準ライブラリ sys --- システム固有のパラメーターと関数」Python 3.14 ドキュメント(2026年8月確認)

問19|モジュール再読込

1つのプログラムの中で同じモジュールを二度 import したときの挙動として正しいものはどれか。

  1. 二度目は別のオブジェクトが作られ、状態は共有されなくなる
  2. 二度目はキャッシュが使われ、モジュール本体は再実行されない
  3. 二度目もモジュール本体が最初から読み直され、再度実行される
  4. 二度目は ImportError となり、同じ名前は一度しか使えない
正解と解説
正解:B. 二度目はキャッシュが使われ、モジュール本体は再実行されない

一度読み込んだモジュールは sys.modules という辞書にキャッシュされる。二度目の import は、そこに登録されている同じモジュールオブジェクトを名前に結び付け直すだけなので、ファイルの読み直しも本体の再実行も起こらない。モジュールの最上位に print を書いておくと、二度 import しても1回しか表示されないことで確かめられる。別名を付けて import しても、指しているのは同じオブジェクトである。したがって、あるモジュールの変数を書き換えれば、そのモジュールを import している他の場所からも書き換わったあとの値が見える。同じ名前を二度 import してもエラーにはならず、開発中に本体を読み直したい場合は importlib の機能を明示的に使う必要がある。

根拠:Python Software Foundation「Python チュートリアル 6. モジュール」Python 3.14 ドキュメント(2026年8月確認)

問20|仮想環境の判定

いま動いている Python が venv で作った仮想環境の中かどうかを判定する方法として適切なものはどれか。

  1. sys.base_prefix が定義されていれば、仮想環境の中だと判定できる
  2. sys.prefix が定義されていなければ、仮想環境の中だと判定できる
  3. sys.prefix と sys.base_prefix が一致していれば、仮想環境の中だと判定できる
  4. sys.prefix と sys.base_prefix が食い違っていれば、仮想環境の中だと判定できる
正解と解説
正解:D. sys.prefix と sys.base_prefix が食い違っていれば、仮想環境の中だと判定できる

sys.prefix はいま動いている Python がインストールされている場所を指し、sys.base_prefix はその元になった Python の場所を指す。仮想環境の外では両方が同じ値になるが、python3 -m venv で作った環境の中では sys.prefix が作成した環境のディレクトリを指すようになり、sys.base_prefix は元の Python を指したままなので、2つが食い違う。この食い違いの有無が判定の手がかりになる。一致していれば仮想環境の中というのは逆の条件である。sys.base_prefix は仮想環境の外でも常に定義されているので、定義の有無では判定できない。sys.prefix も常に定義されている。具体的なパス文字列や pip の版は環境ごとに違うので、覚えるのはこの関係だけでよい。

根拠:Python Software Foundation「Python チュートリアル 12. 仮想環境とパッケージ」Python 3.14 ドキュメント(2026年8月確認)/Python Software Foundation「Python 標準ライブラリ sys --- システム固有のパラメーターと関数」Python 3.14 ドキュメント(2026年8月確認)

問21|writeの戻り値

書き込みメソッドの戻り値を受け取る次のコードについて、表示される内容はどれか。

with open('/tmp/sample.txt', 'w') as f:    n = f.write("one\ntwo\nthree\n") print(n)
  1. 14 と表示される
  2. 11 と表示される
  3. 3 と表示される
  4. None と表示される
正解と解説
正解:A. 14 と表示される

write は書き込んだ文字数を戻り値として返す。"one\ntwo\nthree\n" は one が3文字、two が3文字、three が5文字で合わせて11文字、これに改行が3つ付くので合計14文字になる。改行を数え忘れると 11 になる。書き込んだ行数が返ると考えると 3 になるが、write が数えているのはあくまで文字数である。何も返さないと考えて None を選ぶのもよくある誤りで、append や sort のように None を返すメソッドと混同しやすいが、write は数を返す。なお write は改行を自動では付けないので、行を区切りたいときは文字列の中に自分で書く必要がある。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問22|readlines

3行が書かれたファイルを readlines で読んだ。表示される内容はどれか。

with open('/tmp/sample.txt', 'w') as f:    f.write("one\ntwo\nthree\n") with open('/tmp/sample.txt') as f:    print(f.readlines())
  1. ['one', 'two', 'three'] と表示される
  2. ['one\ntwo\nthree\n'] と表示される
  3. 'one\ntwo\nthree\n' と表示される
  4. ['one\n', 'two\n', 'three\n'] と表示される
正解と解説
正解:D. ['one\n', 'two\n', 'three\n'] と表示される

readlines は全行をリストにして返すが、各要素の行末にある改行文字はそのまま残る。したがって ['one\n', 'two\n', 'three\n'] になる。改行が取り除かれると考えると ['one', 'two', 'three'] になるが、それを得たいときは各要素に rstrip を適用する必要がある。区切らずに1つの文字列として返すのは read であり、readlines はリストを返す。改行を区切りとして扱わず、ファイル全体を1要素として包んだだけのリストになると考えると ['one\ntwo\nthree\n'] になるが、readlines は改行のたびに要素を分ける。for line in f と書いて1行ずつ取り出したときも、渡ってくる行には同じく改行が付いている。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問23|二度目のread

同じファイルオブジェクトから続けて2回読んだ。表示される内容はどれか。

with open('/tmp/sample.txt') as f:    a = f.read()    b = f.read() print(len(a), b == '')
  1. 14 True と表示される
  2. 14 False と表示される
  3. 0 True と表示される
  4. 0 False と表示される
正解と解説
正解:A. 14 True と表示される

ファイルオブジェクトは現在位置を覚えている。最初の read で先頭から末尾まで読み切るので、a には改行を含む14文字が入り、位置は末尾へ移る。2度目の read は末尾から読もうとするため、残りが無く空文字列が返る。したがって len(a) は 14 で、b == '' は True になる。2度目も同じ内容が返ると考えると b == '' が False になるが、同じ内容を読み直すには f.seek(0) で位置を先頭に戻す必要がある。len(a) が 0 になるのは、ファイルが空だった場合か、すでに位置が末尾にあった場合である。何も表示されないのを見て読み込みに失敗したと誤解しやすい場面だが、原因は位置が末尾にあることである。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問24|withとclose

with のブロックの中と外で closed を確かめた。表示される2行はどれか。

with open('/tmp/sample.txt') as f:    print(f.closed) print(f.closed)
  1. True のあとに True
  2. False のあとに False
  3. False のあとに True
  4. True のあとに False
正解と解説
正解:C. False のあとに True

with のブロックの中ではファイルはまだ開いているので、closed は False である。ブロックを抜けるときに自動的に閉じられるため、そのあとの closed は True になる。したがって False のあとに True が表示される。with を使う目的は、この閉じる処理を書き忘れても必ず実行されるようにすることで、ブロックの中で例外が起きて途中で抜けた場合でも閉じられる。カンマで区切れば複数のファイルを1つの with 文でまとめて開くこともでき、その場合もどれも同じように閉じられる。閉じたあとのファイルオブジェクトから読もうとすると ValueError になるので、with の外で読み直すことはできない。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問25|書き込みモード

同じファイルを3回開いて書き込んだあと読み出した。表示される内容はどれか。

p = '/tmp/sample.txt' with open(p, 'w') as f:    f.write('AAA')with open(p, 'a') as f:    f.write('BBB')with open(p, 'w') as f:    f.write('C') with open(p) as f:    print(f.read())
  1. ファイルの中身は AAABBBC になっている
  2. ファイルの中身は C になっている
  3. ファイルの中身は AAABBB になっている
  4. ファイルの中身は CAABBB になっている
正解と解説
正解:B. ファイルの中身は C になっている

'w' で開くと、その時点で既存の内容の有無に関わらずファイルは長さ0に切り詰められる。順に追うと、最初の 'w' で AAA が入り、次の 'a' は追記なので AAABBB になる。ところが3回目に 'w' で開いた瞬間に AAABBB は消え、そこへ C を書いたので最終的に C だけが残る。3回とも 'a' で開いたと考えると AAABBBC になり、最後の書き込みが無視されたと考えると AAABBB になる。切り詰めずに先頭から上書きするのは 'r+' の挙動で、その場合は CAABBB のように先頭の1文字だけが置き換わる。切り詰めが起きるのは開いた瞬間であって書き込んだ瞬間ではないので、'w' で開いただけで何も書かなければファイルは空になる。書き足すつもりなら 'a'、作り直すつもりなら 'w' と、意図をモードで表明する。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問26|閉じた後の操作

close したあとのファイルオブジェクトを読もうとすると、何が起こるか。

f = open('/tmp/sample.txt')f.close()print(f.read())
  1. ValueError となり、閉じたファイルへの操作だと知らされる
  2. OSError となり、ファイルを開き直せないと知らされる
  3. 空文字列が表示され、そのまま正常に実行を終える
  4. 自動で開き直され、ファイルの内容がすべて表示される
正解と解説
正解:A. ValueError となり、閉じたファイルへの操作だと知らされる

close したあとのファイルオブジェクトに対して read を呼ぶと ValueError となり、I/O operation on closed file. というメッセージが出る。閉じられたことはオブジェクト側が覚えているので、必要になったからといって自動で開き直されることはない。読むものが無いという意味で空文字列が返るわけでもなく、これは末尾まで読み切ったあとの read の挙動である。ファイルが見つからないときの FileNotFoundError などは OSError の仲間だが、閉じたファイルを操作したときの例外はそれとは別で ValueError である。with 文を使えばブロックを抜けたあとに読もうとすること自体が構造的に避けられる。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問27|読み込み専用

モードを省略して開いたファイルへ書き込もうとすると、何が起こるか。

with open('/tmp/sample.txt', 'w') as f:    f.write('data') with open('/tmp/sample.txt') as f:    f.write('more')
  1. 内容が more に置き換わり、そのまま正常に実行を終える
  2. 内容の末尾に more が足され、そのまま正常に実行を終える
  3. ValueError となり、モードの指定が正しくないと知らされる
  4. io.UnsupportedOperation となり、書き込めないと知らされる
正解と解説
正解:D. io.UnsupportedOperation となり、書き込めないと知らされる

open の第2引数を省略すると読み込み専用の 'r' になる。読み込み専用で開いたファイルオブジェクトに write を呼ぶと io.UnsupportedOperation となり、not writable というメッセージが付く。文法としては通ってしまう書き方なので、モードを取り違えたことに気づくのは実行してからになる。上書きしたいなら 'w'、末尾に足したいなら 'a'、読み書きの両方をしたいなら 'r+' で開き直す必要がある。逆に 'w' や 'a' で開いたファイルから read を呼ぶと、同じ io.UnsupportedOperation で not readable と知らされる。モードは、そのファイルオブジェクトに何を許すかを宣言している場所だといえる。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問28|seekとtell

先頭から5文字だけ読んで位置を確かめた次のコードで、表示される内容はどれか。

with open('/tmp/sample.txt') as f:    head = f.read(5)    pos = f.tell()    f.seek(0)    print(repr(head), pos, f.tell())
  1. 'one\nt' 5 5
  2. 'one\nt' 0 0
  3. 'one\nt' 5 0
  4. 'one\ntwo\nthree\n' 14 0
正解と解説
正解:C. 'one\nt' 5 0

read に数を渡すと、その文字数だけを読む。先頭から5文字は one の3文字と改行1文字、そして two の t で 'one\nt' になる。読んだぶんだけ位置が進むので、直後の tell は 5 を返す。次の seek(0) で位置を先頭に戻すため、最後の tell は 0 になる。read が位置を動かさないと考えると tell が2回とも 0 になり、seek が効かないと考えると tell が2回とも 5 になる。read の引数が文字数ではなく行数だと取り違えると、5行目までを読むつもりでファイル全体の 'one\ntwo\nthree\n' と 14 を選んでしまう。seek(0) を使えば、同じファイルオブジェクトのまま先頭から読み直せる。

根拠:Python Software Foundation「Python チュートリアル 7. 入力と出力」Python 3.14 ドキュメント(2026年8月確認)

問29|JSONとタプル

タプルを含む辞書を JSON にして読み戻す次のコードで、表示される2行はどれか。

import json s = json.dumps({'t': (1, 2)})back = json.loads(s)print(s)print(back, type(back['t']).__name__)
  1. {"t": [1, 2]} のあとに {'t': [1, 2]} list
  2. {"t": [1, 2]} のあとに {'t': (1, 2)} tuple
  3. {"t": (1, 2)} のあとに {'t': (1, 2)} tuple
  4. {"t": "(1, 2)"} のあとに {'t': '(1, 2)'} str
正解と解説
正解:A. {"t": [1, 2]} のあとに {'t': [1, 2]} list

JSON には配列しか無く、タプルという型が存在しない。そのため dumps の時点でタプルは配列に変換され、出力は {"t": [1, 2]} になる。読み戻すときも配列はリストとして復元されるので、back['t'] の型は list であり、タプルには戻らない。元がタプルだったという情報はどこにも残らないため、往復させると型が変わる。タプルのまま扱いたければ、読み戻したあとに自分で tuple() を適用する必要がある。JSON の出力に丸括弧が現れることはなく、タプルが文字列に変換されるわけでもない。同じ理由で、辞書のキーに整数を使うと dumps で文字列に変換され、読み戻しても整数には戻らない。

根拠:Python Software Foundation「Python 標準ライブラリ json --- JSON エンコーダーとデコーダー」Python 3.14 ドキュメント(2026年8月確認)

問30|JSONのキー

整数のキーを持つ辞書と集合を JSON にしようとすると、何が起こるか。

import json print(json.dumps({1: 'a'}))print(json.dumps({1, 2}))
  1. 1行目に {1: "a"} と出て、2行目に [1, 2] と出る
  2. 1行目に {"1": "a"} と出て、2行目に [1, 2] と出る
  3. 1行目で TypeError となり、2行目は実行されない
  4. 1行目に {"1": "a"} と出て、2行目で TypeError となる
正解と解説
正解:D. 1行目に {"1": "a"} と出て、2行目で TypeError となる

JSON のオブジェクトのキーは文字列でなければならないので、整数をキーにした辞書を dumps すると、キーは文字列に変換されて {"1": "a"} と出力される。これは正常に成功する処理であり、ここで例外にはならない。ただし読み戻したときのキーは文字列のままで、整数には戻らないことに注意する。2行目の集合は JSON に対応する型が無いため、dumps しようとすると TypeError となり、Object of type set is not JSON serializable と知らされる。集合が配列として書き出されると考えると [1, 2] になるが、そう変換してほしければ自分で list() を適用してから渡す必要がある。日本語を含む値については、既定のままだとエスケープされて出力され、ensure_ascii に偽を渡すとそのままの文字で出力される。

根拠:Python Software Foundation「Python 標準ライブラリ json --- JSON エンコーダーとデコーダー」Python 3.14 ドキュメント(2026年8月確認)

演習:この章の問題を解く

ランダム出題の演習ツールです(JavaScript が有効な場合に動きます)。上の「確認問題」はそのままでもすべて読めます。

※ 解説は学習用の情報提供です。最新の出題範囲・制度は必ず公式発表をご確認ください。
※ 出題は、AWS公式ドキュメント・試験ガイド、JDLA「G検定 試験出題範囲(シラバス2024)第1.4版」、一般社団法人Pythonエンジニア育成推進協会の公開情報とPython公式ドキュメントに沿った仮の宿 学習室のオリジナル問題です(2026年8月時点で確認)。Pythonの問題はすべて実行して確認しています。試験制度・出題範囲は各実施団体の公式発表をご確認ください。