38
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python 入門100本ノック

まえがき

背景

私は新入社員の研修課題として Python の練習問題を探していました。
しかし、ちょうど良い課題はなかなか見つかりません。
というのも、教育担当の立場からしても新人研修は難しいのです。

  • そもそも新人教育に使うことのできる時間が限られている
  • 新入社員が自力で進められるように、練習問題だけでなく説明も充実させる必要がある。
  • 新入社員の力量に応じて研修内容を調整したい。

ということで、一般公開可能な Python 練習問題を作成しました。
新人教育に限らず、これから Python を学ぶ方の参考になればと思っています。

対象読者

対象読者として次のような方を想定しています。

  • Python 初心者向けの研修課題を探している方
  • Python をこれから学びたい方

環境

次のような環境が使用できることを想定しています。

1. オペレーティングシステム (OS)

メッセージボックスなどのGUI要素を表示可能なデスクトップ環境が必要です。
デスクトップが使用可能であれば、Windows、macOS、Linux などの指定はありません。

2. Python

Python のバージョンは3.7 以上が必要です。
Python 3.7 より古いバージョンでは、本練習問題で紹介する Python の構文や機能の一部が使用できません。

Pythonのインストール

Python公式サイトから最新版のインストーラをダウンロードします。
インストーラを起動し、表示内容に従ってインストールを行います。
基本的にデフォルト設定のまま進めて良いと思いますが、最初に表示される画面で「Add Python to PATH」にチェックを入れることをお勧めします。

3. テキストエディタ

Python のコードを記述および編集するためのツールが必要です。
テキストエディタは何を使っても問題ありませんが、何が良いか分からない場合は Visual Studio Code (VS Code) がお勧めです。

Visual Studio Code のインストール

VS Codeのダウンロードページからインストールします。
拡張機能「Python」「Japanese Language Pack for Visual Studio Code」をインストールします。

4. Python の実行

上記の環境が整った上で、Python の実行が可能であることを前提としています。

Python スクリプトの実行方法

Python スクリプトとは、Python プログラムを記述した ".py" 形式のテキストファイルのことです。
スクリプトは、Python インタープリタ によって実行されます。

コマンドラインからPythonスクリプトを実行する場合、次のように3つのディレクトリが存在します。

ディレクトリ 説明 補足
カレントディレクトリ 現在の作業ディレクトリです。 コマンドラインでは cd コマンドで指定します。
スクリプトディレクトリ 実行する Python スクリプトファイルが存在するディレクトリです。 スクリプトを実行する際には、このディレクトリにあるスクリプトへのパスを、絶対パスまたはカレントディレクトリからの相対パスで指定します。
Python ディレクトリ Python インタープリタの実行ファイルや関連ライブラリが格納されているディレクトリです。 環境変数 PATH にこのディレクトリが設定されている場合、コマンドラインから python コマンドを実行する際に、インタープリタへのフルパスを指定する必要はありません。

ただし、ディレクトリとはフォルダとほぼ同じ意味です。
コマンドラインから操作する文脈ではフォルダのことをディレクトリと呼ぶことが多いです。

このとき、Python を実行するにはコマンドラインで次のようなコマンドを実行します。

cd "作業ディレクトリ"
"Python インタープリタの実行ファイルのパス" "Python スクリプトファイルのパス"

このコマンドを簡単にするため、以下の条件でコマンドを作成します。

(1) Python スクリプトファイルのファイル名は "main.py" にする。
(2) 作業ディレクトリはスクリプトディレクトリと同じディレクトリを使用する。
(3) 環境変数 PATH にPython ディレクトリが設定されている。
(4) Python インタープリタの実行ファイルは "python.exe" である。

この条件であれば、Python を実行するコマンドは次のようになります。

cd "スクリプトディレクトリのパス"
python main.py

例えば、もし Python スクリプトファイルを "C:\Users\ユーザー名\Desktop\practice" に格納する場合、次のように実行します。

cd "C:\Users\ユーザー名\Desktop\practice"
python main.py

このパス "C:\Users\ユーザー名\Desktop\practice" はサンプルです。
ご自身の環境に合わせてスクリプトを格納し、そのパスを指定してください。

また、もし (3) や (4) の条件を満たしているか分からない場合、次のコマンドを実行してください。

python -V

もし次のように Python のバージョンが表示された場合、条件 (3) と (4) は満たしています。

Python 3.xx.xx

逆に、もしバージョンが表示されない場合は、エラーメッセージが表示されるはずです。
エラーの表示の内容は環境に応じて異なりますが、次のようなエラーメッセージのイメージです。

'python' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチファイルとして認識されていません。

もしバージョンが表示されずエラーとなる場合は、次のように python3 で実行してみてください。

python3 -V

これでバージョンが表示される場合、Python の実行コマンドも同様に python3 を指定します。

cd "C:\Users\ユーザー名\Desktop\practice"
python3 main.py

最後に、Python 実行における全体像を図示すると次のようになります。
この図は、コマンドがPythonインタープリタを起動し、Pythonがスクリプトを実行する流れを示しています。

ただし、環境によってはコマンドラインを使わずにPythonを実行するなど、上記の構成を実現できないこともあります。
このような場合、一部の練習問題が取り組みにくくなる可能性がありますので注意してください。
必要に応じて一部の問題をスキップするなど、ご自身の環境に合わせて調整してください。

構成

1. 前半問題50問
Python の基本的な構文を確認・解説する練習問題です。

2. 実践問題10問
基本的な構文を使って Python プログラミングを行う練習問題です。

3. 後半問題30問
Python の基本的な構文を確認・解説する練習問題です。前半問題より実践的な構文が登場します。

4. 実践問題10問
基本的な構文を使って Python プログラミングを行う練習問題です。
前半の実践問題より難易度が高くなっています。
また、解説文では発展的な内容について説明しています。

進め方

ここでは私が想定する、Python に関する事前知識がない場合の進め方を記載します。
もし Python に関して事前に学習済みの場合や、会社・先輩の指示がある場合は、この節を無視して各自の進め方を実施してください。

Python に関する事前知識がない場合の学び方

まず、事前知識がない状態で問題を解くことはお勧めしません。
悩むだけで時間が過ぎてしまい、問題を解けない可能性が高いです。
最初は知識を学ぶことを優先し、解説を読む感覚で進めて良いと思います。
特に、回答例のコードや解説中のサンプルコードを動かしたり、自分でコードを書き換えてみたりして Python に慣れることを推奨します。

一通り解説を読んだ後、もう一度最初から問題を解いていきましょう。
事前に解説を読んでいても、それを再現するのは苦労するはずです。
例えば、Python でエラーが発生する例には次のようなものがあります。

  • インデント(行の先頭の空白の数)が誤っている場合
  • "." と "," を間違って入力している場合
  • 変数名や関数名が誤っている場合

作成したコードを実行してエラーが発生した場合に、そのエラーへの対処方法も学ぶべき対象と考えます。
エラーが発生した場合には、基本的に次のようなエラーメッセージが表示されます。

  • どの行でエラーが発生したのか
  • どのようなエラーが発生したのか

エラーメッセージを元にコードを見直し、エラーを解決する方法を検討してください。
エラーが発生した行にインデントの誤りやタイプミスがあるかもしれません。
また、もしエラーメッセージに見覚えがあれば、過去にも同じようなミスが発生していた可能性があります。

エラーには種類に応じて名前がついており、それだけでも原因に検討を付けることができます。
よく見かけるエラーは次のようなものがあるので、エラーに対処する際に少しずつ覚えていくと良いでしょう。

エラー名 説明 対処方法
SyntaxError 文法が間違っている場合に発生 コードの構文誤りを確認(:() のミスなど)
IndentationError インデントが適切でない場合に発生 スペースやタブを確認
TypeError 型が不適切な場合に発生 型を確認(整数と文字列の混在など)
ValueError 値が不適切な場合に発生 関数に渡す値を確認
IndexError リストやタプルの範囲外のインデックスを参照した場合に発生 リストの長さを確認
KeyError 辞書に存在しないキーを参照した場合に発生 キーが存在するか確認
AttributeError オブジェクトに存在しない属性やメソッドを参照した場合に発生 変数の型を確認
NameError 定義されていない変数を使用しようとした場合に発生 変数名のスペルを確認
ModuleNotFoundError インポートしたモジュールが見つからない場合に発生 モジュール名に間違いがないか確認を追加
ZeroDivisionError 0で除算した場合に発生 ゼロで割っていないかチェック
FileNotFoundError 指定したファイルが存在しない場合に発生 ファイル名とパスを確認
PermissionError ファイルやディレクトリにアクセスする権限がない場合に発生 権限の確認
Python に関する事前知識がない場合の進め方

この Python 入門100本ノック は次のように解説用の問題と実践用の問題に分かれています。

  • 基本的な構文を 確認・解説 する練習問題
  • 基本的な構文を使って Python プログラミングを 実践 する練習問題

そこで、私が想定する Python に関する事前知識がない場合の本練習問題の進め方は次のようになります。

  1. 「1. 前半問題50問」の内容を学ぶ。
    1-1. 回答例と解説を読む。
    1-2. 記載されている回答例やサンプルコードを動作させる。
    1-3. 記載されている回答例やサンプルコード参考に、自分で変更したものを動作させる。

  2. 「1. 前半問題50問」の回答や解説を見ない状態で、コードを作成・実行する。
    2-1. 問題文に対して自分で作成したコードを実行し、期待通りに動作することを確認する。
    2-2. 期待通りに動作しない場合は、その原因の調査や改善に取り組む。
    2-2. 必要に応じて回答例や解説を読み直す。

  3. 「2. 実践問題10問」の内容を解く。
    3-1. 問題文に対して自分で作成したコードを実行し、期待通りに動作することを確認する。
    3-2. 期待通りに動作しない場合は、その原因の調査や改善に取り組み、必要に応じて回答例や解説を読む。

  4. 「3. 後半問題30問」の内容を進める。
    4-1. 「1. 前半問題50問」と同様に、サンプルコードを動作しながら、解説の内容を学ぶ。
    4-2. 「1. 前半問題50問」と同様に、回答や解説を見ない状態で、コードを作成・実行する。

  5. 「4. 実践問題10問」の内容を解く。
    5-1. 「2. 実践問題10問」と同様に、コードの作成・実行と不具合発生時の調査・改善を行う。

1.前半問題50問

基本的な構文

問題 1 出力(基本)

print 関数を使って "Hello, World!" を出力してください。

回答例
print('Hello, World!')
解説

print 関数は引数に渡したデータを表示します。
文字列の場合、シングルクォーテーション ' とダブルクォーテーション " のどちらも使用できます。例えば、次のコードはどちらも正しいです。

print('Hello, World!')
print("Hello, World!")

しかし、クォーテーションを付けていない場合はエラーとなります。

print(Hello, World!)

一方で数値の場合、クォーテーション '" を付けずに print 関数に渡すことができます。

print(100)

文字列や数値は「データ型」や「型」などと呼ばれ、プログラミングにおける重要な概念です。
文字列として扱うか数値として扱うかによって、記述の仕方が異なります。
例えば、100 を文字列として扱うことも可能です。

print('100')

問題 2 出力(複数行)

print 関数を複数回使って、123 を順番に出力してください。

回答例
print(1)
print(2)
print(3)
解説

Python のコードは上から順番に一行ずつ処理されていきます。
例えば、回答例のコードで print(2)print(1) より先に実行されることはありません。
そのため、123 は必ずこの順序で出力されることになります。

問題 3 出力(複数項目)

print 関数を使って、数値 123 と文字列 "Hello" を一行に並べて出力してください。

回答例
print(123, 'Hello')
解説

print 関数は複数の引数を受け取ることができ、カンマで区切ることで複数の値を出力できます。
回答例のように、数値と文字列を並べて扱うことも可能です。

問題 4 コメント

一行コメントを使って、数値 2 の出力を行わないように次のコードを変更してください。

print(1)
print(2)
print(3)
回答例
print(1)
# print(2)
print(3)
解説

記号 # の後に記載された部分はコメントと呼ばれ、プログラムの実行には影響しません。
コメントは処理を行わないように変更するだけでなく、次のように処理内容の説明やメモを書き込むためにも使用されます。

# print 関数のテスト
print('a')

問題 5 複数行コメント

次のコードを変更し、出力を行わないコメントに修正してください。

print(1)
print(2)
print(3)
回答例
"""
print(1)
print(2)
print(3)
"""
解説

トリプルクォート """''' で囲まれた部分は、改行を含んでいても1つの文字列として認識されます。
これを利用して複数行のコメントとして利用することができます。
ただし、厳密にはコメントの機能ではないため、次のような限定的な範囲で使用することが一般的です。

  • 開発中の一時的なコメントアウト
  • 改行を含む文字列の定義
  • Docstring と呼ばれるプログラムの説明文の記述

変数とデータ型

問題 6 変数の宣言と使用

変数 x に数値 10 を代入し、その値を print 関数で出力してください。

回答例
x = 10
print(x)
解説

変数に値を代入するには、代入演算子 = を使用します。
代入された値は以後の処理でも利用可能です。
例えば、回答例では print 関数を使ってその値を出力しています。

問題 7 数値の加算

数値 12 を足し算した結果を変数 x に代入し、その値を print 関数で出力してください。

回答例
x = 1 + 2
print(x)
解説

数値同士に+ 演算子を適用すると、数値の加算を行います。
今回の例では、数値 12 を足し算した結果である 3 を取得できます。
一方で、文字列を連結させた "12" を得るには別の処理が必要になります。
これについては次の問題で扱います。

問題 8 文字列の加算

文字列の "1" と "2" を結合した結果を変数 x に代入し、その値を print 関数で出力してください。

回答例
x = '1' + '2'
print(x)
解説

文字列同士に+ 演算子を適用すると、文字列の連結を行います。
今回の例では、文字列 '1' と '2' を文字列を連結させた '12' を取得できます。
一方で、数値を足し算した 3 を得るには別の処理が必要になります。

問題 9 データ型の変換

整数 10 を文字列に変換して print 関数で出力してください。さらに、文字列 '10' を整数に変換して print 関数で出力してください。

回答例
print(str(10))
print(int('10'))
解説

str() 関数は入力値を文字列型に変換し、int() 関数は入力値を数値型に変換します。
例えば、数値 12 を文字列として連結させて "12" を取得するには次のように記述します。

a = 1
b = 2
x = str(a) + str(b)
print(x)

問題 10 エスケープシーケンス

改行を意味する特殊な文字列 \n を含む文字列 "1\n2\n3" をprint 関数で出力し、改行されて表示されることを確認してください。

回答例
print('1\n2\n3')
解説

回答例のように特殊な文字列 \n を含む文字列を print 関数で出力すると自動的に改行されます。

1
2
3

このような特殊な文字列はエスケープシーケンスと呼ばれ、次のようなものがあります。

エスケープシーケンス 説明
\r キャリッジリターン
\n 改行(New Line)
\t タブ(Tab)
\b バックスペース
\f フォームフィード
\v 垂直タブ
\\ バックスラッシュ(\)
\' シングルクォート(')
\" ダブルクォート(")
\xhh 16進数(Hex)表記の文字、hh は 2 桁の 16 進数コード
\ooo 8進数(Octal)表記の文字、ooo は最大 3 桁の 8 進数コード
\uXXXX Unicode(16bit)表記の文字、XXXX は 4 桁の Unicode(16bit)
\UXXXXXXXX Unicode(32bit)表記の文字、XXXXXXXX は 8 桁の Unicode(32bit)

これらは、改行やタブといった特殊な文字を文字列内で表現するために使用されます。、
まずは意図せず変換されてしまわないように注意すると良いでしょう。

# エスケープシーケンスの出力例
print("キャリッジリターン: Hello\rWorld")  # \r → 行頭に戻る
print("改行: Hello\nWorld")  # \n → 改行
print("タブ: Hello\tWorld")  # \t → タブ
print("バックスペース: Hello\bWorld")  # \b → 1文字削除
print("フォームフィード: Hello\fWorld")  # \f → 一部の環境でページ送り
print("垂直タブ: Hello\vWorld")  # \v → 垂直タブ(環境によって表示が異なる)
print("バックスラッシュ: C:\\test")  # \\ → バックスラッシュそのまま表示
print("シングルクォート: It\'s Python")  # \' → シングルクォート
print("ダブルクォート: She said \"Hello\"")  # \" → ダブルクォート
print("16進数コード: \x41\x42\x43")  # \xhh → A B C
print("8進数コード: \101")  # \101 は 'A'(8進数)
print("Unicode: \u03A9")  # \uXXXX → Ω(ギリシャ文字オメガ)
print("Unicode絵文字: \U0001F600")  # \UXXXXXXXX → スマイル絵文字

問題 11 エスケープシーケンスのエスケープ

タブ記号を意味するエスケープシーケンス \t をエスケープして、文字列 "C:\test" を表示する print 関数を実行してください。

回答例
print('C:\\test')
解説

次のように "C:\test" をそのまま出力すると、\t がタブとして動作してしまい、期待通りに表示されません。

print("C:\test")

回答例のようにエスケープシーケンス \\ を使用することで、文字列内に \ を文字として含めることができます。
このように、エスケープシーケンスを通常の文字として扱うための操作を エスケープ と呼びます。
一部の文字列に特殊な意味を持たせるエスケープシーケンスに対し、その特殊な動作を防ぐのがエスケープです。

問題 12 r文字列の使用

r文字列と print 関数を使って "C:\test" を出力してください。

回答例
print(r'C:\test')
解説

r文字列を使うことでエスケープシーケンスを無効化し、記載した文字をそのままの形で扱うことができます。
ただし、文字列の末尾に \ 記号がある場合はエラーになることに注意が必要です。

print(r"C:\test\")  #  エラーになる

r文字列 という言葉の由来は raw string であり、未加工の文字列を意味します。

問題 13 f文字列の使用

文字列変数 name に "Python" を代入し、f文字列と print 関数を使って "Hello, Python!" を出力してください。

回答例
name = 'Python'
print(f'Hello, {name}!')
解説

f文字列を使って波括弧 {} 内に変数名を書くことで、文字列の中に変数の値を埋め込むことができます。
変数は数値を指定することも可能です。例えば、次のように数値 12 を文字列として連結させて "12" を取得することもできます。

a = 1
b = 2
x = f'{a}{b}'  # 文字列 "12" が x に代入される
print(x)

f文字列 という言葉の由来は formatted string literals であり、フォーマット済み文字列リテラルを意味します。

問題 14 文字列の長さ

文字列 "Python" の長さ(文字数)を len 関数を使って取得し、 print 関数で出力してください。

回答例
x = len('Python')
print(x)
解説

文字列に対して len() 関数はその文字数を返します。
入力値が文字列型でない場合も、len 関数は入力値に応じた長さを返します。

len という関数名の由来は、長さを意味する英単語 length です。

問題 15 部分文字列

文字列 "Python" を変数に格納し、最初の2文字を抽出して print 関数で出力してください。

回答例
x = 'Python'
print(x[0:2])
解説

文字列の指定位置にある文字は、0から始まるインデックスを使って取得できます。

s = 'Python'
print('s[0]', s[0])  # 'P'
print('s[1]', s[1])  # 'y'
print('s[2]', s[2])  # 't'
print('s[3]', s[3])  # 'h'
print('s[4]', s[4])  # 'o'
print('s[5]', s[5])  # 'n'

さらに、文字列の指定範囲にある部分文字列は、 [開始インデックス:終了インデックス] の形式で指定して抽出できます。
開始インデックスは抽出されますが、終了インデックスは抽出されません。
この形式では開始インデックス、終了インデックスを省略することができ、省略した場合、それぞれ先頭や末尾を意味します。

s = 'Python'
print('s[2:5]', s[2:5])  # 'tho'
print('s[:4]', s[:4])   # 'Pyth'
print('s[3:]', s[3:])   # 'hon'
print('s[:]', s[:])    # 'Python'

また、インデックスには負数を指定することも可能です。
負数の場合、文字列の末尾からの位置インデックスを意味します。

s = 'Python'
print('s[-1]', s[-1])  # 'n'
print('s[-2]', s[-2])  # 'o'
print('s[-3]', s[-3])  # 'h'
print('s[-4]', s[-4])  # 't'
print('s[-5]', s[-5])  # 'y'
print('s[-6]', s[-6])  # 'P'
s = 'Python'
print('s[2:-2]', s[2:-2])  # 'th'
print('s[-2:]', s[-2:])  # 'on'

このように特定の範囲を指定して操作する機能をスライスと呼びます。

問題 16 None の使用

変数に None を代入し、その値を print 関数で出力してください。

回答例
x = None
print(x)
解説

None は特別な値で、変数が値を持たないことを意味します。
特定の条件で値が存在しないことを示すために使います。

問題 17 変数のデータ型

type 関数を使って、次の各変数のデータ型を確認してください。

a = 10
b = 3.14
c = "Hello, Python!"
d = True
回答例
a = 10
b = 3.14
c = "Hello, Python!"
d = True

print(type(a))
print(type(b))
print(type(c))
print(type(d))
解説

Pythonでは変数に値を代入すると、代入した値に対応するデータ型が自動的に決まります。
下記はよく使われるデータ型の一覧です。

データ型 説明
int (整数型) 小数点を持たない数値です。正の数、負の数、ゼロが含まれます。 10, -3, 0
float (浮動小数点型) 小数点を含む数値です。 3.14, -0.001, 2.0
str (文字列型) 漢字やひらがな、アルファベットや数字、記号などの文字の並びです。 "例1", 'Python', "123"
bool (ブール型) 正誤を意味する TrueFalse の2つの値しか持たない型です。 True, False
list (リスト型) 複数の要素を順序付けて保持する型です。 [1, 2, 3], ["a", "b", "c"]
tuple (タプル型) 複数の要素を順序付けて保持する型です。リスト型と違い、作成後に要素を変更できません。 (1, 2, 3), ("a", "b", "c")
set (集合型) 重複しない要素の集まりを保持する型です。順序は保証されません。 {1, 2, 3}, {"a", "b", "c"}
dict (辞書型) キーと値の組でデータを保持する型です。 {"a": 1, "b": 2, "c": 3}
NoneType 特殊なデータ型で、値がないことを示します。 None

type 関数を使うことで、変数がどのデータ型であるかを調べることができます。

リスト操作

問題 18 リストの作成と操作

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst とその先頭要素をそれぞれ print 関数で出力してください。

回答例
lst = [1, 2, 3]
print(lst)
print(lst[0])
解説

リストは複数の値を保持するためのデータ構造で、角括弧 [] で囲んで作成します。
カンマで区切ることで複数の要素を持つリストを作成できます。
リストに含まれる要素は順番に、lst[0]lst[1]lst[2] のように指定することで取得できます。
回答例では変数 lst にリストを代入していますが、別の変数名でも同様に指定します。
例えば変数名を x にした場合は次のようになります。

x = [1, 2, 3]
print(x)
print(x[0])

問題 19 リストのスライス

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst の先頭から2つの要素を抽出してできるリスト [1, 2]print 関数で出力してください。

回答例
lst = [1, 2, 3]
print(lst[:2])
解説

文字列から部分文字列を取得する際に使用したスライス機能を使って、リストの一部を抽出できます。
回答例のように、[:2] はリストの最初の2つの要素を返します。
その他の指定方法についても、文字列の部分文字列と同様です。

lst = ['P', 'y', 't', 'h', 'o', 'n']
print('lst[0]', lst[0])  # 'P'
print('lst[1]', lst[1])  # 'y'
print('lst[2]', lst[2])  # 't'
print('lst[3]', lst[3])  # 'h'
print('lst[4]', lst[4])  # 'o'
print('lst[5]', lst[5])  # 'n'
lst = ['P', 'y', 't', 'h', 'o', 'n']
print('lst[-1]', lst[-1])  # 'n'
print('lst[-2]', lst[-2])  # 'o'
print('lst[-3]', lst[-3])  # 'h'
print('lst[-4]', lst[-4])  # 't'
print('lst[-5]', lst[-5])  # 'y'
print('lst[-6]', lst[-6])  # 'P'

問題 20 リストの追加

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst の末尾に数値 4 を追加してできるリスト [1, 2, 3, 4]print 関数で出力してください。

回答例
lst = [1, 2, 3]
lst.append(4)
print(lst)
解説

append メソッドはリストの末尾に新しい要素を追加します。
リストオブジェクトを直接変更する処理を行い、戻り値は常に None です。
例えば、以下のコードはリスト自体が変更されることを示しています。

lst = [1, 2, 3]
result = lst.append(4)
print(lst)    # [1, 2, 3, 4] と出力される
print(result) # None と出力される

また、append メソッドはリストオブジェクトに定義されている関数であり、リスト型の変数が持っている機能です。
リスト型の変数 x に対し、x.append() の形で使用します。

このように、オブジェクトが持っている関数をメソッドと呼びます。
長さを取得する len 関数や、型を取得する type 関数等とは使い方が異なることに注意が必要です。

問題 21 リストの長さ

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst の長さを len 関数で取得し、print 関数で出力してください。

回答例
lst = [1, 2, 3]
print(len(lst))
解説

len 関数は入力値に応じた長さを返します。
入力値がリスト型の場合、含まれている要素の数を返します。

このように、Python に標準で用意されている汎用的な関数を組み込み関数と呼びます。
組み込み関数には次のようなものがあります。

関数名 説明
print 入力値を文字列として出力する
len 入力値の長さを取得する
type 入力値の型を取得する
str 入力値を文字列に変換する
int 入力値を整数に変換する

問題 22 リストの削除

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst から数値 2 を削除してできるリスト [1, 3]print 関数で出力してください。

回答例
lst = [1, 2, 3]
lst.remove(2)
print(lst)
解説

リストオブジェクトの remove メソッドは、指定した値と一致する最初の項目を削除します。
append メソッドと同様に、リストオブジェクトに定義されています。
リスト内に同一の値が複数存在していた場合、最初に一致した項目のみが削除されます。
また、一致する項目が存在しない場合 ValueError が発生します。

lst = [1, 1, 2, 3]
lst.remove(1)
print(lst)    # [1, 2, 3] と出力される
lst.remove(1) 
print(lst)    # [2, 3] と出力される
lst.remove(1) # エラーが発生する

問題 23 リストの結合

2つのリスト [1, 2][3, 4] を結合してできるリスト [1, 2, 3, 4]print 関数で出力してください。

回答例
lst = [1, 2] + [3, 4]
print(lst)
解説

リスト同士に + 演算子を適用すると、リスト内の要素を足し合わせます。
文字列や数値で使用する + 演算子と同じ書き方であり、型に応じた加算処理が行われます。

もし + 演算子ではなく append メソッドを使用した場合、次のようにリストの中にリストが含まれる結果となります。

lst = [1, 2]
lst.append([3, 4])  # [1, 2, [3, 4]] となる
print(lst)

条件分岐とループ

問題 24 条件分岐(if 文)

変数 x に数値 15 を代入してください。
if 文を使って、変数 x の値が 10 より大きいかどうかを判定し、10 より大きい場合に "xは10より大きい" と出力してください。

回答例
x = 15
if x > 10:
    print("xは10より大きい")
解説

if 文は条件が True と解釈された場合にブロックを実行します。
条件が False の場合はブロックをスキップします。
例えば、変数 x が 10 より大きい場合に "xは10より大きい" と出力します。

最初に変数 x に代入する値を 15 ではなく 1 などに変更すれば、10より小さい場合は "xは10より大きい" が表示されないことを確かめることができます。

x = 1
if x > 10:
    print("xは10より大きい")

if 文は文末にコロン : を記載し、後に続く行はインデント(字下げ)される必要があります。
これは、その行が if 文の一部であることを示すためです。
もし次のように if 文の中で何も処理しないまま次の処理に進むと、インデントされていないとみなされてエラーが発生します。

x = 15
if x > 10:
    # ここに処理を追加する予定
    
if x > 20:
    # ここに処理を追加する予定

これを回避するためには pass ステートメントを使用します。
次のように pass を使ってブロックを空にすることで、エラーを回避することができます。

x = 15
if x > 10:
    # ここに処理を追加する予定
    pass
if x > 20:
    # ここに処理を追加する予定
    pass

インデントが正しくない場合、Python はエラーを発生させます。インデントには通常4つのスペースを使用しますが、タブでも構いません。
ただし、インデントに使用する文字はコード全体で統一する必要があります。

問題 25 条件分岐(elif、else)

変数 x に数値 10 を代入してください。
elifelse を使って変数 x の大きさを判定し、結果に応じて次のようにメッセージを出力してください。

(1) 10 より大きい場合: "xは10より大きい"
(2) 上記以外で 5 より大きい場合:"xは5より大きいが、10以下"
(3) 上記以外の場合: "xは5以下"

回答例
x = 10
if x > 10:
    print("xは10より大きい")
elif x > 5:
    print("xは5より大きいが、10以下")
else:
    print("xは5以下")
解説

if 文に続けて elifelse を使うことで、複数の条件を順に確認できます。
elif 文では if 文と同様に条件を記述し、else はいずれの条件を満たさなかったという条件を意味します。
どれかの条件が True の場合、そのブロックが実行されます。

また、True となったブロック以降の elifelse のブロックは実行されません。
例えば、変数 x の値が 12 の場合、最初の if x > 10: ブロックが実行され、それ以降の elif x > 5:else のブロックはスキップされます。

変数 x の値が 5 より大きくても、それ以前のブロックが実行されていれば elif x > 5: のブロックはスキップされます。
もし x > 10x > 5 の両方のブロックの処理を実行させたい場合、elif を使わずに if で記述することになります。

x = 12
if x > 10:
    print("xは10より大きい")
if x > 5:
    print("xは5より大きい")

elif は else if の省略形であり、if 文と「それ以外で」という意味の else に由来する構文です。

問題 26 比較演算子(大小関係)

変数 x に数値 10、変数 y に数値 20 を代入してください。
次の大小関係を確認し、それぞれの結果を TrueFalse で出力してください。

(1) xy より大きい
(2) xy 以上
(3) xy と等しい
(4) xy 以下
(5) xy より小さい

回答例
x = 10
y = 20
print(x > y)
print(x >= y)
print(x == y)
print(x <= y)
print(x < y)
解説

比較演算子は2つの値を比較し、結果を True または False で返します。
そのため、次のように比較結果を変数に代入することも可能です。

x = 10
y = 20
z = x > y
print(z)  # False と表示される

この例では、変数 zx > y の結果である False が代入されます。

Python で数の大小を取得する比較演算子は次のようなものがあります。

比較演算子 意味
> より大きい
< より小さい
>= 以上
<= 以下
== 等しい
!= 等しくない

次のコードは、比較演算子の結果を整理して表示します。

x = 10
y = 20
print(f'x={x}, y={y}')
print('xはyより大きい', x > y)
print('xはy以上',       x >= y)
print('xはyと等しい',   x == y)
print('xはy以下',       x <= y)
print('xはyより小さい', x < y)
print('xはyと異なる',   x != y)

問題 27 比較演算子(包含関係)

変数 lst にリスト [1, 2, 3] を代入してください。
in 演算子を使って、リストに要素 2 が含まれているか確認し、その結果を TrueFalse で出力してください。

回答例
lst = [1, 2, 3]
print(2 in lst)
解説

in 演算子は、指定した要素がリストに含まれているかを確認し、結果を True または False で返します。
また in 演算子は、リストに限らず、順番に要素を取り出せるオブジェクトに対して使用することができます。
順番に要素を取り出せるオブジェクトはリストの他にもタプル、集合、辞書、文字列などがあります。

次のコードは文字列 "Python" の中に、小文字の "p" と大文字の "P" が含まれるか確認します。

print('p' in 'Python')  # False が表示される
print('P' in 'Python')  # True が表示される

問題 28 比較演算子(オブジェクト)

変数に None を代入してください。
is 演算子を使って、変数が None であるか確認し、その結果を TrueFalse で出力してください。

回答例
x = None
print(x is None)
解説

is 演算子は同一オブジェクトかどうかの確認を行います。
等号関係を比較する == 演算子と違い、オブジェクトが同一であるか比較ができます。
つまり、2つのオブジェクトの値が一致することを確認する際に == 演算子を使用し、オブジェクト自体が同一であることを確認する際に is 演算子を使用します。
例えば、次のコードは同じ要素をもつ2つのリストを ==is で比較しています。

x = [1, 2, 3]
y = [1, 2, 3]
print('x == y: ', x == y)  # 値が同じなので True
print('x is y: ', x is y)  # オブジェクトが異なるので False

一方で、完全に同じリスト同士を ==is で比較すると次のようになります。

x = [1, 2, 3]
y = x
print('x == y: ', x == y)  # 値が同じなので True
print('x is y: ', x is y)  # オブジェクトも一致するので True

次のコードは if 文で is 演算子を使った例です。print 関数の戻り値が None であることが分かります。

x = print('print 関数の戻り値を取得します')
if x is None:
    print('print 関数の戻り値は None です')

問題 29 論理演算子

変数 x に数値 10、変数 y に数値 20、 を代入してください。
論理演算子 andor を使って次の大小関係を確認し、それぞれの結果を TrueFalse で出力してください。

(1) xy の両方が 15 より大きい
(2) xy の少なくとも片方が 15 より大きい

回答例
x = 10
y = 20
print(x > 15 and y > 15)
print(x > 15 or y > 15)
解説

and 演算子は前後の条件が両方 True と解釈される場合に True と解釈された値を返します。
or 演算子は前後の条件が少なくとも片方 True と解釈される場合に True と解釈された値を返します。

False と解釈される値は False 以外にも存在し、下表の値はFalse と解釈されます。

備考
False
None None オブジェクト
0, -0 整数のゼロ(数値がない状態)
0.0, -0.0 浮動小数点のゼロ
"", '' 空白の文字列
[] 空のリスト(要素なし)
{} 空の辞書(キー・値なし)
set() 空の集合

逆に、下表のような値は True と解釈されます。

説明
True
1, -5 0 でない任意の整数
0.1, -3.5 0.0 でない浮動小数点数
"Python" 空でない文字列
[1, 2, 3] 要素があるリスト
{"key": "value"} 要素がある辞書
{1, 2, 3} 要素がある集合

例えば、次のように論理演算子を使用して TrueFalse と解釈される値を得ることができます。

x = 1 and True
print(x)

また、if 文においても TrueFalse 以外の条件を指定することも可能です。

x = 'Python'
if x:
    print(x  True と解釈されました)

問題 30 論理否定

変数に None を代入してください。
is 演算子を使って、変数が None であるか判定し、None ではない場合に "x は None ではない" を出力してください。

回答例
x = None
if not (x is None):
    print("x は None ではない")
解説

not 演算子は条件を反転させます。 TrueFalse に反転し、 FalseTrue に反転します。
ただし、is 演算子や in 演算子の結果を反転したい場合は、それぞれ is not 演算子や not in 演算子を用いて記述することが一般的です。

x = 'Python'
if x is not None:
    print('x は None ではない')
lst = [1, 2, 3]
if 0 not in lst:
    print('0 は lst に含まれない')

問題 31 for ループ(リスト)

変数にリスト [1, 2, 3] を代入してください。
for ループを使って、リストの全ての要素を順に出力してください。

回答例
lst = [1, 2, 3]
for item in lst:
    print(item)
解説

リストを用いた for ループは、指定したリスト内の全ての要素に対して順番に処理を行います。
例えば for item in lst: と記述した場合、リスト lst の要素が先頭から順番に変数 item に代入されて繰り返し処理されます。
つまり、次のような繰り返し処理が実行されます。

  • item = lst[0] と代入された状態で for 分のブロック内の処理を実行
  • item = lst[1] と代入された状態で for 分のブロック内の処理を実行
  • item = lst[2] と代入された状態で for 分のブロック内の処理を実行

...

  • item = lst[len(lst)-1] と代入された状態で for 分のブロック内の処理を実行

for 文は if 文と同様、文末にコロン : を記載し、後に続く行は、インデントされる必要があります。

問題 32 for ループ(range)

range 関数とfor ループを使って、0 から 9 までの数を出力してください。

回答例
for i in range(10):
    print(i)
解説

for ループはリストだけでなく、タプル、集合、辞書、文字列など、順番に要素を取り出せるオブジェクトを指定して処理を繰り返すことができます。
range 関数は指定された範囲の数値を順番に取り出せるオブジェクトを生成します。
range(n)0 から n-1 までの n 個の数値を生成するため、同じ処理を n 回繰り返す場合に使用されます。

もし、0 からではなく 1 から順番に繰り返しを行いたい場合、2つの引数を指定して range(1, n) のように指定することもできます。
この場合、range(1, n)1 から n-1 までの n-1 個の数値を生成するオブジェクトを返します。

for i in range(1, 10):
    print(i)  # 1, 2, 3,...,9 が順に出力される

また、range(n) はリストではないため、リストのように + 演算子で加算することができません。
例えば 0 から 9 までの10ループに加えて、 20 から 29 までの10ループを合わせて合計20回の処理をしたい場合を考えます。
次のような記述はエラーになってしまいます。

for i in range(10) + range(20, 30):  # `+` 演算子がエラーとなる
    print(i)

これを回避するに、加算する前にリストに変換する方法があります。

for i in list(range(10)) + list(range(20, 30)):
    print(i)

list 関数はリスト型に変換処理を行うため、リストとしての加算が可能となります。
型の変換が行われていることは、次のように確認できます。

x = range(10)
print(type(x))  # range(10) の型を確認 → <class 'range'>

y = list(x)
print(type(y))  # リスト変換後の型を確認 → <class 'list'>

問題 33 break の使用

変数 x に数値 5 を代入してください。
0 から 9 までの数を順番に出力するfor ループ内で break を使用し、出力する値が x になった時点で処理を終了してください。

回答例
x = 5
for i in range(10):
    if i == x:
        break
    print(i)
解説

break ステートメントを使うことで、ループの途中で終了することができます。
ループの実行中に特定の条件が満たされた場合に、ループを終了するのに役立ちます。

問題 34 continue の使用

変数 x に数値 5 を代入してください。
0 から 9 までの数を順番に出力するfor ループ内で continue を使用し、出力する値が x になった場合の処理をスキップしてください。

回答例
x = 5
for i in range(10):
    if i == x:
        continue
    print(i)
解説

continue ステートメントを使うことで現在のループ処理を終了し、次のループ処理に移行することができます。
繰り返し処理中に if 文と組み合わせて使用し、特定の条件を満たした場合に処理をスキップする仕組みを実現します。

問題 35 while ループ

while ループを使って、10 から 1 まで 1 ずつカウントダウンして出力してください。

回答例
x = 10
while x > 0:
    print(x)
    x = x - 1
解説

while ループは、指定された条件が True である限りループを繰り返します。

x = x - 1 は変数 x の値を1減らすことを意味する代入文です。
例えば、x10 の時に x - 19 であり、x = x - 1x9 を代入します。
同様に、x9 の時に x - 18 であり、x = x - 1x8 を代入します。

結果として、x の値を 1 ずつ下げながら繰り返し処理を実行することができます。
回答例では while の条件で x > 0 を指定しているため、x0 が代入された次のループは処理が行われずに終了することになります。

また、x = x - nx -= n と記述しても同じ処理を実現できます。
同様に、x += nx = x + n と同じ処理が行われます。
例えば、100 から 15 ずつカウントダウンして、負の数は表示せずに終了する場合は次のようなコードになります。

x = 100
while x > 0:
    print(x)
    x -= 15

データ構造

問題 36 タプルの作成と操作

変数 tpl にタプル (1, 2, 3) を代入してください。
変数 tpl とその先頭要素をそれぞれ print 関数で出力してください。

回答例
tpl = (1, 2, 3)
print(tpl)
print(tpl[0])
解説

角括弧 [] で囲んで作成したリストに対し、タプルは丸括弧 () で囲んで作成します。
文字列の部分文字列やリスト操作と同じように、タプルの要素を抽出することができます。

tpl = ('P', 'y', 't', 'h', 'o', 'n')
print('tpl[0]', tpl[0])  # 'P'
print('tpl[1]', tpl[1])  # 'y'
print('tpl[2]', tpl[2])  # 't'
print('tpl[3]', tpl[3])  # 'h'
print('tpl[4]', tpl[4])  # 'o'
print('tpl[5]', tpl[5])  # 'n'
tpl = ('P', 'y', 't', 'h', 'o', 'n')
print('tpl[-1]', tpl[-1])  # 'n'
print('tpl[-2]', tpl[-2])  # 'o'
print('tpl[-3]', tpl[-3])  # 'h'
print('tpl[-4]', tpl[-4])  # 't'
print('tpl[-5]', tpl[-5])  # 'y'
print('tpl[-6]', tpl[-6])  # 'P'
tpl = ('P', 'y', 't', 'h', 'o', 'n')
print('tpl[1:-1]: ', tpl[1:-1])  #  ('y', 't', 'h', 'o')

問題 37 タプルの結合と長さ

2つのタプル (1, 2)(3, 4) を結合してできるタプル (1, 2, 3, 4) を変数 tpl に代入してください。
変数 tpl とその長さを print 関数で出力してください。

回答例
tpl = (1, 2) + (3, 4)
print(tpl)
print(len(tpl))
解説

リストと同じく、+ 演算子を用いてタプル同士の結合を行うことができます。
また、リストと同じく len() 関数でタプルに含まれている要素の数を取得することができます。

タプルとリストの違いとして、タプルは一度作成したオブジェクトに編集を加えることができません。
例えば、タプルには append メソッドや remove メソッドがないため、次のコードはエラーが発生します。

tpl = (1, 2, 3)
tpl.append(4)  # エラーが発生する
tpl = (1, 2, 3)
tpl.remove(2)  # エラーが発生する

さらに、リストであればインデックスを指定して値を書き換えることができますが、タプルではエラーとなります。

lst = [1, 2, 3]
lst[0] = 9
print(lst)  # [9, 2, 3] が出力される

tpl = (1, 2, 3)
tpl[0] = 9  # エラーが発生する

問題 38 集合の作成、追加、削除

変数 st に集合 {1, 2, 3} を代入してください。
変数 st のメソッドを使用して次の操作を順番に行い、それぞれの結果を print 関数で出力してください。

(1) 変数 st に要素 1 を追加
(2) 変数 st から要素 1 を削除

回答例
st = {1, 2, 3}
st.add(1)
print(st)

st.remove(1)
print(st)
解説

集合は要素の重複を許さず、順序を持たないデータ構造です。
リストやタプルのように複数の要素を持つことができますが、インデックスを指定して先頭や末尾などの要素を取得することはできません。
また、重複した要素は1つとして扱われます。

例えば、集合型オブジェクトの add() メソッドは指定した要素を追加しますが、重複する要素を追加しても変化しません。

st = {1, 2, 3}
st.add(1)  # 既に存在している 1 と重複するため変化しない
print(st)  # {1, 2, 3} が出力される

また、集合型オブジェクトの remove() メソッドは指定した要素を削除します。
指定した要素が存在しない場合、remove メソッドはエラーとなります。

st = {1, 2, 3}
st.remove(1)  # 1 が削除されて {2, 3} になる
st.remove(1)  # 削除対象の要素が存在しないためエラーが発生する

一方で、discard メソッドも指定要素を削除する機能を持ちますが、指定要素が存在しない場合でもエラーが発生しません。

st = {1, 2, 3}
st.discard(1)  # 1 が削除されて {2, 3} になる
st.discard(1)  # エラーとはならず、変化しない
print(st)  # {2, 3} が出力される

エラーが発生しない discard メソッドですが、リストやタプルに discard は定義されていません。
また、集合に要素を追加する add メソッドも、リストやタプルには定義されていません。
リストに要素を追加するメソッドは append であり、タプルは要素を追加する機能がありません。

単語 意味 定義されているデータ型
remove 「削除する」→ 指定した要素を削除(存在しない場合はエラー) リスト型 / 集合型
discard 「捨てる」「処分する」→ 指定した要素を削除(存在しない要素を指定してもエラーにならない) 集合型
add 「追加する」「付け加える」→ 指定した要素を追加(重複を許さない) 集合型
append 「付け足す」「末尾に追加する」→ 指定した要素を末尾に追加(順序を保持し、重複チェックは行われない) リスト型

問題 39 集合の和集合と共通部分

変数 x{1, 2, 3} を代入し、変数 y{3, 4, 5} を代入してください。
変数 x と変数 y の和集合 {1, 2, 3, 4, 5} を計算し、print 関数で出力してください。
また、変数 x と変数 y の共通部分 {3} を計算し、print 関数で出力してください。

回答例
x = {1, 2, 3}
y = {3, 4, 5}
print(x.union(y))
print(x.intersection(y))
解説

集合型オブジェクトの union() メソッドは、指定した集合との和集合を返します。
和集合は少なくとも片方に含まれている要素を含む集合であり、両方に含まれている要素は1つにまとめられます。

また、| 演算子も同様の結果を得ることができます。

st = {1, 2, 3} | {3, 4, 5}
print(st)  # {1, 2, 3, 4, 5} が出力される

集合型オブジェクトの intersection() メソッドは、指定した集合との共通部分を返します。
両方の集合に共通して含まれている要素のみを含む集合です。
また、& 演算子も同様の結果を得ることができます。

st = {1, 2, 3} & {3, 4, 5}
print(st)  # {3} が出力される

問題 40 集合の要素

変数 st に集合 {1, 2, 3} を代入してください。
for 文を用いて変数 st の要素を1つずつ出力してください。

回答例
st = {1, 2, 3}
for x in st:
    print(x)
解説

リストと同様に、集合に対しても for 文で繰り返し処理を行うことができます。
ただし、集合は順序を持たないデータ構造であるため、要素の出力順序は一定ではありません。

また、指定した要素が集合に含まれているか確認する場合、in 演算子を使用できます。

st = {1, 2, 3}
print(1 in st) # True が表示される
print(9 in st) # False が表示される

集合は要素の重複を許さず、順序を持たないという特徴があります。
処理したい内容や目的に応じて、リストやタプルなどの他のデータ構造から最適なものを使用できます。

もし、処理の中で他のデータ構造の機能を使いたくなった場合、データ構造を変換することも可能です。
次の例は、リストから重複要素を削除する目的で集合に変換する例です。

lst = [1, 1, 2, 3]
st = set(lst)  # 集合に変換、重複要素 1 が削除される
print(st)
print(type(st))

リストを集合に変換すると、順番が保持されなくなってしまうことに注意が必要です。

逆に、集合をリストに変換したい場合の例は次のようになります。

st = {1, 2, 3}
lst = list(st)  # リストに変換
lst.append(1)  # 末尾に重複要素 1 を追加する
print(lst)
print(type(lst))

問題 41 辞書の作成と操作

変数 dct に辞書 {'id': '0001', 'name': 'guest'} を代入してください。
変数 dct からキー 'name' の値を取得して print 関数で出力してください。

回答例
dct = {'id': '0001', 'name': 'guest'}
print(dct['name'])
解説

辞書はキーと値の組でデータを保持するデータ構造です。
辞書型の変数 x のキー 'key'にアクセスしたい場合、x['key'] と記述します。

次の例では、辞書型の変数 x に対し、x['key'] の形で代入と取得の両方の処理を行います。

dct = {'id': '0001', 'name': 'guest'}
dct['key'] = 'value'
print(dct['id'], dct['name'], dct['key'])  # 0001 guest value が表示される

もし、定義されていないキー 'key'の値を x['key'] の形で取得しようとするとエラーになります。

dct = {'id': '0001', 'name': 'guest'}
print(dct['key'])  # エラーが発生する

また、キーを指定すると対応する値が定まる仕組みのため、キーが重複することはできません。

例えば次のコードはキー name が重複している例です。
この場合、後から指定したキーと値の組が優先されます。

dct = {'name': '0001', 'name': 'guest'}  # name が重複している
print(dct)  # {'name': 'guest'} が表示される

問題 42 辞書のキーと値

変数 dct に辞書 {'id': '0001', 'name': 'guest'} を代入してください。
変数 dct に含まれるキーと値の組を取得し、for 文を用いて順番に全て print 関数で出力してください。

回答例
dct = {'id': '0001', 'name': 'guest'}
for key, value in dct.items():
    print(key, value)
解説

辞書型のオブジェクトの items メソッドを使うとキーと値の組を取得することができます。
実際に、回答例の for 文では、2つの要素 keyvalue を指定してます。

基本的な for 構文では、for x in dct.items(): のように1つの要素を指定します。
例えば、次のように1つの要素を指定する方法でも同じような処理を実現できます。

dct = {'id': '0001', 'name': 'guest'}
for x in dct.items():
    print(x)

このコードの出力結果を見ると、変数 x の値が ('id', '0001')('name', 'guest') となっていることが分かります。
つまり、キーと値の組が1つの変数に格納されており、キーと値を分けて扱うには向いていません。
例えば、次のコードではキーと値を x[0]x[1] として取得しています。

dct = {'id': '0001', 'name': 'guest'}
for x in dct.items():
    print(f'キー: {x[0]}, 値: {x[1]}')

そこで、複数の変数に同時に代入を行う機能を使用します。
次のコードはタプル型のデータ ('id', '0001') を変数 xy に同時に代入しています。

x, y = ('id', '0001')
print(x)  # id が出力される
print(y)  # 0001 が出力される

この機能を for 文で使用すると、回答例のようにキーと値の組を同時に取得して繰り返し処理を行うことができます。
この場合、キーと値は keyvalue として取得できるため、上記の例のように x[0]x[1] と記述する必要がありません。

dct = {'id': '0001', 'name': 'guest'}
for key, value in dct.items():
    print(f'キー: {key}, 値: {value }')

問題 43 リスト内包表記

リスト内包表記を使って、1 以上 10 未満の偶数リストを作成し、そのリストを print で表示してください。

回答例
even_list = [x for x in range(1, 10) if x % 2 == 0]
print(even_list)
解説

リスト内包表記は簡潔なリストの生成方法です。
条件を追加して特定の要素だけを含めることができます。

リスト内包表記の構文は次のようになります。

[ for 変数 in 繰り返しオブジェクト if 条件]

このリスト内包表記でできるリストは、次の処理でできる変数 result と同じです。
ただ、作成されるリストデータは同じですが、リスト内包表記を用いた方が高速に処理できます。

result = []  # 空のリストを作成
for 変数 in 繰り返しオブジェクト:
    if 条件:
        result.append()

この構文を用いて、回答例では range(1, 10) に含まれる要素 x の内、x % 2 == 0 を満たすものを抽出しています。
x % 2x2 で割った余りを計算し、x % 2 == 0x2 で割り切れるか判定します。

つまり、回答例の処理をリスト内包表記を使わずに記載すると次のようになります。

even_list = []
for x in range(1, 10):
    if x % 2 == 0:
        even_list.append(x)
print(even_list)

このコードと回答例を見比べると、 for x in range(1, 10)if x % 2 == 0 の部分は全く同じであることが分かります。

また、問題の内容を 1 以上 5 未満の整数を2倍したリストと読み替えて、次のような記述で同じ結果を得ることもできます。

even_list = [2 * x for x in range(1, 5)]
print(even_list)

その他の基本操作

問題 44 文字列の置換

変数 x に文字列 "1, 2, 3" を代入してください。
変数 x に含まれる文字 "," を文字 "&" に置換した文字列 "1& 2& 3" を print 関数で出力してください。

回答例
x = '1, 2, 3'
print(x.replace(',', '&'))
解説

文字列型オブジェクトの replace メソッドは、指定された文字列を別の文字列に置換します。
このとき元の文字列は変更されず、新しい文字列が返されます。
次のコードは、replace メソッドが元の文字列を変更しないことを確認しています。

x = '1, 2, 3'
x.replace(',', '&')
print(x)  # 1, 2, 3 が出力される

一方で、リスト型オブジェクトの append メソッドや remove メソッドは元のリストを変更します。

lst = [1, 2, 3]
lst.append(4)
print(lst)  # [1, 2, 3, 4] が出力される

オブジェクトに定義されているメソッドには、結果を戻り値で返すものとオブジェクトの値を更新するものがあることに注意が必要です。

問題 45 文字列の分割と結合

変数 x に文字列 "1, 2, 3" を代入してください。
変数 x を文字 "," で分割してできるリスト ['1', ' 2', ' 3'] を変数 lst に代入し、変数 lstprint 関数で出力してください。
さらに、変数 lst 内の文字列を文字列 "&" で結合してできる文字列 "1& 2& 3" を変数 y に格納し、変数 yprint 関数で出力してください。

回答例
x = '1, 2, 3'
lst = x.split(',')
print(lst)
y = '&'.join(lst)
print(y)
解説

文字列型オブジェクトの split メソッドは、指定された区切り文字で文字列を分割してできるリストを返します。
もし区切り文字を指定しなかった場合、split メソッドは連続したホワイトスペースで分割を行います。

x = '1 2 3   4'
print(x.split())  # ['1', '2', '3', '4'] が出力される

ここで、ホワイトスペースとは以下の文字などの空白の文字を意味します。

  • スペース ( )
  • タブ (\t)
  • 改行 (\n)
  • キャリッジリターン (\r)
  • 垂直タブ (\v)
  • 改ページ (\f)

文字列型オブジェクトの join メソッドは、指定されたリストの要素を自身の文字で結合してできる文字列を返します。
文字列型オブジェクト自身に制約はありません。
次の例は、長さ 0 の文字列 "" や長さ 5 の文字列 " and " に対して join メソッドを使用した例です。

lst = ['a', 'b', 'c']
print(''.join(lst))  # abc が出力される
print(' and '.join(lst))  # a and b and c が出力される

しかし、結合対象のリストの要素は文字列型である必要があります。
次の例は、数値型の要素からなるリストを指定した場合にエラーが発生することを示しています。

lst = [1, 2, 3]
'&'.join(lst)  # エラーが発生する

また、結合対象のリストを省略することはできません。

'&'.join()  # エラーが発生する

問題 46 数値計算

変数 x に数値 10、変数 y に数値 3 を代入してください。
次の計算を行い、結果を print 関数で出力してください。

(1) xy を掛けた結果(積)
(2) xy で割った結果(小数表示)
(3) xy で割った整数部分
(4) xy で割った余り

回答例
x = 10
y = 3
print(x * y)
print(x / y)
print(x // y)
print(x % y)
解説

掛け算の計算は * 演算子を使用します。
例えば、x * y は変数 x の値と変数 y の値を掛け合わせた結果を計算します。

通常の割り算の計算は / 演算子を使用します。
例えば、x / y は変数 x の値を変数 y の値で割った結果を浮動小数点数(小数)として計算します。

ただし、浮動小数点数は厳密な精度を持たないことがあるため、計算結果の誤差に注意が必要です。
例えば、コンピュータは 0.1 + 0.2 のような計算を完全に正確には扱えません。

print(0.1 + 0.2)  # 0.30000000000000004 が出力される

割り算の整数部分の計算は、// 演算子を使用します。
計算結果が負の少数の場合、// は小さくなるように整数部分を取得します。
例えば、-9 / 2-4.5 ですが、-9 // 2-5 を整数部分として取得します。

割り算の余りの計算は、%演算子を使用します。
x % y0 以上 |y| 未満の値を返します。
xy が整数であれば、(x // y) * y + (x % y)x と同じ値になります。

問題 47 リスト操作

変数 lst にリスト [1, 2, 3] を代入してください。
変数 lst に含まれる数値の最大値を取得し、print 関数で出力してください。

回答例
lst = [1, 2, 3]
print(max(lst))
解説

max 関数は、与えられた項目の中で最も大きな値を返します。
数値からなるリストであれば数値としても最大の値を返し、文字列からなるリストであれば辞書順で最後の文字列を返します。

エラー処理

問題 48 エラーハンドリング

変数 x に数値 0 を代入してください。
数値 1x で割った値を計算し、結果を print 関数で出力してください。
ただし、tryexcept を使ってゼロ除算エラー ZeroDivisionError を検知し、エラーが発生した場合は "ゼロ除算エラーが発生しました" と出力してください。

回答例
x = 0
try:
    print(1 / x)
except ZeroDivisionError:
    print('ゼロ除算エラーが発生しました')
解説

try 句内でエラーが発生した場合、except 句に記載した処理が実行されます。
try 句は if 文や for 文と同様、文末にコロン : を記載し、後に続く行はインデントされる必要があります。

これを使用すると、想定されるエラーに対し、エラー内容に応じた処理を実装することができます。
このような仕組みをエラーハンドリングと呼びます。

回答例では ZeroDivisionError が発生することを想定してエラーハンドリングしています。
もしエラーが発生し、そのエラーがエラーハンドリングされなかった場合、プログラムは異常終了してしまいます。

つまりエラーハンドリングを行わない場合、エラーが発生した箇所以降の処理が行われず、期待していた処理が実行されない可能性があります。

x = 0
y = 1 / x
print('処理を終了しました')  # 前の行でエラーが発生するため、この行は処理が行われない

そこでエラーハンドリングを行うことで、仮にエラーが発生しても、期待する処理を実行させることができます。

x = 0
try:
    y = 1 / x
except ZeroDivisionError:
    print('ゼロ除算エラーが発生しました')
print('処理を終了しました')

補足ですが、Python のエラーは構文エラー(SyntaxError)と実行時エラー(Exception)の2種類があります。
厳密には、try 句でエラーハンドリングできるのは実行時エラーだけです。
ここでは、主に実行時エラーのことをエラーと呼んでいます。

問題 49 エラー処理

変数 xNone を代入してください。
変数 x の値を 2 倍した値を print 関数で出力してください。
ただし、tryexcept を使って型エラー TypeError を検知し、エラーが発生した場合はエラー内容を出力してください。

回答例
x = None
try:
    print(x * 2)
except TypeError as e:
    print(e)
解説

変数 xNone が代入された状態で x * 2 という演算を行おうとすると、TypeError が発生します。
except 句で as キーワードを使用すると、発生したエラーの情報を取得することができます。

回答例では except TypeError as e: と記載することで、TypeError の内容を変数 e に格納しています。
変数 e の値を print 関数で出力すると、今回の場合は次のようなエラーメッセージが表示されます。

unsupported operand type(s) for *: 'NoneType' and 'int'

これは * 演算子が NoneType 型と int 型では操作できないということを意味しています。
このようなメッセージを出力しておくことで、予期せぬエラーが発生した際の調査に役立つ可能性があります。

問題 50 意図的なエラー

変数 x に数値 -1 を代入してください。
変数 x の値が負であるか判定し、負の値の場合に ValueError を発生させてください。
このとき、エラーメッセージは "負の値が入力されました" にしてください。

回答例
x = -1
if x < 0:
    raise ValueError('負の値が入力されました')
解説

raise キーワードを使って意図的に例外を発生させます。
これにより、特定の条件下でエラーを通知することができます。
さらに、try 句と組み合わせて使用することで、より高度なフローを作成することができます。
例えば、年齢を入力する場面で負の値が入力された場合にエラーを発生させ、入力処理に戻るようなフローが考えられます。

2.実践問題10問

実践問題(前半)

問題 51 九九の計算表示

2重の for 文を使って、次のような九九の計算を print 関数で出力してください。

1 × 1 = 1
1 × 2 = 2
...
1 × 9 = 9
2 × 1 = 2
...
2 × 9 = 18
...
9 × 1 = 9
...
9 × 8 = 72
9 × 9 = 81

回答例
for i in range(1, 10):
    for j in range(1, 10):
        print(f'{i} × {j} = {i * j}')
解説

問題文に「2重の for 文を使って」と指示があるので、それぞれのループで使用する繰り返し変数を2つ用意しましょう。
変数名は何でも良いですが、回答例では ij としています。
九九を表現するため、ij はそれぞれ 1 から 9 を順番に繰り返すことを想定しています。

このとき、f文字列を使えば九九の計算式は f'{i} × {j} = {i * j}' で表現できます。
例えば、次のようなコードで計算式が期待通りに表示できるか試すことができます。

i = 1
j = 2
print(f'{i} × {j} = {i * j}')

もちろん、f文字列を使わずに他の方法で計算式を表示する方法もあります。

i = 1
j = 2
print(i, '×', j, '=', i * j)

次に、この計算式を2重の for ループと組み合わせていきます。
問題文に記載された出力例で、まずは最初の数行に着目します。

1 × 1 = 1
1 × 2 = 2
...
1 × 9 = 9

この部分は、掛け算の左側の値が 1 で固定されており、最初に 1 の段を表示することが分かります。
先ほどの計算式を使うと、i1 で固定し、j1 から 9 まで繰り返すことで、この部分は表示できそうです。

i = 1
for j in range(1, 10):
    print(f'{i} × {j} = {i * j}')

for 文の書き方についても、他の方法で記載して問題ありません。
例えば range 関数の使い方に慣れていない場合、次のように後から 1 をプラスするという方法もあります。

i = 1
for x in range(9):
    j = x + 1
    print(f'{i} × {j} = {i * j}')

また、range 関数自体を覚えていなかった場合、繰り返したい値を列挙する方法もあります。

i = 1
for j in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    print(f'{i} × {j} = {i * j}')

次に、掛け算の左の値が 1 で固定されていた部分を動かしていきます。
問題文の出力例の続きを見ると、次は 2 の段です。

1 × 1 = 1
1 × 2 = 2
...
1 × 9 = 9
2 × 1 = 2
...
2 × 9 = 18

もし for 文を使わなければ、次のように i を変えながら同じ処理を繰り返していくことになります。

i = 1
for j in range(1, 10):
    print(f'{i} × {j} = {i * j}')

i = 2
for j in range(1, 10):
    print(f'{i} × {j} = {i * j}')
    
i = 3
for j in range(1, 10):
    print(f'{i} × {j} = {i * j}')

この内容を for 文で記載すると、回答例のようなコードになります。

問題 52 ループと条件分岐と文字列操作

100 未満の正の整数の内、いずれかの桁が 3 である数を全て表示してください。

回答例
for i in range(1, 100):
    if '3' in str(i):
        print(i)
解説

まず、問題文の「100 未満の正の整数」に着目します。
range 関数を使えば、次のように 100 未満の正の整数を全て表示することができます。

for i in range(1, 100):
    print(i)

ここから、条件「いずれかの桁が 3 である」を満たす場合にのみ表示するように条件分岐を行う必要があります。
この条件の判定を行うにあたり、大きく2つの方針が考えられます。

(1) 整数の各桁の値を取得し、3 に一致するか判定する。
(2) 整数を文字列として扱い、いずれかの文字が "3" に一致するか判定する。

まず方針 (1) では、各桁の値をどのように取得するかが問題になります。
今回の場合、調べたい値が 100 未満の正の整数となっているので、10 で割った整数部分と余りで取得できます。
つまり 100 未満の正の整数 i に対し、1の位の値は i % 10、10の位の値は i // 10 で取得できます。

例えば、次のコードは適当に選んだ 100 未満の正の整数 57 に対して 10 で割った整数部分と余りを取得しています。

i = 57
print(i % 10)  # 7 が表示される
print(i // 10)  # 5 が表示される

この方法で取得した各桁の値を 3 と比較し、一致する場合に print 関数で表示するには次のコードのようになります。

for i in range(1, 100):
    if i % 10 == 3 or i // 10 == 3:
        print(i)

このコードは正しく動作しますが、割り算の計算部分と論理演算 or を組み合わせている部分がやや複雑に見えます。
そこで、整数を文字列として扱う方針 (2) を考えてみます。

まず、整数 i を文字列として扱うには str 関数を使用します。
型の変換がうまくいっているか確認するには type 関数が役立ちます。

i = 57
x = str(i)
print(type(i))  # <class 'int'> が表示される
print(type(x))  # <class 'str'> が表示される

次に、方針 (1) で各桁の値を計算したように、文字列の各文字を1つずつ取得する方法を考えてみましょう。
文字列型の変数 x に対して、先頭文字は x[0]、次の文字は x[1] のように取得できます。
つまり、2桁の整数が文字列化されている前提であれば、次のように各桁の値を取得できます。

i = 57
x = str(i)
print(x[0])  # 5 が表示される
print(x[1])  # 7 が表示される

これを用いて、各桁の値を "3" と比較し、一致する場合に print 関数で表示するコードを書いてみます。

for i in range(1, 100):
    x = str(i)
    if x[0] == '3' or x[1] == '3':  # ここが問題
        print(i)

しかし、このコードを実行すると if x[0] == '3' or x[1] == '3': の部分で次のようなエラーが発生してしまいます。

IndexError: string index out of range

これは、文字列に範囲外のインデックスを指定してアクセスしようとしたときに発生するエラーです。
例えば、整数 1 を文字列化した長さ 1 の文字列 x に対し、2桁目にあたる x[1] はアクセスに失敗しエラーとなります。
つまり、2桁の整数が文字列化されていることを想定しており、1桁の整数が文字列になった場合の処理が考慮されていなかったのです。

この方針のままエラーを回避する方法はいくつかありますが、変更箇所が少なくて済むのは末尾の文字を取得する方法です。
2文字目へのアクセスのために x[1] を使用していましたが、2文字目ではなく末尾の文字 x[-1] を使用するのです。

整数が1桁の場合、先頭の文字と末尾の文字は一致し、該当の1桁の文字を取得することになります。

i = 1
x = str(i)
print(x[0])  # 1 が表示される
print(x[-1])  # 1 が表示される

もちろん、整数が2桁の場合は両方の桁の値を取得することができます。

i = 57
x = str(i)
print(x[0])  # 5 が表示される
print(x[-1])  # 7 が表示される

結果として、次のようなコードでいずれかの桁が 3 である整数を表示できます。

for i in range(1, 100):
    x = str(i)
    if x[0] == '3' or x[-1] == '3':
        print(i)

このコードは正しく動作しますし、方針 (1) での割り算による計算処理がシンプルになっています。
ただ、論理演算 or の部分は残っていますし、x[-1] を使用している点が理解しにくいかもしれません。

そこで、各桁の値を取得して "3" に一致するか判定する方針を変えて、文字列 "3" が含まれているか判定する方針を考えます。
つまり、次のように in 演算子を使用して包含関係の比較を行う方針です。

i = 57
x = str(i)
print('3' in x)

この方法であれば、各桁の値を取得して比較する処理は不要になります。
回答例はこの方針で作成したコードとなっています。

問題 53 ループの終了条件

正の整数の内、いずれかの桁が 3 である数を小さいほうから100個表示してください。

回答例
num = 1
count = 0
while count < 100:
    if '3' in str(num):
        print(num)
        count += 1
    num += 1
解説

この問題では正の整数を小さい順に判定し、条件を満たすものを100個見つけるまで処理を繰り返す必要があります。
見つけるべき整数の個数は100個と指定されていますが、繰り返しの回数は事前に特定できません。
このような場合には、条件が満たされるまで処理を繰り返す while 文を使うのが良いでしょう。
このとき、次の2種類の変数が必要になります。

(1) 条件を満たすか判定するため、小さい順に数え上げる正の整数
(2) 条件を満たした整数をいくつ見つけたか数えるための整数

ここでは、(1) のための変数を num、(2) のための変数を count とすることにします。
変数 num は小さい順に数え上げる正の整数なので、最初は 1 を代入しておきます。
また、変数 count は条件を満たした個数を表すため、最初は 0 を代入しておきます。

num = 1
count = 0

これらの変数を用いて、while 文の構造を考えます。
繰り返し処理の継続条件は100個見つけるまでです。
つまり、while 文の繰り返し条件に count < 100 を指定します。

変数 num は小さい順に数え上げるものであるため、繰り返し処理で毎回 1 ずつ増やしていきます。
一方で、変数 count は変数 num が条件を満たした際に 1 増やし、表示した整数の個数を記録します。

この方針で、while 文の全体を記述すると次のようになります。

num = 1
count = 0
while count < 100:
    if True:
        count += 1
    num += 1

この時点では条件を満たすかどうかの判定を無視しています。
つまり、常に条件を満たすことを意味する True を判定条件にして、if 文を if True: と記載しています。

ただ、このコードは print 関数を含んでいないため、実行しても何も出力されません。
そこで、条件を満たした際に判定対象の整数 num を表示するように print 関数を追加します。
この出力処理は変数 num が条件を満たした場合のみ実行させたいので、print 関数は if 文の中に追加します。

num = 1
count = 0
while count < 100:
    if True:
        print(num)
        count += 1
    num += 1

最後に、変数 num が条件を満たすかの判定処理を考えます。
問題文の条件は「いずれかの桁が 3 である」となっており、前問と同様の方針が使えます。
整数型の変数 num を文字列型に変換すれば、in 演算子を使って文字列 "3" が含まれているか確認することができます。

i = 57
x = str(i)
print('3' in x)

この方法を先ほどの while 文と組み合わせれば、回答例のコードになります。

問題 54 2次元リストの操作

変数 list_2d に2次元リスト [[1, 2, 3], [4, 5, 6], [7, 8, 9]] を代入してください。
変数 list_2d に含まれる数値要素 1, 2,..., 9を、それぞれ 2 倍して順番に表示してください。

回答例
list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in list_2d:
    for item in row:
        print(item * 2)
解説

2次元リストは、中に含まれている要素がリストになっているリストです。
行と列を持つ表のような構造と捉えることができます。

[1, 2, 3] # 1つ目の要素
[4, 5, 6] # 2つ目の要素
[7, 8, 9] # 3つ目の要素

実際に、次のようにインデックスを指定することで、上記のようなデータ構造を確認できます。

list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(list_2d[0])
print(list_2d[1])
print(list_2d[2])

また、次のように for 文を使うことで同様のデータ構造を確認できます。

list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in list_2d:
    print(row)

さらに、上記で表示したリストデータに含まれる用をアクセスしてみましょう。
インデックスを指定する方法を使う場合、さらに2つ目の角括弧 [] を指定することになります

list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(list_2d[0][0], list_2d[0][1], list_2d[0][2])
print(list_2d[1][0], list_2d[1][1], list_2d[1][2])
print(list_2d[2][0], list_2d[2][1], list_2d[2][2])

インデックス指定を for 文を使う方法と組み合わせて使用することも可能です。

list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in list_2d:
    print(row[0], row[1], row[2])

逆に、2重の for 文を使用することも可能です。

list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in list_2d:
    for item in row:
        print(item)

この問題では「それぞれ 2 倍して順番に表示」とのことなので、print 関数内で値を 2 倍する処理を追加します。
回答例では2重の for 文を使用し、取得した数値要素を 2 倍して出力しています。

問題 55 2次元リストの作成

リスト内包表記を用いて次のような要素を持つ 10×10 の2次元リストを作成し、print 関数で出力してください。

['0-0', '0-1', '0-2', '0-3', '0-4', '0-5', '0-6', '0-7', '0-8', '0-9']
['1-0', '1-1', '1-2', '1-3', '1-4', '1-5', '1-6', '1-7', '1-8', '1-9']
['2-0', '2-1', '2-2', '2-3', '2-4', '2-5', '2-6', '2-7', '2-8', '2-9']
['3-0', '3-1', '3-2', '3-3', '3-4', '3-5', '3-6', '3-7', '3-8', '3-9']
['4-0', '4-1', '4-2', '4-3', '4-4', '4-5', '4-6', '4-7', '4-8', '4-9']
['5-0', '5-1', '5-2', '5-3', '5-4', '5-5', '5-6', '5-7', '5-8', '5-9']
['6-0', '6-1', '6-2', '6-3', '6-4', '6-5', '6-6', '6-7', '6-8', '6-9']
['7-0', '7-1', '7-2', '7-3', '7-4', '7-5', '7-6', '7-7', '7-8', '7-9']
['8-0', '8-1', '8-2', '8-3', '8-4', '8-5', '8-6', '8-7', '8-8', '8-9']
['9-0', '9-1', '9-2', '9-3', '9-4', '9-5', '9-6', '9-7', '9-8', '9-9']

回答例
list_2d = [[f'{x}-{y}' for y in range(10)] for x in range(10)]
print(list_2d)
解説

この問題では、要素としてリストを含むリストを作成する必要があります。
通常のリスト内包表記はリストを作成する機能です。
この問題のように2次元リストを作成するには、リスト内包表記の中にリスト内包表記を含む構造になると考えられます。

まずは、問題文で提示されている最初のリスト要素に着目してみましょう。

['0-0', '0-1', '0-2', '0-3', '0-4', '0-5', '0-6', '0-7', '0-8', '0-9']

このリストの要素は "0-y" の形の文字列で、"y" は列番号に対応していることがわかります。
そのため、列番号 y に対する要素はf文字列を使って f'0-{y}' と記述できます。

以上を踏まえると、最初の要素は次のリスト内包表記で作成できることが分かります。

row = [f'0-{y}' for y in range(10)] 
print(row)

続いて、2番目のリスト要素に着目します。

['1-0', '1-1', '1-2', '1-3', '1-4', '1-5', '1-6', '1-7', '1-8', '1-9']

先頭のリスト要素では "0-y" の形の文字列だった要素が、 "1-y" の形の文字列になっています。
そのため、先頭のリストと同様に考えることで、次のリスト内包表記で作成できることが分かります。

row = [f'1-{y}' for y in range(10)] 
print(row)

これ以降も同様で、 要素の形が "0-y", "2-y", "3-y",..., "9-y" と順番に変わっているだけです。
この変わっている部分を整数型の変数 x で表すと、次のように共通の形で書くことができます。

x = 3
row = [f'{x}-{y}' for y in range(10)] 
print(row)

目的の2次元リストは、このリストを要素に持つようにリスト内包表記で作成できます。
変数 x0 から 9 の範囲で順番に動くように指定すれば、回答例のコードになります。

問題 56 辞書の操作

変数 ascii_dict に辞書 {'0x30': '0', '0x40': '@', '0x50': 'P'} を代入してください。
変数 ascii_dict のキーと値を逆にした辞書 {'0': '0x30', '@': '0x40', 'P': '0x50'} を作成し、print 関数で出力してください。

回答例
ascii_dict = {'0x30': '0', '0x40': '@', '0x50': 'P'}
reversed_dict = {value: key for key, value in ascii_dict.items()}
print(reversed_dict)
解説

指定された辞書型の変数 ascii_dict のキーと値は items 関数を使って順番に取り出すことができます。
これを用いて、目的の「キーと値を逆にした辞書」を新たな変数 reversed_dict に格納することにします。

まず、変数 reversed_dict を何も含まない空の辞書にしておきます。
その後、items 関数で取得したキーと値を逆にして、順番に変数 reversed_dict に設定していきます。

reversed_dict = {}
for key, value in ascii_dict.items():
    reversed_dict[value] = key
print(reversed_dict)

このとき、辞書のキーが重複できないことに注意が必要です。
もし元の辞書 ascii_dict の2つのキーで値が一致していた場合、キーと値を逆にした際に変数 reversed_dict のキーが重複してしまいます。
そして重複したキーをそのまま保持することは不可能です。

ただし、今回の問題で指定された辞書 ascii_dict では値が重複していないため、そのような問題は発生しません。
もし元の辞書で値が重複する可能性がある場合、エラーとして処理したり、いずれか1つのキーと値のみを保持する仕組みにするなどの対応が必要になります。

結果として、先ほどのコードで「キーと値を逆にした辞書」を出力することができます。

{'0': '0x30', '@': '0x40', 'P': '0x50'}

また、この for 文を用いた処理は辞書内包表記を使って実現できます。
辞書内包表記はリスト内包表記と似た構文で、キーと値のペアを条件指定して辞書を作成する機能です。

回答例では辞書内包表記を使って、キーと値を逆にした辞書を簡潔に作成しています。

問題 57 文字列のカウント

変数 target_text に文字列 "hello" を代入してください。
変数 target_text に含まれる各文字の出現回数をカウントし、文字と出現回数の組を全て表示してください。

回答例
target_text = 'hello'
count_dict = {}
for char in target_text:
    if char in count_dict:
        count_dict[char] += 1
    else:
        count_dict[char] = 1
print(count_dict)
解説

この問題では、変数 target_text に含まれる各文字を順にチェックし、各文字の出現回数をカウントします。
各文字の出現回数は辞書型の変数 count_dict を作成して保持することにします。
変数 count_dict のキーはカウント対象の文字で、その文字の出現回数を値に保持します。

例えば、1文字目の "h" をチェックする処理で、変数 count_dict{'h': 1} にして出現回数を保持します。
2文字目の "e" をチェックする処理では、同様に {'h': 1, 'e': 1} のように出現回数を保持します。
全ての文字をチェックした時点で、文字列 "hello" に含まれる各文字の出現回数を次のように保持しているイメージです。

{'h': 1, 'e': 1, 'l': 2, 'o': 1}

そこで、まずは各文字の出現回数を格納するための空の辞書 count_dict を作成します。
文字列がの変数 target_text に含まれる文字は、for 文で順番に取得できます。

target_text = 'hello'
count_dict = {}
for char in target_text:
    print(f'文字 {char} の出現回数をカウントする')

文字 char に対して変数 count_dict のカウントを増やすには count_dict[char] += 1 を実行します。
しかし、この処理をそのまま適用することはできません。

target_text = 'hello'
count_dict = {}
for char in target_text:
    count_dict[char] += 1  # エラーが発生する

上記のコードでは、count_dict[char] += 1 の行で次のエラーが発生してしまいます。

KeyError: 'h'

これは変数 count_dict の存在しないキー "h" のカウントを増やそうとして、アクセスに失敗してエラーとなっています。

そこで、文字の出現回数のカウント処理は、次の2つの状況を考慮する必要があります。

(1) 出現した文字が変数 count_dict に既に存在する場合、カウントを増やす。
(2) 出現した文字が変数 count_dict にまだ存在しない場合、カウントを 1 として保持する。

そこで文字 char が既に変数 count_dict のキーとして存在するかどうかを確認し、 if 文で処理を分岐します。
辞書型の変数 count_dict に指定したキーが存在するかの確認は、in 演算子を使用できます。

char = 'h'
count_dict = {}
if char in count_dict:
    print(f'指定したキー {char} は、辞書 {count_dict} に存在する')
else:
    print(f'指定したキー {char} は、辞書 {count_dict} に存在しない')

この条件分岐処理に応じて、変数 count_dict のデータ更新を行います。
文字 char に対して変数 count_dict のカウントを増やす場合は count_dict[char] += 1 を実行します。
文字 char に対して変数 count_dict のカウントを 1 にする場合は count_dict[char] = 1 を実行します。

以上の処理を組み合わせることで、回答例のようなコードになります。

また、下記の分岐処理は別の方法で1つにまとめて実現することもできます。

(1) 出現した文字が変数 count_dict に既に存在する場合、カウントを増やす。
(2) 出現した文字が変数 count_dict にまだ存在しない場合、カウントを 1 として保持する。

そもそもこの分岐は、変数 count_dict のキーが存在しない場合に発生するエラーを回避するためのものでした。
キーが存在しないエラーを回避するためには、キーと値を設定すればよいのです。

つまり、出現した文字が変数 count_dict にまだ存在しない場合、カウントを 0 として保持します。
その後、出現した文字に対する変数 count_dict のカウントを増やします。

これを実現するために、辞書型オブジェクトの setdefault メソッドを使用します。
setdefault メソッドは、辞書にキーが存在しない場合にデフォルト値を設定するメソッドです。
逆に辞書にキーが存在する場合、setdefault メソッドは辞書型オブジェクトの値を変更しません。

char = 'h'
count_dict = {}
count_dict.setdefault(char, 0)  # h はキーに存在しないため、値が 0 のキー h が設定される
count_dict[char] += 1  # 指定したキーが存在するため、、エラーにならない
print(count_dict)  # {'h': 1} が表示される

count_dict.setdefault(char, 0)  # h はキーに存在するため、値は変更されない
count_dict[char] += 1  # 2回目のカウントのため、値に 2 が設定される
print(count_dict)  # {'h': 2} が表示される

結果として、次のようなコードでも目的の出現回数データを取得することができます。

target_text = 'hello'
count_dict = {}
for char in target_text:
    count_dict.setdefault(char, 0)
    count_dict[char] += 1
print(count_dict)

問題 58 リストと文字列

変数 target_list にリスト [1, 2, 3, None, 5, None, 7] を代入してください。
変数 target_listNone でない要素を抽出し、文字列 "&" で結合した文字列 "1&2&3&5&7" を出力してください。

回答例
target_list = [1, 2, 3, None, 5, None, 7]
filtered_list = [str(item) for item in target_list if item is not None]
result = '&'.join(filtered_list)
print(result)
解説

この問題では、次の2つのリスト操作を行う必要があります。

(1) リストから None でない要素を抽出する。
(2) リスト内の要素を文字列として結合する。

まず、(1) のリスト要素の抽出は、リスト内包表記を使用する方法が考えられます。
次のコードはリスト内包表記を用いて、変数 target_list から None でない要素を抽出して表示します。

target_list = [1, 2, 3, None, 5, None, 7]
filtered_list = [item for item in target_list if item is not None]
print(filtered_list)

続いて、(2) の文字列結合は、文字列型オブジェクトの join メソッドを使う方法が考えられます。
次のコードは join メソッドを用いて、変数 lst の要素を文字列 "&" で結合しようとしています。

filtered_list = [1, 2, 3, 5, 7]
result = '&'.join(filtered_list)  # ここが問題

しかし、このコードを実行すると result = '&'.join(filtered_list) の部分で次のようなエラーが発生してしまいます。

TypeError: sequence item 0: expected str instance, int found

これは、文字列型のオブジェクトを指定するべき箇所で、整数型のオブジェクトが指定されている場合に発生するエラーです。
つまり、文字列型のオブジェクトの join メソッドで指定するリストの要素は文字列型である必要があり、その条件を満たしていないのです。

上記のコードで変数 filtered_list に含まれる要素は 1, 2, 3, 5, 7 といった整数型の値となっています。
このままでは文字列型のオブジェクトの join メソッドによる結合処理が実行できません。

このような場合、変数 filtered_list に含まれる要素の型を文字列型に変換するのが良いでしょう。
リスト型の変数内の要素を全て文字列型に変換する場合、str 関数をリスト内包表記を使用する方法があります。

次のコードは、先ほどのコードに型変換の処理を追加し、エラーが発生しないように修正したものです。

filtered_list = [1, 2, 3, 5, 7]
str_list = [str(item) for item in filtered_list]
result = '&'.join(str_list)
print(result)

以上の内容を踏まえて、(1) と (2) の内容を続けて実行すると次のようになります。

target_list = [1, 2, 3, None, 5, None, 7]
filtered_list = [item for item in target_list if item is not None]
str_list = [str(item) for item in filtered_list]
result = '&'.join(str_list)
print(result)

このコードは変数 target_list を操作し、目的の文字列 "1&2&3&5&7" を出力することができます。
回答例では、2回のリスト内包表記を1行にまとめて処理しています。

問題 59 繰り返しとエラー処理

変数 lst にリスト [1, 2 , 3, None, 5] を代入してください。
変数 lst の要素を for 文で順番に取得し、要素の値をそれぞれ 2 倍して print 関数で出力してください。
ただし、tryexcept を使って型エラー TypeError を検知し、エラーが発生した場合はエラー内容を出力してください。

回答例
lst = [1, 2, 3, None, 5]
for x in lst:
    try:
        print(x * 2)
    except TypeError as e:
        print(e)
解説

次のように for 文を使用すれば、変数 lst の内容を表示することができます。

lst = [1, 2, 3, None, 5]
for x in lst:
    print(x)

問題文には「要素の値をそれぞれ 2 倍して」とあるので、繰り返し変数 x の値を 2 倍する処理を追加します。

lst = [1, 2, 3, None, 5]
for x in lst:
    print(x * 2)  # エラーが発生する

しかし、リスト lst に含まれている要素 None は数値ではないため、2 倍するという操作を行おうとすると TypeError が発生します。
そこで、この部分でエラーが発生しても処理を継続できるようにエラー処理を追加します。

ここで、try 句の位置と for 文の位置関係も重要です。
例えば、次のように for 文を含む形で try 句を記述してみましょう。

lst = [1, 2, 3, None, 5]
try:
    for x in lst:
        print(x * 2)
except TypeError as e:
    print(e)

この場合、for 文の繰り返し処理中にエラーが発生した時点で except 句の行に進みます。
except 句の行は for 文の外にあるため、その時点で for 分の繰り返し処理を抜けてしまうことになります。
つまり、今回の場合は None に対する処理中にエラーが発生し、次の 5 に対する繰り返し処理が行われないことになります。

回答例では for 文の中に try 句も except 句も含まれている状態です。
この場合、エラーが発生した場合はエラーの内容を表示し、次の繰り返し処理に進むことができます。

問題 60 辞書のリスト

次のリスト型の変数 employees から情報を取得し、location_id が "L01" または "L02" に一致するデータの idname を全て表示してください。

employees = [
    {'id': '0001', 'name': '田中', 'location_id': 'L01'},
    {'id': '0002', 'name': '山田', 'location_id': 'L02'},
    {'id': '0003', 'name': '小林', 'location_id': 'L01'},
    {'id': '0004', 'name': '藤本', 'location_id': 'L03'},
    {'id': '0005', 'name': '佐々木', 'location_id': 'L02'},
    {'id': '0006', 'name': '松田', 'location_id': 'L04'},
    {'id': '0007', 'name': '中村', 'location_id': 'L01'},
    {'id': '0008', 'name': '石川', 'location_id': 'L03'},
    {'id': '0009', 'name': '清水', 'location_id': 'L05'},
    {'id': '0010', 'name': '近藤', 'location_id': 'L02'}
]
回答例
employees = [
    {'id': '0001', 'name': '田中', 'location_id': 'L01'},
    {'id': '0002', 'name': '山田', 'location_id': 'L02'},
    {'id': '0003', 'name': '小林', 'location_id': 'L01'},
    {'id': '0004', 'name': '藤本', 'location_id': 'L03'},
    {'id': '0005', 'name': '佐々木', 'location_id': 'L02'},
    {'id': '0006', 'name': '松田', 'location_id': 'L04'},
    {'id': '0007', 'name': '中村', 'location_id': 'L01'},
    {'id': '0008', 'name': '石川', 'location_id': 'L03'},
    {'id': '0009', 'name': '清水', 'location_id': 'L05'},
    {'id': '0010', 'name': '近藤', 'location_id': 'L02'}
]

for employee in employees:
    if employee['location_id'] in {'L01', 'L02'}:
        print(employee['id'], employee['name'])
解説

この問題では、リストの中に辞書が格納された構造のデータ employees を使用します。
employee は「従業員」や「社員」を意味する英単語であり、ここでは従業員の名前と勤務地を保持するサンプルデータとなっています。
変数 employees はリストであり、その各要素は idnamelocation_id というキーを持つ辞書データです。
次のコードはリストの先頭の要素を取得し、辞書データにアクセスするものです。

employees = [
    {'id': '0001', 'name': '田中', 'location_id': 'L01'},
    {'id': '0002', 'name': '山田', 'location_id': 'L02'},
    # 中略
]
employee = employees[0]
print(employee)
print(employee['id'], employee['name'], employee['location_id'])

この問題では、リスト employees の各要素が特定の条件を満たす場合に idname を出力する必要があります。
そのため、次のような for 文を用いた処理を行うことが考えられます。

employees = [
    {'id': '0001', 'name': '田中', 'location_id': 'L01'},
    {'id': '0002', 'name': '山田', 'location_id': 'L02'},
    # 中略
]
for employee in employees:
    if True:
        print(employee['id'], employee['name'])

この時点では条件を満たすかどうかの判定を無視しています。
つまり、常に条件を満たすことを意味する True を判定条件にして、if 文を if True: と記載しています。

そして、今回の問題での判定条件は location_id が "L01" または "L02" に一致するかどうかです。
辞書型変数 employeelocation_id キーの値は、既に確認したように employee['location_id'] で取得できます。
これが "L01" または "L02" に一致するという条件は、論理演算子 or を使えば実現できます。

employees = [
    {'id': '0001', 'name': '田中', 'location_id': 'L01'},
    {'id': '0002', 'name': '山田', 'location_id': 'L02'},
    # 中略
]
for employee in employees:
    if employee['location_id'] == 'L01' or employee['location_id'] == 'L02':
        print(employee['id'], employee['name'])

また、変数の値が "L01" または "L02" に一致するという条件は、 集合 {'L01', 'L02'} に含まれると言い換えることもできます。
回答例では、集合を用いた条件分岐を行っています。

3.後半問題30問

関数

問題 61 関数の定義

呼び出されると "Hello World" と出力する関数 sample を定義し、その関数を3回呼び出してください。

回答例
def sample():
    print('Hello World')
    
sample()
sample()
sample()
解説

Python では print 関数や len 関数などの組み込み関数が最初から提供されています。
そのような組み込み関数とは別に、def 文を用いて独自に関数を定義して使用することもできます。

def 文は if 文や for 文と同様、文末にコロン : を記載し、後に続く行はインデントされる必要があります。
def 文で定義した名前の関数を呼び出すことで、定義した処理を実行することができます。

回答例では、def sample(): のように定義しているため、sample() の形で関数を呼び出すことができます。
これにより、定義した処理 print('Hello World') が実行されます。

同じ処理を何度も行う場合に、関数を使えば処理内容を1箇所に集約できます。
例えば、関数を使わずに回答例と同じ処理を行おうとすると、次のようになります。

print('Hello World')
print('Hello World')
print('Hello World')

このコードでは "Hello World" という文字列を3回 print 関数に渡しています。
一方で、関数を使った場合は "Hello World" の記述は1回で済みます。
このように、同じ処理を何度も行いたい場合、関数を用いることでコードが整理され、保守性や可読性が向上します。

また、処理を1度しか行わない場合でも、長い処理を分割して分かりやすく整理することもできます。

def main_process():
    print('ここで前処理を実行する')
    
def pre_process():
    print('ここでメイン処理を実行する')
    
def pro_process():
    print('ここで後処理を実行する')

# 前処理
main_process()

# メイン処理
pre_process()

# 後処理
pro_process()

問題 62 関数の引数

引数 name を受け取り "こんにちは、{name} さん" と出力する関数 greet を定義し、引数に "テスト" を渡して実行してください。
ただし、出力する文字列内の "{name}" は引数で受け取った文字列 name の値に置き換えてください。

回答例
def greet(name):
    print(f'こんにちは、{name} さん')
    
greet('テスト')
解説

定義する関数に引数を設定することで、呼び出す際に指定した値を関数の処理内で使用することができます。

例えば、回答例では def greet(name): のように引数 name を指定して定義しています。
この関数は greet(name) の形で呼び出すことができ、変数 name の値は呼び出し時に指定した引数が処理に使われます
今回の場合、定義した処理 print(f'こんにちは、{name} さん')name の値が "テスト" として実行されます。
結果として、"こんにちは、テスト さん" が出力されることになります。

引数を使えば、似たような処理を共通化して関数を定義することができます。
例えば、次のコードでは名前に応じた分岐処理を関数化しています。

def greet_if_not_none(name):
    if name is None:
        print('名前が指定されませんでした')
    else:
        print(f'こんにちは、{name} さん')
    
greet_if_not_none('動作確認')
greet_if_not_none('試験')
greet_if_not_none(None)

また、1つの関数に引数は複数設定することが可能です。
このとき、複数の引数を設定する場合、呼び出し時に指定する順序は定義した順序に合わせる必要があります。
例えば、次のコードでは3つの引数を持つ関数を定義しています。

def greet_three(name_1, name_2, name_3):
    print(f'こんにちは、{name_1} さん')
    print(f'こんにちは、{name_2} さん')
    print(f'こんにちは、{name_3} さん')
    
greet_three('名前1', '名前2', '名前3')

この関数の定義では3つの引数 name_1name_2name_3 がこの順序で指定されています。
呼び出し時に "名前1"、"名前2"、名前3" の順序で指定しているため、name_1='名前1'name_2='名前2'name_3='名前3' が対応します。

問題 63 関数の戻り値

引数 ab を受け取り、それらの和を返す関数 add を定義してください。
さらに、引数に 12 を渡して実行し、結果を出力してください。

回答例
def add(a, b):
    return a + b
    
x = add(1, 2)
print(x)
解説

def 文の中で return 文を使うと、関数からの戻り値を返します。
関数の呼び出し元で戻り値を取得し、その後の処理で使用できます。
return 文がない関数の戻り値は None になります。

return 文が実行された時点で、その関数の処理は終了します。
例えば、次の関数でログ出力を意図した print 関数は実行されません。

def add(a, b):
    return a + b
    print(f'a={a}, b={b}, a+b={a+b}')  # この行は処理されない
    
x = add(1, 2)
print(x)

ログ出力を行う場合は、次のように return 文の前に処理する必要があります。

def add(a, b):
    print(f'a={a}, b={b}, a+b={a+b}')
    return a + b
    
x = add(1, 2)
print(x)

引数や戻り値を設定した関数を使うことで、複雑な処理を整理することができます。
関数は次の3つの構成要素から成り立っており、どのようなシステムも関数を組み合わせて構築されているとみなすことができます。

(1) 引数: 関数に入力する値
(2) 処理: 関数が動作することで与える影響
(3) 戻り値: 関数が出力する値

問題 64 デフォルト引数

デフォルト値 "ゲスト" を設定した引数 name を持ち、"ようこそ、{name} さん" と出力する関数 welcome_message を定義してください。
定義した関数 welcome_message を次の2通りで実行してください。

(1) 引数を指定せずに呼び出す
(2) 引数に "管理者" を指定して呼び出す

回答例
def welcome_message(name='ゲスト'):
    print(f'ようこそ、{name} さん')
    
welcome_message()
welcome_message('管理者')
解説

関数の引数にはデフォルト値を設定することができます。
デフォルト値は、関数の呼び出しで引数が渡されなかった場合に使われる既定の値です。
引数にデフォルト値を設定するには、関数の定義で 引数名=デフォルト値 の形で引数を記述します。

回答例では、def welcome_message(name='ゲスト'): のように定義しています。
引数 name を指定せずにこの関数を呼び出した場合、引数 name の値は "ゲスト" として処理が行われます。
もちろん、引数を指定して関数を呼び出した場合、引数 name の値は指定した値で処理が行われます。

ただし、関数に複数の引数を設定する場合、デフォルト引数の後にデフォルト値を持たない引数を設定することはできません。
例えば、次のようにデフォルト引数が前にあると構文エラーになります。

def greet_ng(name_1='ゲスト', name_2, name_3):  # エラーが発生する
    print(f'こんにちは、{name_1} さん')
    print(f'こんにちは、{name_2} さん')
    print(f'こんにちは、{name_3} さん')

一方で、次のように3つ目の引数のみデフォルト値を指定すればエラーは発生しません。

def greet_ok_1(name_1, name_2, name_3='ゲスト'):
    print(f'こんにちは、{name_1} さん')
    print(f'こんにちは、{name_2} さん')
    print(f'こんにちは、{name_3} さん')

また、次のように全ての引数にデフォルト値を指定してもエラーは発生しません。

def greet_ok_2(name_1='ゲスト', name_2='ゲスト', name_3='ゲスト'):
    print(f'こんにちは、{name_1} さん')
    print(f'こんにちは、{name_2} さん')
    print(f'こんにちは、{name_3} さん')

このように、デフォルト値を指定した引数を関数に定義する場合、他の引数の後に配置する必要があります。
これは引数が省略されて呼び出された際に、どの引数が省略されているか判断するシステムの仕組みによるものです。

問題 65 可変長引数とキーワード引数

可変長引数 args とキーワード引数 kwargs を設定した関数 print_args を定義し、可変長引数とキーワード引数を print 関数で表示する処理を作成してください。
また、作成した関数を用いて print_args('A', 'B', key1='X', key2='Y') を実行してください。

回答例
def print_args(*args, **kwargs):
    print(args)
    print(kwargs)

print_args('A', 'B', key1='X', key2='Y')
解説

この問題では、次の2つの機能を使っています。

可変長引数: 複数の引数を指定された順序のタプル型で受け取ります。* 記号を用いて定義します。
キーワード引数: キーと値で指定された引数を辞書型で受け取ります。** 記号を用いて定義します。

回答例では、受け取った引数をそのまま print 関数で表示しており、結果は次のようになります。

('A', 'B')
{'key1': 'X', 'key2': 'Y'}

これらはタプル型と辞書型の変数として扱えるため、次のように for 文で1つずつ使用することも可能です。

def print_args(*args, **kwargs):
    for x in args:
        print(x)
    
    for key, value in kwargs.items():
        print(key, value)

print_args('A', 'B', key1='X', key2='Y')

このように、可変長引数やキーワード引数を使うことで、柔軟で再利用しやすい関数を作ることができます。

クラス

問題 66 クラスの作成

次の条件を満たすクラス SimpleClass を定義し、引数に "サンプル" を指定してオブジェクトを初期化してください。

(1) 初期化を行う際に1つの引数 name を受け取る
(2) 初期化処理で "名前が {name} のオブジェクトを作成しました" と出力する

回答例
class SimpleClass:
    def __init__(self, name):
        print(f'名前が {name} のオブジェクトを作成しました')

x = SimpleClass('サンプル')
解説

クラスは、独自のデータ構造とそれに関連する操作を定義し、新しいデータ型として利用できるようにする仕組みです。
その定義に従ってデータ型のオブジェクトを作成することができ、変数に格納して扱うことができます。

例えば、strintlist などは、Python が最初から提供している組み込みクラスです。
文字列 "text" は str クラスのオブジェクトであり、数値 1 は int クラスのオブジェクトです。
str クラスのオブジェクトは split メソッドや join メソッドなどの操作が定義されています。

このように、特定の目的でデータを保持し、それに関連した操作を定義するのがクラスです。
クラスは class 文を使って次のように定義します。

class クラス名:
    # 変数やメソッドの定義をここに記述する

定義したクラスのオブジェクトを初期化する処理は __init__ 関数で定義します

class クラス名:
    def __init__(self, 初期化引数):
        # 初期化の処理をここに記述する

__init__ 関数の先頭の引数 self は、そのクラスのオブジェクト自身を指す変数です。
初期化処理でオブジェクトのデータやメソッドにアクセスするための変数です。
厳密には self という名前でなくとも動作しますが、慣習として self とするのが一般的です。

クラスのオブジェクトを作成する際に指定する引数は、self の後で定義します。
そして、クラス名を用いて クラス名(初期化引数) の形でオブジェクトを作成することができます。
次の例はクラス Sample を定義し、初期化用の引数として name を設定しています。

class Sample:
    def __init__(self, name):
        print(name)

x = Sample('サンプル')

この例の print 関数の出力内容をf文字列で整形するように修正し、クラス名を問題文で指定されたものに変更すれば、回答例のようなクラスになります。

問題 67 メソッドの作成

前の問題で作成したクラス SimpleClass に次の2つの機能を追加してください。

(1) クラス SimpleClass のオブジェクト初期化処理において、引数 name で指定された値をインスタンス変数 name で保持する。
(2) メソッド print_name が実行されると、インスタンス変数 name の値を print 関数で出力する。

回答例
class SimpleClass:
    def __init__(self, name):
        self.name = name
        print(f'{self.name} を登録しました')

    def print_name(self):
        print(self.name)
解説

インスタンス変数とは、クラスのオブジェクトが保持するデータのことです。
そのクラスのオブジェクト自身を指す変数 self を用いて、self.変数名 の形で扱います。

例えば、次のコードではインスタンス変数 name に 引数で指定された値 name を代入しています。

class Sample:
    def __init__(self, name):
        self.name = name

この例では、インスタンス変数と引数とで同じ名前 name を使用しています。
これらは、オブジェクト自身を指す変数 self の有無で区別します。

この問題で指定された機能 (1) は、引数で指定された値をインスタンス変数で保持するというものです。
上記の例では引数で指定された値 name を インスタンス変数 self.name に代入して保持しており、(1) の機能を満たしています。

このとき、クラスの定義の外からもインスタンス変数にアクセスすることが可能です。
例えば、オブジェクト x のインスタンス変数 nameにアクセスするには、x.name のように記述します。

class Sample:
    def __init__(self, name):
        self.name = name
        
x = Sample('サンプル')
print(x.name)  # サンプル が表示される

続いて、機能 (2) で指定されたメソッド print_name を定義します。
クラスにメソッドを定義する場合、class 文の中で def 文を使用して関数を定義します。

class クラス名:
    def __init__(self, 初期化引数):
        # 初期化の処理をここに記述する
        
    def メソッド名(self, メソッド引数):
        # メソッドの処理をここに記述する

このとき、__init__ 関数もクラスに定義されたメソッドであり、同じ構文に従っています。
メソッド名が __init__ の関数が、自動的に初期化処理で呼ばれる仕組みとなっています。

クラスにメソッドを定義すると、そのクラスのオブジェクト xx.メソッド名() の形でメソッドを実行できます。
次のコードは、インスタンス変数 name の値を print 関数で出力するメソッド print_name を定義しています。

class Sample:
    def __init__(self, name):
        self.name = name
        
    def print_name(self):
        print(self.name)
        
x = Sample('サンプル')
x.print_name()  # サンプル が表示される

これにより問題文で指定された機能を実装することができました。
ここで実装した機能を前問のクラス SimpleClass に実装すれば、回答例のようになります。

また、この問題でメソッドと呼んでいるものは、厳密にはインスタンスメソッドと呼びます。
インスタンスとは、クラスで定義されたデータ型のオブジェクトのことです。
そしてインスタンスメソッドは、そのクラスのオブジェクトが使用できる関数です。

インスタンスメソッドの特徴は、オブジェクトが使用できるという点です。
そしてオブジェクトは、オブジェクトごとにインスタンス変数を保持することができます。

次の例では2つのオブジェクトを作成し、それぞれ異なるデータを保持していることを確認します。

class Sample:
    def __init__(self, name):
        self.name = name
        
    def print_name(self):
        print(self.name)
        
x = Sample('サンプル-X')
y = Sample('サンプル-Y')
x.print_name()  # サンプル-X が表示される
y.print_name()  # サンプル-Y が表示される

メソッドには、インスタンスメソッドの他にクラスメソッドやスタティックメソッドと呼ばれるものが存在します。
ただし、インスタンスメソッドのことを指して、単にメソッドと表現することも多いです。

クラスメソッドは定義したクラス自体に対して使用するメソッドで、詳しくは次の問題で扱います。
スタティックメソッドはクラスの状態と関係のないメソッドです。
基本的に通常の関数で代用できるため、スタティックメソッドの詳細は扱いません。

問題 68 クラスメソッドの作成

前の問題で作成したクラス SimpleClass に次の2つの機能を追加してください。

(1) クラス SimpleClass のインスタンスが作成された回数をクラス変数 count で保持する。
(2) クラスメソッド print_count が実行されると、クラス変数 count の値を print 関数で表示する。

回答例
class SimpleClass:
    count = 0

    def __init__(self, name):
        SimpleClass.count += 1
        self.name = name
        print(f'{self.name} を登録しました')

    @classmethod
    def print_count(cls):
        print(cls.count)

    def print_name(self):
        print(self.name)
解説

クラス変数はクラス全体で共有される変数です。
オブジェクトごとに異なるデータを保持するインスタンス変数と違い、クラスで共通した1つのデータを保持する変数です。
クラス変数は、クラスを定義する class 文内に直接記述された変数で定義します

class Sample:
    value = 'クラス変数'  # class 文内に直接記述されたクラス変数

クラスの定義内であっても、関数内に記述された変数はクラス変数として扱われません。

class Sample:
    def __init__(self):
        value = '変数'  # class 文内に直接記述されていないため、クラス変数ではない

クラス変数は全てのインスタンスで共有されており、クラス名.変数名 のように指定してアクセスすることができます。

class Sample:
    value = 'クラス変数'
    
    def __init__(self):
        print(Sample.value)  # クラス名.変数名の形でクラス変数にアクセスする

x = Sample()  # 初期化処理により、__init__ 関数内の print が実行される
print(Sample.value)  # クラス名.変数名の形でクラス変数にアクセスする

この問題で指定された機能 (1) は、インスタンスが作成された回数をクラス変数で保持するものです。
この機能を作成するため、クラス変数 count の初期値を 0 に設定し、インスタンスが作成された際にカウントアップする仕組みにします。

次のコードはインスタンスが作成された回数をクラス変数 count で保持する仕組みを実装し、動作を確認しています。

class Sample:
    count = 0
    
    def __init__(self):
        Sample.count += 1
        
print(Sample.count)  # 0 が表示される
x = Sample()  # 初期化処理により、__init__ 関数内の処理でクラス変数 count が更新される
print(Sample.count)  # 1 が表示される

続いて、機能 (2) で指定されたクラスメソッド print_count を定義します。
クラスメソッドを定義する場合、メソッド定義に @classmethod デコレータを追加します。

class クラス名:
    @classmethod
    def クラスメソッド名(cls, クラスメソッド引数):
        # クラスメソッドの処理をここに記述する

デコレータは関数やメソッドに特別な機能を追加するための仕組みです。
@classmethod デコレータは、メソッドの処理をクラス全体に関連付ける機能を追加します。

また、クラスメソッドの先頭の引数 cls は、そのクラス自身を指す変数です。
インスタンスメソッドにおいて self という名前が使われるのと同じく、クラスメソッドにおいては cls という名前を使用するのが一般的です。

クラスメソッドではクラス自身を指す変数 cls を用いて cls.クラス変数名 でクラス変数にアクセスできます。
次のコードは、クラス変数 count の値を print 関数で表示するクラスメソッド print_count を定義しています。

class Sample:
    count = 0

    @classmethod
    def print_count(cls):
        print(cls.count)
        
Sample.print_count()  # 0 が表示される

このクラスメソッドと、インスタンスが作成された回数を保持する仕組みを組み合わせると次のようになります。

class Sample:
    count = 0
    
    def __init__(self):
        Sample.count += 1

    @classmethod
    def print_count(cls):
        print(cls.count)
        
Sample.print_count()  # 0 が表示される
x = Sample()  # 初期化処理により、__init__ 関数内の処理でクラス変数 count が更新される
Sample.print_count()  # 1 が表示される

前問で作成した SimpleClass にこのような仕組みを実装すると、回答例のようなコードになります。

自作ライブラリ

問題 69 自作モジュールの使用

次のような関数 sample が定義されたモジュール import_test を作成してください。
そのモジュールを別のモジュールからインポートし、sample 関数を呼び出してください。

def sample():
    print('Hello World')
回答例
import import_test
import_test.sample()
解説

まず、問題で指定された関数 sample() を定義するモジュールを作成します。
Python では、コードを記述した Python ファイルが1つのモジュールとして機能します。
そのときのモジュール名はファイル名から定まり、拡張子 ".py" を除いた部分がモジュール名として認識されます。

今回の問題ではモジュールの内容が指定されているため、問題文にある sample() 関数の内容をそのままテキストファイルに保存します。
モジュール名は import_test と指定されているため、保存するファイルのファイル名は "import_test.py" とする必要があります。
このとき、ファイル名の末尾の拡張子 ".py" まで正しく指定する必要があります。

以上の操作で、sample 関数 が定義されたモジュールの作成は完了です。

次に、このモジュールをインポートして呼び出すためのスクリプトファイルを作成します。
モジュールをインポートするには、import キーワードを用いて import モジュール名 という処理を記述します。
この問題でインポートするモジュールは import_test となっているので、インポートの処理は次のようになります。

import import_test

この処理を実行すると、Python は指定された名前のモジュールを探して処理中のスクリプトに読み込みます。

モジュール内で定義されている関数を呼び出すには、モジュール名.関数名() のように記述します。
今回の場合、import_test モジュールの sample() 関数を呼び出すため、次のような記述を行います。

import import_test
import_test.sample()

このコードを実行すると import_test モジュールの sample 関数が動作し、print 関数によって "Hello World" が出力されるはずです。
このコードがこの問題の回答例となっています。

この問題で作成するコードはシンプルですが、実行してみると期待通りに動作しない可能性があります。
そのような場合、どのようなエラーが発生しているのかを確認して修正することになります。

よくあるエラーの原因は、インポートしようとしているファイルを Python が見つけられないというものです。

例えば、インポート対象のファイル "import_test.py" は、インポートする側のPythonスクリプトと同じフォルダに格納する必要があります。
他のフォルダに格納している場合にインポートする方法もありますが、そのための設定が必要です。

また、インポートするファイルが見つかっても、インポートに失敗してエラーが発生することもあります。
例えば、インポート対象のファイル "import_test.py" にインデントの誤りがあり構文エラーが発生することもあります。

もし期待通りに動作しない場合、出力されているエラーメッセージを確認し、エラーの原因を調査してください。

問題 70 自作ライブラリの使用

前問で作成したモジュール import_test を含むライブラリ library_test を作成してください。
そのライブラリのモジュール import_test を別のモジュールからインポートし、sample 関数を呼び出してください。

回答例
from library_test import import_test
import_test.sample()
解説

ライブラリは複数のモジュールをまとめたものであり、モジュールを含むフォルダによって構成されます。
そのときのライブラリ名はフォルダ名から定まり、フォルダ名がそのままライブラリ名として認識されます。

この問題ではライブラリ library_test を作成するため、"library_test" という名前のフォルダを作成します。
前問で作成したファイル "import_test.py" は、作成したフォルダ "library_test" 内に格納します。

以上の操作で、"library_test" フォルダを library_test ライブラリとして使用できます。
ただし、本来 Python は "_init_.py" という名前のファイルが存在するフォルダをライブラリとして扱います。
これはライブラリをインポート際の初期化処理を記述するためのファイルで、初期化処理が必要なければ空白のファイルになります。
このファイルが存在しない場合も動作しますが、空白の __init__.py ファイルを作成することを推奨します。

また、ライブラリをインポートして呼び出すためには、前問のコードを少しだけ変更する必要があります。
ライブラリからモジュールをインポートするには、from キーワードと import キーワードを用いて次のように記述します。

from ライブラリ名 import モジュール名

この問題ではライブラリ library_test からモジュール import_test をインポートするので、次のようなコードになります。

from library_test import import_test

このコードで前問と同じく import_test モジュールをインポートできます。
したがって、import_test モジュールに定義された関数 sample も同様に実行できます。

from library_test import import_test
import_test.sample()

これがこの問題の回答例となっています。

また、インポートの処理は他の書き方で記述することもできます。
次のコードは、from キーワードを使用せず、モジュール名の前にライブラリ名を指定する方法でモジュールを読み込んでいます。

import library_test.import_test
library_test.import_test.sample()

この書き方では、インポートの処理だけでなく、sample 関数呼び出しの際にもライブラリ名 library_test を指定する必要があります。

マジック変数

問題 71 Python ファイルのパス

マジック変数 __file__ を使用して、実行している Python ファイルのパスを表示してください。

回答例
print(__file__)
解説

変数 __file__ は、実行中の Python ファイルのファイルパスを表す特殊な変数です。
このような変数はマジック変数と呼ばれています。

マジック変数 __file__ を使うと、実行中の Python ファイルを基準にしたパスを作成することができます。

問題 72 実行中モジュールのパッケージ

マジック変数 __package__ を使用して、実行している Python ファイルが属しているパッケージ名を表示してください。

回答例
print(__package__)
解説

変数 __package__ は、モジュールがどのパッケージに属しているかを示すマジック変数です。
ここでパッケージとは、複数のモジュールをまとめて管理するフォルダ構造を指します。
似たような言葉にライブラリがありますが、ライブラリの方が広い概念です。
ライブラリは特定の機能を提供するモジュールやパッケージの集合を指し、ツールとしての役割に注目した概念です。

ライブラリとして他のモジュールからインポートされた場合、変数 __package__ は所属するパッケージ名になります。
一方で、スクリプトが直接実行された場合、変数 __package__None もしくは空文字列になります。

問題 73 実行中のモジュール

現在実行中の Python ファイルがメインモジュールとして直接実行されたかどうかを判定し、直接実行された場合に "直接実行" と出力してください。

回答例
if __name__ == '__main__':
    print('直接実行')
解説

変数__name__ はモジュールの名前を表すマジック変数です。
スクリプトが直接実行された場合、__name__'__main__' になります。
一方で、ライブラリとして他のモジュールからインポートされた際は、値はそのモジュール名になります。

このマジック変数 __name__ は、回答例のように直接実行された際の条件分岐のために使用されます。

標準ライブラリ

問題 74 標準ライブラリの使用

import キーワードを使って標準ライブラリの datetime モジュールをインポートしてください。
datetime モジュールの datetime クラスのクラスメソッド now を実行し、取得した現在時刻のオブジェクトを print 関数で出力してください。

回答例
import datetime
now = datetime.datetime.now()
print(now)
解説

この問題で使用する datetime モジュールは日付と時刻を扱うための標準ライブラリです。
datetime モジュールには datetime クラスが含まれています。
モジュール名とクラス名が一致しているため、混同しないように注意が必要です。

この問題では標準ライブラリのインポートを行いますが、自作ライブラリのインポートと同様に import キーワードを使用します。

import datetime

回答例では datetime.datetime でクラスを指定し、そのクラスの now メソッドを呼び出しています。
クラスメソッド now は現在のシステム日時を持ったオブジェクトを返します。

問題 75 from と import

from キーワードを使って datetime モジュールから date クラスをインポートしてください。
date クラスの today メソッドで現在日付のオブジェクトを取得し、print 関数で出力してください。

回答例
from datetime import date
today = date.today()
print(today)
解説

from キーワードを使うことで、特定のクラスや関数を指定してインポートできます。
前問のようにモジュール全体をインポートした場合は モジュール名.クラス名 という形でクラスを指定しますが、from キーワードでクラスをインポートした場合はクラス名を直接指定できます。

from datetime import date
today = date.today()  # モジュール名の指定は不要

この問題で使用する date クラスは日付を扱うクラスです。
前問で使用した datetime クラスは日付と時刻を扱いますが、date クラスの場合は時刻を扱いません。

date クラスのクラスメソッド today は、現在のシステム日付を持ったオブジェクトを返します。

問題 76 モジュールの別名インポート

as キーワードを用いて datetime モジュールを別名 dt という名前でインポートしてください。
datetime クラスのクラスメソッド now を実行し、取得した現在時刻のオブジェクトを print 関数で出力してください。

回答例
import datetime as dt
now = dt.datetime.now()
print(now)
解説

as キーワードを使うことで、インポートするモジュールに別名を付けることができます。
短く分かりやすい別名を付けることで、モジュールの指定を簡潔に記述することができます。

回答例のコードではモジュール名 datetime を別名 dt で記述しています。
これにより モジュール名.クラス名.関数名 のような長い指定を簡潔にすることができます。

ファイル操作

問題 77 ファイルの存在確認

標準ライブラリ pathlib モジュールから Path クラスをインポートしてください。
カレントディレクトリに "test.txt" というファイルが存在するかどうかを判定し、結果を True または False で出力してください。

回答例
from pathlib import Path

filepath = Path('test.txt')
print(filepath.exists())
解説

カレントディレクトリとは、現在プログラムが動作しているディレクトリを意味します。
詳細は Python スクリプトの実行方法 を参照してください。

Pythonではファイルやディレクトリのパスを扱うための標準ライブラリとして pathlib モジュールが用意されています。
また、標準モジュール os でもパスを扱うことは可能ですが、pathlibの方が可読性が高く直感的な記述が可能と言われています。

Path クラスはファイルやディレクトリのパスを扱うためのクラスで、パスに関連する便利なメソッドが多数定義されています。
この問題では Path オブジェクトの exists メソッドを使って、そのパスが実際に存在するかどうかを取得します。

Path クラスのオブジェクトを作成するには、パスを表す文字列 x を使って Path(x) と記述します。
次のコードは変数 x の部分に文字列オブジェクト 'test.txt' をそのまま指定して Path オブジェクトを作成しています。

from pathlib import Path

filepath = Path('test.txt')

この処理で取得した Path オブジェクトに exists() メソッドを使うと、そのファイルやディレクトリが存在するかを True または False で返します。
回答例では、その結果を print 関数でそのまま表示しています。

問題 78 ファイルへの書き込み

文字列 "Hello, World!" を Python でカレントディレクトリのファイル "test.txt" に書き込んでください。
ただし、同名のファイルが存在する場合、既存のテキストデータを無視して書き込んだ値で上書きしてください。

回答例
with open('test.txt', 'w') as file:
    file.write('Hello, World!')
解説

Pythonでファイルを操作するには、組み込み関数 open を使用します。
open関数は、ファイルを開いてファイルオブジェクトを返します。
次のコードはカレントディレクトリの "test.txt" を開いています。

file = open('test.txt', 'w')
file.close()

ファイルへの書き込みを行う場合、ファイル名とモードを引数に指定してopen 関数を使用します。
この例では、ファイル名 "test.txt" と書き込みモード "w" を指定しています。
設定可能なモードには次のようなものがあります。

モード 概要 説明
"r" 読み込み ファイルの内容を読み取るためのモード。ファイルが存在しない場合はエラーになります。
"w" 書き込み 新しいファイルを作成するか、既存ファイルの内容を上書きします。
"a" 追記 既存ファイルの末尾にデータを追加します。ファイルが存在しない場合は新規作成されます。
"x" 新規作成 新しいファイルを作成します。ファイルが既に存在する場合はエラーになります。

さらにプラス記号 "+" を追加すると、読み書き両方を許可するモードになります。

モード 説明
"r+" 既存のファイルを 読み書き可能 にする。ファイルが存在しない場合はエラー。
"w+" ファイルが存在しない場合は 新規作成、存在する場合は 内容を上書きし、読み書き可能 にする。
"a+" ファイルが存在しない場合は 新規作成、存在する場合は 末尾に追記しつつ、読み書き可能 にする。
"x+" 新規作成のみ許可 し、読み書きも可能。ファイルが既に存在するとエラー。

モードの指定を省略した場合、読み込み "r" として処理されます。
今回の問題のように書き込みを行う場合には、モードの指定を省略できません。

また、open関数を使用してファイルを開いた場合、処理を抜けるまでに close メソッドを呼び出してファイルを閉じる必要があります。
もしファイルが閉じられていない場合、他のプログラムからファイルにアクセスできなくなるといった問題が発生する可能性があります。

上記のコードを実行すると、カレントディレクトリの "test.txt" を開き、上書き保存して閉じます。
もしカレントディレクトリに "test.txt" が存在しなかった場合は、新規ファイルが "test.txt" という名前で作成されます。
逆に、カレントディレクトリに "test.txt" が存在した場合、空白のデータで上書きが行われます。

ファイルへの書き込み処理には、write メソッドを使用します。
次のコードは、この問題で指定された文字列 "Hello, World!" を書き込む処理を追加したものです。

file = open('test.txt', 'w')
file.write('Hello, World!')
file.close()

このコードで、この問題で指定された処理を実現することができます。
しかし、例えば write メソッドの処理中にエラーが発生した場合、上記のコードでは close メソッドが実行されず、ファイルが閉じられない可能性があります。

そこで、そのような問題を回避するため、with句を使用することが一般的です。
with 句はリソースの管理を自動化するための構文であり、open 関数と組み合わせるとファイルを自動的に閉じることができます。
with 句は if 文や try 句等と同様、文末にコロン : を記載し、後に続く行はインデントされる必要があります。

次のコードは、上記のコードを with 句を使って記述したものです。

with open('test.txt', 'w') as file:
    file.write('Hello, World!')

このコードでは、open関数で開いたファイルオブジェクトを as キーワードで指定した変数 file に割り当てています。
write メソッドを使用した書き込み処理は上記のコードと同様ですが、close メソッドは不要になります。

このコードが回答例となっています。

問題 79 ファイルへの追記

文字列 "Hello, World!" を Python でカレントディレクトリのファイル "test.txt" に書き込んでください。
ただし、同名のファイルが存在する場合は既存のテキストデータを保持し、末尾に追記する形で書き込んでください。

回答例
with open('test.txt', 'a') as file:
    file.write('Hello, World!')
解説

基本的な操作は前問と同じです。
open() 関数で追記モード "a" を指定することで、既存データを残して書き込む処理を実現できます。

回答例のコードでは、末尾に追加する文字は改行されません。
このコードを複数回実行した場合、末尾の1行に連続して追記されることに注意が必要です。

もし末尾の行を改行して、新たな行に追記を行う場合は次のようになります。

with open('test.txt', 'a') as file:
    file.write('\nHello, World!')

このコードでは、改行を意味するエスケープシーケンス '\n' を使用して改行するように指定しています。

問題 80 ファイルの読み取り

カレントディレクトリにあるファイル "test.txt" の内容を読み取り、print 関数で出力してください。
ただし、ファイルが存在しない場合は "ファイルが存在しません" と出力してください。

回答例
try:
    with open('test.txt', 'r') as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print('ファイルが存在しません')
解説

open() 関数で読み込みモード "r" を指定することで、ファイルを読み取り用に開きます。
ファイルを読み取る場合もファイルを閉じる処理が必要ですが、with 句を使えば close メソッドは不要です。

ファイルの読み取り処理は read メソッドを使用します。
次のコードはファイルから読み取ったテキストを変数 content に格納して出力しています。

with open('test.txt', 'r') as file:
    content = file.read()
    print(content)

この問題では、ファイルが存在しない場合の処理が指定されています。
ファイルの存在確認には pathlib モジュールを使用する方法もありますが、回答例ではエラー処理で実装しています。

エラー処理を使用した実装であれば、次のように他のエラーに対しても対応することが可能です。

try:
    with open('test.txt', 'r', encoding='utf-8') as file:
        content = file.read()
        print(content)

except FileNotFoundError:
    print('エラー: ファイルが存在しません。')

except PermissionError:
    print('エラー: ファイルの読み込み権限がありません。')

except UnicodeDecodeError:
    print('エラー: ファイルのエンコードが適切でないため、読み込めません。')

except Exception as e:
    print(f'予期しないエラーが発生しました: {e}')     

問題 81 ファイルの削除

カレントディレクトリにあるファイル "test.txt" を削除してください。
ただし、ファイルが存在しない場合は「ファイルが存在しません」と表示してください。

回答例
from pathlib import Path

filepath = Path('test.txt')
if filepath.exists():
    filepath.unlink()
else:
    print('ファイルが存在しません')
解説

Python でファイルを削除する方法はいくつかありますが、その1つが Path オブジェクトの unlink メソッドを使う方法です。
unlink メソッドは、該当の Path オブジェクトに対応するファイルを削除します。
ただし、フォルダを削除することはできないため、フォルダを削除する場合は別の方法を使用する必要があります。

他にも、Path オブジェクトは便利なメソッドが多数用意されています。
この問題ではファイルが存在しない場合の処理を指定されているため、Path オブジェクトの exists メソッドの結果で条件分岐しています。

問題 82 csvのリスト型書き込み

標準ライブラリの csv モジュールをインポートしてください。
次の変数 lst の情報をカレントディレクトリのテキストファイル "test.csv" にカンマで区切られたcsv形式で書き込んでください。

lst = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
回答例
import csv

lst = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

with open('test.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(lst)
解説

csv モジュールを使用してファイル書き込みを行う場合、open 関数で取得したファイルオブジェクトを指定して書き込み用オブジェクトを作成します。
csv モジュールの writer 関数は、リスト型データをファイルオブジェクトへ書き込むためのオブジェクトを作成します。

この書き込み用オブジェクトの書き込み用メソッド writerows は、2次元リストを一度にまとめて書き込みます。
今回の問題では書き込む内容が2次元リストで与えられているため、回答例では writerows メソッドを使用しています。

このとき、open 関数を使用する際の引数で newline を指定することを推奨します。
例えば、次のように newline を指定せずに open 関数を使用した場合、出力されるファイルに意図しない空行が挿入されることがあります。

import csv

lst = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

with open('test.csv', 'w') as file:
    writer = csv.writer(file)
    writer.writerows(lst)

また、書き込み用オブジェクトの書き込み用メソッド writerow メソッドは、1次元リストを指定して1行ずつ書き込むことができます。
例えば、次のコードは回答例のコードと同様の結果を得ることができます。

import csv

lst = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

with open('test.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    for row in lst:
        writer.writerow(row)

問題 83 csvのリスト型読み込み

標準ライブラリの csv モジュールをインポートしてください。
カレントディレクトリのcsv形式のテキストファイル "test.csv" を読み込み、カンマ部分で分割したリストを1行ずつ出力してください。

回答例
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)
解説

csv モジュールを使用してファイル読み込みを行う場合、open 関数で取得したファイルオブジェクトを指定して読み込み用オブジェクトを作成します。
csv モジュールの reader 関数は、ファイルオブジェクトからリスト型でテキストを読み込むためのオブジェクトを作成します。
この読み込み用オブジェクトはテキストファイルの行データを要素として順番に取り出すことができるため、for 文と組み合わせて使用することができます。

import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)

このコードでは、for 文の繰り返し処理が動く際にファイルから行データを取得する処理が動きます。
変数 reader はファイルを読み込むオブジェクトであり、テキストファイルのテキストを保持しているわけではありません。
結果として、もしファイルが非常に大きなサイズのものであっても、1行ずつ処理するため問題が発生しにくいです。

一方で、次のように読み込み用オブジェクトをリスト型の変数に変換する方法もあります。

import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    rows = list(reader)
    
for row in rows:
    print(row)

このコードでは、全てのテキスト情報をリスト型の変数で保持することになります。
この場合、ファイルサイズが大きければ、それに応じてリスト型の変数に大きな情報を格納することになります。
もし、非常に大きなファイルをリスト型の変数に格納した場合、python やその実行環境が使用できるメモリが不足して問題となる可能性があります。

とはいえ、リスト型の変数で保持することが必ずしも問題というわけではありません。
リスト型しか受け付けない関数を使用する場合や、リスト内包表記を使用する場合など、リストへの変換を行うこともあります。
想定しているファイルサイズや処理内容に応じて、最適な実装方法を選択するのが理想です。

また、読み込み用オブジェクトは open 関数で取得したオブジェクト file に依存しているため、with 句内でしかファイル読み込みを行えません。
例えば、次のように with 句の外でリスト型に変換しようとした場合、ファイル読み込みに失敗するため注意が必要です。

import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    
rows = list(reader)  # ここでエラーが発生する
for row in rows:
    print(row)

問題 84 csvの辞書型書き込み

標準ライブラリの csv モジュールをインポートしてください。
次の変数 lst の情報をカレントディレクトリのテキストファイル "test.csv" にヘッダ行を持つcsv形式で書き込んでください。

lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
回答例
import csv

lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]

with open('test.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=('id', 'name'))
    writer.writeheader()
    writer.writerows(lst)
解説

csv モジュールを使用してファイル書き込みを行う場合、open 関数で取得したファイルオブジェクトを指定して書き込み用オブジェクトを作成します。
csv モジュールの DictWriter 関数は、辞書型データをファイルオブジェクトへ書き込むためのオブジェクトを作成します。

csv モジュールの DictWriter 関数は、多くの点で writer 関数と共通しています。
ただし、 DictWriter 関数では書き込む項目の順序を定める情報として、辞書型データのキーを引数 fieldnames で指定する必要があります。

import csv

with open('test.csv', 'w', newline='') as file:
    writer_1 = csv.writer(file)  # リスト型データを書き込むオブジェクトを作成
    writer_2 = csv.DictWriter(file, fieldnames=('id', 'name'))    # 辞書型データを書き込むオブジェクトを作成

一方で、writer 関数で取得した書き込み用オブジェクトに定義されたメソッド writerowwriterowsDictWriter 関数で作成した書き込み用オブジェクトでも定義されています。
例えば、writerows メソッドを使用する場合、次のようなコードは正常に動作します。

import csv

lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]

with open('test.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=('id', 'name'))
    writer.writerows(lst)

writerow メソッドを使用する場合、次のコードも同様に動作します。

import csv

lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]

with open('test.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=('id', 'name'))
    for dct in lst:
        writer.writerow(dct)

リスト型の書き込みで使用したメソッド writerowwriterows と比較すると、指定するデータがリスト型か辞書型かの違いとなっています。

最後に、この問題では「ヘッダ行を持つcsv形式で」という条件が付いています。
DictWriter 関数で作成した書き込み用オブジェクトには、ヘッダを書き込むためのメソッドが定義されています。

import csv

with open('test.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=('id', 'name'))
    writer.writeheader()

このコードでは、引数 fieldnames で指定されていた、"id" と "name" という列情報が、writeheader メソッドによりヘッダ情報として書き込まれます。
以上を踏まえ、ヘッダ行と辞書リストを書き込む処理を実装すると回答例のようになります。

問題 85 csvの辞書型読み込み

標準ライブラリの csv モジュールをインポートしてください。
カレントディレクトリのcsv形式のテキストファイル "test.csv" を読み込み、ヘッダ行をキーとする辞書を1行ずつ出力してください。

回答例
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.DictReader(file)
    for dct in reader:
        print(dct)
解説

csv モジュールを使用してファイル読み込みを行う場合、open 関数で取得したファイルオブジェクトを指定して読み込み用オブジェクトを作成します。
csv モジュールの DictReader 関数は、ファイルオブジェクトから辞書型でテキストを読み込むためのオブジェクトを作成します。

基本的には、csv モジュールの DictReader 関数は、reader 関数とほぼ同様に使用することができます。
これにより、テキストファイルの内容をリスト型と辞書型のどちらで読み込むかを使い分けることができます。

ただし、読み込んだ結果の辞書データは、ヘッダ情報をキー、行の情報を値として持つ辞書となっています。
もしヘッダ行がないcsv形式のテキストファイルを読み込む場合、次のように引数 fieldnames で指定する必要があります。

import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.DictReader(file, fieldnames=['id', 'name'])
    for dct in reader:
        print(dct)

入力と出力

問題 86 コマンドライン引数

標準ライブラリの sys モジュールをインポートしてください。
コマンドライン引数が1つだけ指定されているか判定し、次の分岐処理を行ってください。

(1) コマンドライン引数が1つだけ指定されている場合、そのコマンドライン引数の値を print 関数で出力してください。
(2) 指定されているコマンドライン引数が1つではない場合、メッセージ "引数の数が不正です" を print 関数で出力してください。

回答例
import sys

if len(sys.argv) == 2:
    print(sys.argv[1])
else:
    print("引数の数が不正です")
解説

コマンドライン引数とは、プログラムを実行する際にコマンドラインから渡す文字情報のことです。
例えば次のように Python スクリプトファイル "script.py" を実行することで、コマンドライン引数として 'test' という文字列を渡すことができます。

python script.py test

このとき、スクリプトファイル "script.py" 側でコマンドライン引数を取得し、その値を処理に使用することが可能です。
コマンドライン引数はスペース部分で区切られたリスト sys.argv に格納されます。
ただし、sys.argv の先頭項目は実行ファイル名が格納されます。
そのため、上記の例では sys.argv は "script.py" と "test" という2つの要素からなるリストになります。

import sys
print(sys.argv)

このリスト sys.argv からコマンドライン引数で渡された "test" を取得するには、通常のリスト操作で次のように記述します。

import sys
print(sys.argv[0])  # 実行ファイル名が出力される
print(sys.argv[1])  # 1つ目のコマンドライン引数が出力される

このとき、sys.argv はスペース部分で区切られたコマンドライン引数のリストです。
例えばコマンドラインで次のように実行すると、sys.argv は2つのコマンドライン引数をもつリストになります。

python script.py test_1 test_2

このように実行した場合、sys.argv['script.py', 'test_1', 'test_2'] というリストになります。

また、sys.argv は通常のリストと同じように操作することができます。
この問題ではコマンドライン引数に指定された数で条件分岐を行うように指示されていますが、コマンドライン引数の数を調べるには len 関数が使用できます。
sys.argv の先頭項目は実行ファイル名が格納されることに注意すると、len(sys.argv) でコマンドライン引数の数より 1 だけ多い数を得ることができます。

例えば、上記の2つのコマンドライン引数を指定した例で sys.argv は3つの要素からなるリストであり、len(sys.argv)3 となります。
これを用いて条件分岐を行うと、回答例のコードになります。

問題 87 input

入力プロンプトとして "文字を入力してください" と表示してユーザが入力する値を取得し、入力された値を print 関数で表示してください。

回答例
user_input = input('文字を入力してください')
print(user_input)
解説

組み込み関数 input を使用すると、ユーザの入力操作を待つ処理が開始し、入力された文字列を取得することができます。
プログラムの処理はユーザが入力を完了するまで待機し、ユーザは Enter キーによって入力を完了することができます。

input 関数は、引数に指定された文字列を表示します。
どのような入力を受け付けているかを表示することで、ユーザはその内容に従って入力することができます。

ユーザが入力したテキストは input 関数の戻り値として取得できます。
このとき、input 関数の戻り値は常に文字列型です。
数字が入力された場合でも文字列として扱われるため、数値として扱いたい場合は int 関数や float 関数などで型変換を行う必要があります。

回答例は input 関数でユーザの入力を待ち、その結果を表示しています。

問題 88 pprint

標準ライブラリの pprint モジュールから pprint 関数をインポートしてください。
次の変数 lstprint 関数と pprint 関数でそれぞれ表示してください。

lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
回答例
from pprint import pprint
lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
print(lst)
pprint(lst)
解説

pprint モジュールの pprint 関数は、複雑なデータ構造を見やすく整形して表示します。
例えば、print 関数では2次元リストを改行せずに表示しますが、pprint 関数を使うと要素ごとに改行されて表示されます。

この問題では、print 関数の結果は次のようになります。

[{'id': '0001', 'name': 'admin'}, {'id': '0002', 'name': 'guest'}, {'id': '0003', 'name': 'test'}]

一方で、pprint 関数の結果は次のようになります。

[{'id': '0001', 'name': 'admin'},
{'id': '0002', 'name': 'guest'},
{'id': '0003', 'name': 'test'}]

この例では辞書のリストを表示していますが、2次元リストや他の複雑なデータ構造でも整形されて表示されます。

問題 89 メッセージボックス

標準ライブラリの tkinter モジュールのサブモジュール messagebox から showinfo 関数と askyesno 関数をインポートしてください。
それぞれの関数を使用して、"タイトル" と "メッセージ" 設定したメッセージウィンドウを表示してください。

回答例
from tkinter.messagebox import showinfo, askyesno
showinfo("タイトル", "メッセージ")
askyesno("タイトル", "メッセージ")
解説

tkinter はウィンドウやボタンによる GUI を作成できる標準ライブラリです。
この問題では既存の関数によるメッセージウィンドウを表示しますが、独自にカスタマイズしてウィンドウを作成することも可能です。

tkinter モジュールのサブモジュール messagebox から関数をインポートする場合、from tkinter.messagebox import 関数名 のように記述します。
複数の関数をインポートする場合、カンマ区切りで複数の関数を指定することが可能です。

from tkinter.messagebox import showinfo, askyesno

この問題で指定されている showinfo 関数と askyesno 関数は、それぞれ異なる機能をもつメッセージウィンドウを表示する関数です。
showinfo 関数は標準的な情報を通知するための関数で、選択できるボタンは "OK" のみのウィンドウを表示します。
似たような関数として、異なるアイコンでエラーや警告を通知するための showerror 関数や showwarning 関数があります。

一方で、askyesno 関数は "Yes" と "No" の2つのボタンを持つウィンドウが表示される関数です。
メッセージの内容に対してユーザがどちらのボタンをクリックしたかを取得し、その結果に応じて処理を分岐することができます。
ただし、環境によっては "Yes" と "No" ではなく、"はい" と "いいえ" などで表示されることもあります。

次のコードは、askyesno の戻り値を print 関数で出力して確認するものです。
表示されるウィンドウで "Yes" をクリックした場合は True、"No" をクリックした場合は False を取得できます。

from tkinter.messagebox import askyesno
result = askyesno("タイトル", "メッセージ")
print(result) 

ウィンドウに表示されるタイトルとメッセージは関数の引数で指定することができます。
処理の分岐をユーザに確認する際に、状況を伝えるためのタイトルやメッセージを表示することができます。
次のコードはメイン処理を実行するかどうかを確認して条件分岐する例です。

from tkinter.messagebox import showinfo, askyesno

def main():
    showinfo('メイン処理', 'メイン処理が動作します')
                
result = askyesno("実行確認", "メイン処理を開始しますか?")
if result:
    main()
else:
    showinfo('中断通知', '処理を中断します')

この問題では分岐処理についての指示がないため、回答例では関数を呼び出すだけとなっています。

問題 90 ログ出力

標準ライブラリの logging モジュールをインポートし、下記の basicConfig を実行してください。
ロガーオブジェクトを使用して、情報ログ「正常終了しました」と異常ログ「予期せぬエラーが発生しました」をログ出力してください。

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)
回答例
import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)

logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')
解説

標準ライブラリ logging モジュールはログを管理するためのライブラリです。
ログ出力を行うには、getLogger 関数を用いてログを出力するためのロガーオブジェクトを作成します。
ロガーオブジェクトを作成する際は、getLogger 関数の引数にロガーの識別名を指定することができます。
識別名は省略したり、目的に応じて名前を付けたりしても良いですが、マジック変数 __name__ を指定することが一般的です。

import logging
logger = logging.getLogger(__name__)

取得したロガーオブジェクトは、出力するログレベルに応じてメソッドを使い分けます。
ログレベルに対する使用可能なメソッドは次のとおりです。

レベル 数値 説明 メソッド呼び出し例
CRITICAL 50 重大なエラー。プログラムの継続が困難であることを意味します。 critical(ログメッセージ)
ERROR 40 エラー。処理中に問題が発生したことを意味します。 error(ログメッセージ), exception(ログメッセージ)
WARNING 30 警告。処理は続行できるが注意が必要であることを意味します。 warning(ログメッセージ)
INFO 20 情報。通常の動作に関する記録であることを意味します。 info(ログメッセージ)
DEBUG 10 デバッグ情報。開発時の詳細なログ出力であることを意味します。 debug(ログメッセージ)
ユーザ定義 任意の数値 独自に定義可能なログレベルです。 log(任意の数値, ログメッセージ)

表の上部の数値が大きいレベルは重大なログであり、逆に数値が小さいレベルは詳細な情報やデバッグ用のログとなります。

結果として、この問題で指定されている情報ログと異常ログは次のようにログ出力を行います。

import logging
 
logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')

上記のコードはログ出力の処理は実行されますが、ログが出力されない可能性があります。
これは、ログがどのように出力するかを設定していないことが原因です。

ログ出力の設定はファイル出力やファイルローテーション、ログのフィルタリングなど詳細な設定が可能です。
例えば、指定した INFO` レベル以上のログのみが出力されるようにフィルタリング設定することで、デバッグログを行わないように切り替えることができます。

また、詳細な設定が必要ない場合は basicConfig 関数を使うことで簡単にログの設定を行うことができます。
basicConfig 関数は最初の呼び出し時のみ設定が適用されるため、プログラムの先頭付近で一度だけ呼び出すのが一般的です。

この問題では basicConfig() 関数を使ったログ設定が指定されています。
指定された設定を追加した次のコードを実行すると、コンソールにログが出力されるはずです。

import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)

logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')

このコードが回答例となっています。

また、上記の表ではログレベルが ERROR の際に使用するメソッドとして errorexception の2つが記載されています。
error() メソッドは単にエラーメッセージを記録するのに対し、exception() メソッドは例外処理の中で呼び出すことでエラーに関する情報も自動でログに含めます。
つまり、エラー時にログメッセージだけをログ出力する処理は error メソッドを使用し、詳細なエラー情報をログ出力処理には exception メソッドを使用します。

例えば、次のコードは0除算エラーが発生した際のエラーハンドリングを用いて、error メソッドと exception メソッドの動作を確認しています。

import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)

logging.info("メソッドの動作確認開始")
try:
    logging.info("error メソッドの動作確認")
    result = 1 / 0
except ZeroDivisionError:
    logging.error("error メソッドによるログ出力")  # このログ出力では指定したエラーメッセージのみ出力される

try:
    logging.info("exception メソッドの動作確認")
    result = 1 / 0
except ZeroDivisionError:
    logging.exception("exception メソッドによるログ出力")  # このログ出力では詳細なエラー情報が出力される
    
logging.info("メソッドの動作確認終了")

4.実践問題10問

実践問題(後半)

問題 91 九九の計算とファイル操作

計算結果のいずれかの桁が3である九九の計算式をカレントディレクトリのテキストファイル "output.txt" に書き出してください。
作成する計算式は次のような書式とします。

1 × 3 = 3
3 × 1 = 3
4 × 8 = 32
4 × 9 = 36
...
9 × 4 = 36
9 × 7 = 63

回答例
filepath = 'output.txt'

calc_list = [
    f'{i} × {j} = {i * j}' 
    for i in range(1, 10) 
    for j in range(1, 10)
    if '3' in str(i * j)
]
calc_text = '\n'.join(calc_list)

with open(filepath, 'w') as file:
    file.write(calc_text)
解説

回答例では、改行を含むリスト内包表記を使用し、2重ループを実現しています。
リスト内包表記を使用せず、次のようなコードでも同じ結果を得ることができます。

filepath = 'output.txt'
with open(filepath, 'w') as file:
    for i in range(1, 10):
        for j in range(1, 10):
            if '3' in str(i * j):
                file.write(f'{i} × {j} = {i * j} \n')

このコードは、withforforif とインデントが深くなってしまっています。
インデントが深くなりすぎてしまうとコードが見づらくなることがあるため、必要以上に深くならないように気を付けることをお勧めします。
同じ結果を得るにも様々なコードが検討できるため、様々な選択肢を検討し適切なコードを選ぶことが理想です。

問題 92 ループと条件分岐の関数

引数で指定された範囲の正の整数の内、いずれかの桁が 3 である整数の集合を返す関数 find_numbers を作成してください。
ただし、関数 find_numbers は2つの引数 startend を持ち、start 以上 end 未満の正の整数を対象範囲として扱ってください。

回答例
def find_numbers(start, end):
    if start < 1:
        target_range = range(1, end)
    else:
        target_range = range(start, end)
    return {num for num in target_range if '3' in str(num)}
解説

問題文では引数で指定された範囲は、「start 以上 end 未満の正の整数を対象」となっています。
あまり強調されていませんが、0 以下の数が引数に指定された場合は正の整数のみが対象となるように調整する必要があります。

また、関数に整数ではなく少数(浮動小数点型)が指定された場合や、数値以外が指定された場合の挙動については詳しく指定されていません。
ここは練習問題なので細かく気にする必要はありませんが、実務では慎重に検討することをお勧めします。

問題 93 ループの終了条件

コマンドラインに "1桁の整数を入力してください" と表示し、ユーザの入力を取得してください。
ユーザが入力した値を x として、いずれかの桁が x に一致する正の整数を小さいほうから10個表示してください。
ただし、入力された値が1桁の整数でない場合は、ユーザに入力を求める処理に戻って処理を継続してください。

回答例
COUNT_MAX = 10
NUMBER_SET = {str(i) for i in range(10)}

user_input = None
while user_input not in NUMBER_SET:
    user_input = input('1桁の整数を入力してください\n')

num = 1
count = 0
while count < COUNT_MAX:
    if user_input in str(num):
        print(num)
        count += 1
    num += 1
解説

この問題では「小さいほうから10個表示」と指定されています。
しかし、この10個という数字は本質的には重要ではなく、11個でも12個でも100個でも、必要に応じて変更できる数字です。
状況によっては、次のように変更しやすくする仕様も考えられます。

  1. ユーザが入力する
  2. コマンドライン引数で指定する
  3. 設定ファイルから取得する

今回は「小さいほうから10個表示」という条件に従い、回答例では処理の先頭で COUNT_MAX という変数を使用して設定しています。
このような変数の使い方は "定数" と呼ばれ、ファイル入出力のためのファイルパスなどにも使用されます。

さらに、ユーザが入力する値は「1桁の整数」を想定しており、回答例では「1桁の整数」を定数 NUMBER_SET で管理しています。
例えば、ユーザが 'exit' と入力した場合は処理を終了する、という仕様も考えられます。
何でも定数にすれば良いというわけではないですが、必要に応じて工夫できると良いでしょう。

また、この問題では入力された値が1桁の整数でない場合、単純にユーザの入力処理に戻る仕様となっています。
もしかすると、入力された値が条件を満たしていないことをユーザに通知した方が親切な仕様かもしれません。

NUMBER_SET = {str(i) for i in range(10)}
while True:
    user_input = input('1桁の整数を入力してください\n')
    if user_input in NUMBER_SET:
        print(f'1桁の整数 {user_input} が入力されました。')
        break
    else:
        print(f'1桁の整数を入力してください。入力された値は {user_input} です。')
        user_input = input('1桁の整数を入力してください\n')

実務で勝手に仕様を変更すると危険ですが、仕様の考慮漏れや記載漏れの可能性もあります。
改善の提案が喜ばれることもあれば、仕様どおりに対応する必要があることもあるでしょう。
立場や環境に応じて、ぜひ柔軟に対応してください。

問題 94 複雑なデータ構造

次の3次元構造を持つ変数 data から、タプル内の集合の要素数が 1 であるタプルデータを出力してください。

data = [
    ('001', '001', {1, 4}),
    ('001', '002', {2, 7, 8, 9}),
    ('001', '003', {2, 3}),
    ('001', '004', {5}),
    ('001', '005', {6, 7, 8}),
    ('002', '001', {3, 7}),
    ('002', '002', {9}),
    ('002', '003', {3, 8}),  
    ('002', '004', {2}),
    ('002', '005', {3, 5}),
]
回答例
data = [
    ('001', '001', {1, 4}),
    ('001', '002', {2, 7, 8, 9}),
    ('001', '003', {2, 3}),
    ('001', '004', {5}),
    ('001', '005', {6, 7, 8}),
    ('002', '001', {3, 7}),
    ('002', '002', {9}),
    ('002', '003', {3, 8}),  
    ('002', '004', {2}),
    ('002', '005', {3, 5}),
]

filtered_data = [d for d in data if len(d[2]) == 1]

for row in filtered_data:
    print(row)
解説

この問題では、リストの中にタプル、タプルの中に集合という3次元のデータ構造を持つ変数を扱っています。
複雑なデータ構造は様々な場面で登場します。
次の例はその一部です。

  • 設定ファイルの階層構造
  • WebAPI における JSON 形式のデータ構造
  • Webスクレイピングにおける HTML 形式のデータ構造
  • 画像データの位置と色を表すデータ構造

問題 95 文字列の抽出

下記の変数 target_text に含まれる単語のうち、4 文字以内の単語を print 関数で表示してください。
ただし、単語は文章中のスペースで区切られた文字列を指し、言語上の文法は考えないものとします。

target_text = 'Python is a programming language that lets you work quickly and integrate systems more effectively'
回答例
target_text = 'Python is a programming language that lets you work quickly and integrate systems more effectively'
target_words = target_text.split()
result = [word for word in target_words if len(word) <= 4]
for word in result:
    print(word)
解説

今回の問題では「言語上の文法は考えない」という条件が指定されているため、split 関数を用いて単純なスペース区切りで処理できます。
ただし、より現実的な文章は複雑であり、実務では次のような観点を考慮する必要が出てくることもあります。

  1. 単語の抽出方法
    実際の文章には "."、"、"、"?"、"!" といった記号が含まれます。
    これだけでも、スペースで分割するだけでは単語を抽出できないことが分かります。
    さらに、日本語の場合は単語がスペースで分かれているわけでもありません。

  2. 単語の活用形
    文章の中では単語は活用した形で現れます。
    この問題の文章でも、"programming" は "program"、"lets" は "let" として抽出するとなると格段に難しくなります。

  3. 表記揺れの統一
    英語であれば大文字と小文字、日本語では平仮名と片仮名、漢字や全角半角を用いて、同じ単語を別の表記で表すことがあります。
    そのような表記の違いを無視し、同じものとして扱う必要に迫られるかもしれません。

問題 96 辞書の操作関数

引数で与えられた辞書のキーと値を逆にした辞書を返す関数 reverse_dict を作成してください。
ただし、処理に失敗した場合は None を返してください。

回答例
def reverse_dict(input_dict):
    try:
        output_dict = {v: k for k, v in input_dict.items()}
    except Exception:
        return None                
    if len(input_dict) != len(output_dict):
        return None
    return output_dict
解説

この問題では辞書内包表記を用いて関数 reverse_dict を定義します。
「処理に失敗した場合は None」という仕様であるため、エラーハンドリングを行う必要があります。

この問題では、基本的に全てのエラーを処理するという方針になります。
そのための方法の1つに、具体的なエラーを指定せず except 句を記述する方法があります。

import sys
try:
    sys.exit()
except:
    print('予期せぬエラーが発生しました')

この方法は処理可能な全ての例外に対応するため、通常は推奨されません。
もしこの方法を使用する場合、次のような例外もエラーとして処理されてしまいます。

  • Pyhton スクリプトがプログラムを終了させる際に発生させる例外 SystemExit
  • 開発者がプログラムを終了させる際に発生させる例外 KeyboardInterrupt

このように意図せずプログラムの終了を阻害する可能性があるため、具体的なエラーを指定しない方法でのエラーハンドリングは危険です。
特定の例外だけを処理したい場合は、TypeErrorAttributeError などと具体的に記述するのが安全です。

それでも予期しないエラーに対応したい場合、Exception を指定します。
この方法であれば SystemExitKeyboardInterrupt は捕捉されず、通常のエラーのみ処理できます。

try:
    raise ValueError('指定された値が不正です')
except Exception as e:
    print(f'予期せぬエラーが発生しました \n{e}')

この問題では、キーと値を逆にすることでキー重複が発生する可能性があります。
しかし、辞書内包表記で同じキーが複数回現れた場合、最後に出現した値が適用されてエラーは発生しません。
そこで回答例では、キー重複により辞書の長さが減少した場合は None とする処理を行っています。

これにより、キーと値を逆にすることに成功した場合のみ辞書を返す関数を実現できます。

def reverse_dict(input_dict):
    try:
        output_dict = {v: k for k, v in input_dict.items()}
    except Exception:
        return None                
    if len(input_dict) != len(output_dict):
        return None
    return output_dict
    
print(reverse_dict({'a': 1, 'b': 2, 'c': 3})) # キーと値を逆にした辞書を返す
print(reverse_dict({'a': 1, 'b': 2, 'c': {3}})) # 集合をキーに設定できないため None を返す
print(reverse_dict({'a': 1, 'b': 2, 'c': 1})) # 1 がキー重複するため None を返す
print(reverse_dict('not a dict')) # 辞書型でないため None を返す

問題 97 辞書を使った文字列のカウント

テキストファイルを読み込み、その中に現れる各文字の出現回数を全て表示してください。

回答例
file_path = 'output.txt'

with open(file_path, 'r') as file:
    text = file.read()

count_dict = {}
for char in text:
    if char in count_dict:
        count_dict[char] += 1
    else:
        count_dict[char] = 1
for key, value in count_dict.items():
    print(f'{key}: {value}')
解説

回答例は、テキストファイルを読み込み、各文字の出現回数を辞書オブジェクトを使って保持しています。
問題文の条件は満たしていますが、出現する文字が多い場合は出力行数が多く、出現回数を把握しづらいと言えます。
例えば、一番多い文字は何回出現しているのか、どの文字の出現回数が多いのか、といった情報がすぐに分かりません。

出現回数が一番多いものは組み込み関数 max を使うことで調べることができます。

file_path = 'output.txt'

with open(file_path, 'r') as file:
    text = file.read()

count_dict = {}
for char in text:
    if char in count_dict:
        count_dict[char] += 1
    else:
        count_dict[char] = 1
        
# 出現回数の最大値を取得
max_count = max(v for v in count_dict.values())

# 出現回数最大の文字・出現回数を抽出
max_dict = {k: v for k, v in count_dict.items() if v == max_count} 
print(max_dict)

出現回数が多い文字は、組み込み関数 sorted で出現回数順に並び変えることで調べることができます。

file_path = 'output.txt'

with open(file_path, 'r') as file:
    text = file.read()

count_dict = {}
for char in text:
    if char in count_dict:
        count_dict[char] += 1
    else:
        count_dict[char] = 1
        
# ソートしたリストを取得
sorted_dict = sorted(count_dict.items(), key=lambda item: item[1])

# ソートされたリストを出力
from pprint import pprint
pprint(sorted_dict)

ただし、このコードでは並び替えの条件として lambda item: item[1] を指定しています。
lambda は関数を作成するための機能です。

このように、この100問の練習問題では紹介できていない Python の便利な機能が多数存在します。
とはいえ、それらを全て覚える必要があるわけではありません。
その多くの機能は、基本的な知識で使いこなせるものばかりです。
まずはクラスやオブジェクト、関数やメソッドといった基本的なプログラミングの知識を身に着けることが上達の近道です。

問題 98 特定条件の文字列結合クラス

変数 target_list にリスト [1, 2, 3, None, 5, None, 7] を代入してください。
次の条件を満たすクラスを作成し、変数 target_listNone でない要素を文字列 "&" で結合した文字列 "1&2&3&5&7" を出力してください。

クラス名
StringJoiner

概要
追加されたデータを文字列として保持し、指定された区切り文字でそれらを結合する機能を提供します。

インスタンス変数

  1. items: 追加されたデータを文字列として保持するためのリストデータです。

メソッド

  1. __init__(self): インスタンス変数 items を空のリストに初期化します。
  2. append(self, item): 引数 itemNone でない場合、文字列に変換してインスタンス変数 items に追加します。
  3. join(self, delimiter): 引数で指定された文字列 delimiter でインスタンス変数 items を結合した文字列を返します。
    引数 delimiter は省略可能で、デフォルト値は空文字列です。
回答例
class StringJoiner:
    def __init__(self):
        self.items = []
        
    def append(self, item):
        if item is not None:
            self.items.append(str(item))
            
    def join(self, delimiter=''):
        return delimiter.join(self.items)

target_list = [1, 2, 3, None, 5, None, 7]
sj = StringJoiner()
for item in target_list:
    sj.append(item)
result = sj.join('&')
print(result)
解説

この問題で期待する出力文字列 "1&2&3&5&7" は、クラスを使用なくても次のようなコードで取得することが可能です。

target_list = [1, 2, 3, None, 5, None, 7]
filtered_list = [str(item) for item in target_list if item is not None]
result = '&'.join(filtered_list)
print(result)

しかし、より複雑な抽出条件や結合条件がある場合には、クラスを用いることで分かりやすいコードになることがあります。

問題 99 リストの補正と補正情報

変数 lst にリスト [1, 2 , 3, None, 5, None, 7] を代入してください。
変数 lst の要素の値が偶数でない場合は 2 倍したリスト converted_list を作成してください。
ただし、偶数であるかどうかは 2 で割った余りが 0 かどうかで判定し、判定に失敗した要素は 0 に変換してください。
このとき、変換を行った情報を次のようなリストで保持し、pprint 関数で出力してください。

[
{'index': 0, 'from': 1, 'to': 2},
{'index': 2, 'from': 3, 'to': 6},
{'index': 3, 'from': None, 'to': 0},
{'index': 4, 'from': 5, 'to': 10},
{'index': 5, 'from': None, 'to': 0},
{'index': 6, 'from': 7, 'to': 14},
]

回答例
from pprint import pprint

lst = [1, 2, 3, None, 5, None, 7]
converted_list = []
conversion_info = []

for i in range(len(lst)):
    item = lst[i]
    conversion_dict = None
    try:
        if item % 2 != 0:
            conversion_dict = {'index': i, 'from': item, 'to': item * 2}
    except Exception:
        conversion_dict = {'index': i, 'from': item, 'to': 0}
    
    if conversion_dict is None:
        converted_list.append(item)
    else:
        converted_list.append(conversion_dict['to'])
        conversion_info.append(conversion_dict)
    
pprint(conversion_info)
解説

この問題では、変数 lst を元に新たな変数 converted_list を作成します。
このとき、処理内容によっては変数 lst の値が意図せず変更されてしまいます。

lst = [1, 2, 3, None, 5, None, 7]
converted_list = lst
converted_list[0] = 2 * lst[0]  # 先頭要素の値を変更

print(converted_list)  # 期待どおり先頭要素の値が変更されている
print(lst)  # 意図せず先頭要素の値が変更されている

このコードでは、変数 converted_list の先頭要素の値を変更しています
しかし結果を確認してみると、変数 lst の先頭要素も同じように変更されていることが分かります。

これは converted_list = lst という形で代入処理を行ったことが原因です。
このような代入処理を行った場合、converted_listlst と同一のオブジェクトとして扱われることになります。

例えば、次のコードはオブジェクトが同一であるかを確認しています。

lst_1 = [1, 2, 3]  
lst_2 = [1, 2, 3]  # lst_1 と同じ値を持ったリストオブジェクト
lst_3 = lst_1      # lst_1 と同じリストオブジェクト

print(lst_1 is lst_2) # False が出力される
print(lst_1 is lst_3) # True が出力される

変数 lst_2lst_1 とは別のオブジェクトであるため、変数 lst_2 の要素を変更しても lst_1 に影響はありません。
一方で、変数 lst_3lst_1 と同一のオブジェクトであるため、変数 lst_3 の要素を変更すると lst_1 も同様に変更されます。

lst_1 = [1, 2, 3]  
lst_2 = [1, 2, 3]  # lst_1 と同じ値を持ったリストオブジェクト
lst_3 = lst_1      # lst_1 と同じリストオブジェクト

lst_2[0] = 0
print(lst_1) # 変更されていない

lst_3[0] = 0
print(lst_1) # 変更されている

これを避けるためには、新たな変数を作成する際に同一のオブジェクトとならないようにする必要があります。
その方法の1つが、リストオブジェクトの copy メソッドを使用するものです。

lst_1 = [1, 2, 3]  
lst_4 = lst_1.copy()  # lst_1 と同じ値を持ったリストオブジェクト

lst_4[0] = 0
print(lst_1) # 変更されていない
print(lst_4) # 変更されている

他にも様々な方法でこれを回避できますが、意図せずデータが変更されてしまう原因も様々です。
期待どおりの結果を得られていることと同様に、意図せずデータが書き換わっていないことも、プログラムを作成する上で大切な観点です。

問題 100 CSVファイルの調査

次のようなデータを持つcsv形式ファイル "input.csv" からデータを読み込み、operation が "OP02_010" で記録されているデータを調べてください。
該当データに現れる 'user_id' ををすべて表示してください。

user_id,operation,date,time
0001,OP00_010,2025-04-02,08:15:20
0002,OP01_010,2025-04-02,10:45:30
0003,OP00_010,2025-04-02,12:10:50
0001,OP00_010,2025-04-02,14:30:40
0001,OP00_020,2025-04-04,08:10:30
0002,OP01_010,2025-04-04,10:25:10
0003,OP02_010,2025-04-04,12:30:20
0002,OP01_010,2025-04-09,09:25:10
0004,OP02_010,2025-04-09,11:40:05
0001,OP00_010,2025-04-09,13:50:30
0001,OP00_020,2025-04-11,14:15:45
0004,OP01_010,2025-04-11,16:40:35
0005,OP02_010,2025-04-11,18:50:50
0005,OP00_010,2025-04-16,08:35:40
0006,OP00_010,2025-04-16,10:50:25
0001,OP01_010,2025-04-16,12:10:10
0003,OP01_010,2025-04-16,15:10:20
0001,OP00_010,2025-04-18,08:10:30
0002,OP01_010,2025-04-18,10:25:10
0003,OP00_010,2025-04-18,12:30:20
0002,OP01_010,2025-04-23,09:45:00
0003,OP00_010,2025-04-23,11:30:20
0001,OP00_010,2025-04-23,14:05:15
0001,OP00_020,2025-04-25,14:15:45
0004,OP01_010,2025-04-25,16:40:35
0005,OP02_010,2025-04-25,18:50:50
0001,OP01_010,2025-04-30,08:50:30
0003,OP00_010,2025-04-30,10:15:15
0003,OP00_020,2025-04-30,12:40:45
0002,OP01_010,2025-04-30,14:55:20

回答例
import csv
filepath = 'input.csv'

with open(filepath, 'r', newline='') as file:
    reader = csv.DictReader(file)
    data_list = list(reader)

data_set = {
    data_dict['user_id'] 
    for data_dict in data_list 
    if data_dict['operation'] == 'OP02_010'
}

for user_id in data_set:
    print(user_id)
解説

この問題は、システム利用ログファイルのサンプルデータを調査するものです。
ファイル読込とリスト内包表記を用いて、特定の条件を満たしたデータを抽出することができます。

実務では、関連データベースに蓄積されたデータを調査することもあります。
関連データベースはデータを蓄積し管理する点で優れた機能を持っています。
この100問の練習問題には含まれていませんが、Python から関連データベースを操作することも可能です。

38
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?