Python 入門100本ノック

Python 入門100本ノック
==========================================================

## まえがき


### 背景

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

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

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



### 対象読者

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

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



### 環境

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

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

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

#### 2. Python

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

<details>
<summary> Pythonのインストール </summary>

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

</details>





#### 3. テキストエディタ

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


<details>
<summary> Visual Studio Code のインストール </summary>

[VS Codeのダウンロードページ](https://code.visualstudio.com/)からインストールします。
拡張機能「Python」「Japanese Language Pack for Visual Studio Code」をインストールします。

</details>



#### 4. Python の実行

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


<details>
<summary> Python スクリプトの実行方法 </summary>


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

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

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

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

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

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

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

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

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

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

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

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

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


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

```cmd
python -V
```

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

> Python 3.xx.xx

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

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

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

```cmd
python3 -V
```

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

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

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

```mermaid

graph LR;
    command -- 起動 --> python
    command -. パスを指定 .-> script
    python -- 読込・実行 --> script

    subgraph current_dir[カレントディレクトリ]
        command[Python実行コマンド]
    end

    subgraph python_dir[Pythonディレクトリ]
        python[Python.exe]
    end

    subgraph script_dir[スクリプトディレクトリ]
        script[Pythonスクリプト]
    end
```

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

</details>


### 構成

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

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

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

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



### 進め方

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



<details>
<summary> Python に関する事前知識がない場合の学び方 </summary>


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

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

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


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

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

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

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

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


</details>


<details>
<summary> Python に関する事前知識がない場合の進め方 </summary>

この **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問」と同様に、コードの作成・実行と不具合発生時の調査・改善を行う。

</details>


1.前半問題50問
==========================================================

## 基本的な構文


### 問題 1 **出力（基本）**
`print` 関数を使って "Hello, World!" を出力してください。


<details>
<summary> 回答例 </summary>

```python
print('Hello, World!')
```
</details>


<details>
<summary> 解説 </summary>

`print` 関数は引数に渡したデータを表示します。
文字列の場合、シングルクォーテーション `'` とダブルクォーテーション `"` のどちらも使用できます。例えば、次のコードはどちらも正しいです。
```python
print('Hello, World!')
print("Hello, World!")
```
しかし、クォーテーションを付けていない場合はエラーとなります。
```python
print(Hello, World!)
```
一方で数値の場合、クォーテーション `'` や `"` を付けずに `print` 関数に渡すことができます。
```python
print(100)
```
文字列や数値は「データ型」や「型」などと呼ばれ、プログラミングにおける重要な概念です。
文字列として扱うか数値として扱うかによって、記述の仕方が異なります。
例えば、`100` を文字列として扱うことも可能です。
```python
print('100')
```
</details>


### 問題 2 **出力（複数行）**
`print` 関数を複数回使って、`1`、`2`、`3` を順番に出力してください。


<details>
<summary> 回答例 </summary>

```python
print(1)
print(2)
print(3)
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 3 **出力（複数項目）**
`print` 関数を使って、数値 `123` と文字列 "Hello" を一行に並べて出力してください。


<details>
<summary> 回答例 </summary>

```python
print(123, 'Hello')
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 4 **コメント**
一行コメントを使って、数値 `2` の出力を行わないように次のコードを変更してください。
```python
print(1)
print(2)
print(3)
```


<details>
<summary> 回答例 </summary>

```python
print(1)
# print(2)
print(3)
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 5 **複数行コメント**
次のコードを変更し、出力を行わないコメントに修正してください。
```python
print(1)
print(2)
print(3)
```


<details>
<summary> 回答例 </summary>

```python
"""
print(1)
print(2)
print(3)
"""
```
</details>


<details>
<summary> 解説 </summary>

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

- 開発中の一時的なコメントアウト
- 改行を含む文字列の定義
- [Docstring](https://peps.python.org/pep-0257/) と呼ばれるプログラムの説明文の記述
</details>


## 変数とデータ型


### 問題 6 **変数の宣言と使用**
変数 `x` に数値 `10` を代入し、その値を `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = 10
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 7 **数値の加算**
数値 `1` と `2` を足し算した結果を変数 `x` に代入し、その値を  `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = 1 + 2
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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


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


<details>
<summary> 回答例 </summary>

```python
x = '1' + '2'
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 9 **データ型の変換**
整数 `10` を文字列に変換して `print` 関数で出力してください。さらに、文字列 '10' を整数に変換して `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
print(str(10))
print(int('10'))
```
</details>


<details>
<summary> 解説 </summary>

`str()` 関数は入力値を文字列型に変換し、`int()` 関数は入力値を数値型に変換します。
例えば、数値 `1` と `2` を文字列として連結させて "12" を取得するには次のように記述します。
```python
a = 1
b = 2
x = str(a) + str(b)
print(x)
```
</details>


### 問題 10 **エスケープシーケンス**
改行を意味する特殊な文字列 `\n` を含む文字列 "1\n2\n3" を`print` 関数で出力し、改行されて表示されることを確認してください。


<details>
<summary> 回答例 </summary>

```python
print('1\n2\n3')
```
</details>


<details>
<summary> 解説 </summary>

回答例のように特殊な文字列 `\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） |


これらは、改行やタブといった特殊な文字を文字列内で表現するために使用されます。、
まずは意図せず変換されてしまわないように注意すると良いでしょう。
        
```python
# エスケープシーケンスの出力例
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 → スマイル絵文字
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
print('C:\\test')
```
</details>


<details>
<summary> 解説 </summary>

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

```python
print("C:\test")
```

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


### 問題 12 **r文字列の使用**
r文字列と `print` 関数を使って "C:\test" を出力してください。


<details>
<summary> 回答例 </summary>

```python
print(r'C:\test')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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


### 問題 13 **f文字列の使用**
文字列変数 `name` に "Python" を代入し、f文字列と `print` 関数を使って "Hello, Python!" を出力してください。


<details>
<summary> 回答例 </summary>

```python
name = 'Python'
print(f'Hello, {name}!')
```
</details>


<details>
<summary> 解説 </summary>

f文字列を使って波括弧 `{}` 内に変数名を書くことで、文字列の中に変数の値を埋め込むことができます。
変数は数値を指定することも可能です。例えば、次のように数値 `1` と `2` を文字列として連結させて "12" を取得することもできます。
```python
a = 1
b = 2
x = f'{a}{b}'  # 文字列 "12" が x に代入される
print(x)
```
`f文字列` という言葉の由来は formatted string literals であり、フォーマット済み文字列リテラルを意味します。
</details>


### 問題 14 **文字列の長さ**
文字列 "Python" の長さ（文字数）を `len` 関数を使って取得し、 `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = len('Python')
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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

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


### 問題 15 **部分文字列**
文字列 "Python" を変数に格納し、最初の2文字を抽出して `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = 'Python'
print(x[0:2])
```
</details>


<details>
<summary> 解説 </summary>

文字列の指定位置にある文字は、0から始まるインデックスを使って取得できます。
```python
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'
```
さらに、文字列の指定範囲にある部分文字列は、 `[開始インデックス:終了インデックス]` の形式で指定して抽出できます。
開始インデックスは抽出されますが、終了インデックスは抽出されません。
この形式では開始インデックス、終了インデックスを省略することができ、省略した場合、それぞれ先頭や末尾を意味します。
```python
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'
```
また、インデックスには負数を指定することも可能です。
負数の場合、文字列の末尾からの位置インデックスを意味します。
```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'
```

```python
s = 'Python'
print('s[2:-2]', s[2:-2])  # 'th'
print('s[-2:]', s[-2:])  # 'on'
```

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


### 問題 16 **None の使用**
変数に `None` を代入し、その値を `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = None
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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


### 問題 17 **変数のデータ型**
`type` 関数を使って、次の各変数のデータ型を確認してください。
```python
a = 10
b = 3.14
c = "Hello, Python!"
d = True
```


<details>
<summary> 回答例 </summary>

```python
a = 10
b = 3.14
c = "Hello, Python!"
d = True

print(type(a))
print(type(b))
print(type(c))
print(type(d))
```
</details>


<details>
<summary> 解説 </summary>

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

| データ型 | 説明 | 例 |
|---|---|---|
| int (整数型) | 小数点を持たない数値です。正の数、負の数、ゼロが含まれます。 | `10`, `-3`, `0` |
| float (浮動小数点型) | 小数点を含む数値です。 | `3.14`, `-0.001`, `2.0` |
| str (文字列型) | 漢字やひらがな、アルファベットや数字、記号などの文字の並びです。 | `"例１"`, `'Python'`, `"123"` |
| bool (ブール型) | 正誤を意味する `True` と `False` の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` 関数を使うことで、変数がどのデータ型であるかを調べることができます。
</details>


## リスト操作


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
print(lst)
print(lst[0])
```
</details>


<details>
<summary> 解説 </summary>

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


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
print(lst[:2])
```
</details>


<details>
<summary> 解説 </summary>

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

```python
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'
```

```python
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'
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
lst.append(4)
print(lst)
```
</details>


<details>
<summary> 解説 </summary>

`append` メソッドはリストの末尾に新しい要素を追加します。
リストオブジェクトを直接変更する処理を行い、戻り値は常に `None` です。
例えば、以下のコードはリスト自体が変更されることを示しています。
```python
lst = [1, 2, 3]
result = lst.append(4)
print(lst)    # [1, 2, 3, 4] と出力される
print(result) # None と出力される
```

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

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


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
print(len(lst))
```
</details>


<details>
<summary> 解説 </summary>

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
lst.remove(2)
print(lst)
```
</details>


<details>
<summary> 解説 </summary>

リストオブジェクトの `remove` メソッドは、指定した値と一致する最初の項目を削除します。
`append` メソッドと同様に、リストオブジェクトに定義されています。
リスト内に同一の値が複数存在していた場合、最初に一致した項目のみが削除されます。
また、一致する項目が存在しない場合 `ValueError` が発生します。
```python
lst = [1, 1, 2, 3]
lst.remove(1)
print(lst)    # [1, 2, 3] と出力される
lst.remove(1) 
print(lst)    # [2, 3] と出力される
lst.remove(1) # エラーが発生する
```
</details>


### 問題 23 **リストの結合**
２つのリスト `[1, 2]` と `[3, 4]` を結合してできるリスト `[1, 2, 3, 4]` を `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2] + [3, 4]
print(lst)
```
</details>


<details>
<summary> 解説 </summary>

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

もし `+` 演算子ではなく `append` メソッドを使用した場合、次のようにリストの中にリストが含まれる結果となります。
```python
lst = [1, 2]
lst.append([3, 4])  # [1, 2, [3, 4]] となる
print(lst)
```
</details>


## 条件分岐とループ


### 問題 24 **条件分岐（if 文）**
変数 `x` に数値 `15` を代入してください。
`if` 文を使って、変数 `x` の値が 10 より大きいかどうかを判定し、10 より大きい場合に "xは10より大きい" と出力してください。


<details>
<summary> 回答例 </summary>

```python
x = 15
if x > 10:
    print("xは10より大きい")
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

`if` 文は文末にコロン `:` を記載し、後に続く行はインデント（字下げ）される必要があります。
これは、その行が `if` 文の一部であることを示すためです。
もし次のように `if` 文の中で何も処理しないまま次の処理に進むと、インデントされていないとみなされてエラーが発生します。
```python
x = 15
if x > 10:
    # ここに処理を追加する予定
    
if x > 20:
    # ここに処理を追加する予定
```
これを回避するためには `pass` ステートメントを使用します。
次のように `pass` を使ってブロックを空にすることで、エラーを回避することができます。
```python
x = 15
if x > 10:
    # ここに処理を追加する予定
    pass
if x > 20:
    # ここに処理を追加する予定
    pass
```


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


### 問題 25 **条件分岐（elif、else）**
変数 `x` に数値 `10` を代入してください。
`elif` と `else` を使って変数 `x` の大きさを判定し、結果に応じて次のようにメッセージを出力してください。

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


<details>
<summary> 回答例 </summary>

```python
x = 10
if x > 10:
    print("xは10より大きい")
elif x > 5:
    print("xは5より大きいが、10以下")
else:
    print("xは5以下")
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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


### 問題 26 **比較演算子（大小関係）**
変数 `x` に数値 `10`、変数 `y` に数値 `20` を代入してください。
次の大小関係を確認し、それぞれの結果を `True` か `False` で出力してください。

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


<details>
<summary> 回答例 </summary>

```python
x = 10
y = 20
print(x > y)
print(x >= y)
print(x == y)
print(x <= y)
print(x < y)
```
</details>


<details>
<summary> 解説 </summary>

比較演算子は2つの値を比較し、結果を `True` または `False` で返します。
そのため、次のように比較結果を変数に代入することも可能です。
```python
x = 10
y = 20
z = x > y
print(z)  # False と表示される
```
この例では、変数 `z` に `x > y` の結果である `False` が代入されます。

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

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

次のコードは、比較演算子の結果を整理して表示します。
```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)
```
</details>


### 問題 27 **比較演算子（包含関係）**
変数 `lst` にリスト `[1, 2, 3]` を代入してください。
`in` 演算子を使って、リストに要素 `2` が含まれているか確認し、その結果を `True` か `False` で出力してください。


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
print(2 in lst)
```
</details>


<details>
<summary> 解説 </summary>

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

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

```python
print('p' in 'Python')  # False が表示される
print('P' in 'Python')  # True が表示される
```
</details>


### 問題 28 **比較演算子（オブジェクト）**
変数に `None` を代入してください。
`is` 演算子を使って、変数が `None` であるか確認し、その結果を `True` か `False` で出力してください。


<details>
<summary> 回答例 </summary>

```python
x = None
print(x is None)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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



次のコードは `if` 文で `is` 演算子を使った例です。`print` 関数の戻り値が `None` であることが分かります。
```python
x = print('print 関数の戻り値を取得します')
if x is None:
    print('print 関数の戻り値は None です')
```
</details>


### 問題 29 **論理演算子**
変数 `x` に数値 `10`、変数 `y` に数値 `20`、 を代入してください。
論理演算子 `and` や `or` を使って次の大小関係を確認し、それぞれの結果を `True` か `False` で出力してください。

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


<details>
<summary> 回答例 </summary>

```python
x = 10
y = 20
print(x > 15 and y > 15)
print(x > 15 or y > 15)
```
</details>


<details>
<summary> 解説 </summary>

`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}` | 要素がある集合 |


例えば、次のように論理演算子を使用して `True` や `False` と解釈される値を得ることができます。
```python
x = 1 and True
print(x)
```


また、`if` 文においても `True` や `False` 以外の条件を指定することも可能です。
```python
x = 'Python'
if x:
    print(x は True と解釈されました)
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
x = None
if not (x is None):
    print("x は None ではない")
```
</details>


<details>
<summary> 解説 </summary>

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

```python
x = 'Python'
if x is not None:
    print('x は None ではない')
```

```python
lst = [1, 2, 3]
if 0 not in lst:
    print('0 は lst に含まれない')
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
for item in lst:
    print(item)
```
</details>


<details>
<summary> 解説 </summary>

リストを用いた `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` 文と同様、文末にコロン `:` を記載し、後に続く行は、インデントされる必要があります。
</details>


### 問題 32 **for ループ（range）**
`range` 関数と`for` ループを使って、`0` から `9` までの数を出力してください。


<details>
<summary> 回答例 </summary>

```python
for i in range(10):
    print(i)
```
</details>


<details>
<summary> 解説 </summary>

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

もし、`0` からではなく `1` から順番に繰り返しを行いたい場合、2つの引数を指定して `range(1, n)`  のように指定することもできます。
この場合、`range(1, n)` は `1` から `n-1` までの `n-1` 個の数値を生成するオブジェクトを返します。
```python
for i in range(1, 10):
    print(i)  # 1, 2, 3,...,9 が順に出力される
```

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

これを回避するに、加算する前にリストに変換する方法があります。
```python
for i in list(range(10)) + list(range(20, 30)):
    print(i)
```

`list` 関数はリスト型に変換処理を行うため、リストとしての加算が可能となります。
型の変換が行われていることは、次のように確認できます。
```python
x = range(10)
print(type(x))  # range(10) の型を確認 → <class 'range'>

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


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


<details>
<summary> 回答例 </summary>

```python
x = 5
for i in range(10):
    if i == x:
        break
    print(i)
```
</details>


<details>
<summary> 解説 </summary>

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


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


<details>
<summary> 回答例 </summary>

```python
x = 5
for i in range(10):
    if i == x:
        continue
    print(i)
```
</details>


<details>
<summary> 解説 </summary>

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


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


<details>
<summary> 回答例 </summary>

```python
x = 10
while x > 0:
    print(x)
    x = x - 1
```
</details>


<details>
<summary> 解説 </summary>

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

`x = x - 1` は変数 `x` の値を1減らすことを意味する代入文です。  
例えば、`x` が `10` の時に `x - 1` は `9` であり、`x = x - 1` は `x` に `9` を代入します。  
同様に、`x` が `9` の時に `x - 1` は `8` であり、`x = x - 1` は `x` に `8` を代入します。

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

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


## データ構造


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


<details>
<summary> 回答例 </summary>

```python
tpl = (1, 2, 3)
print(tpl)
print(tpl[0])
```
</details>


<details>
<summary> 解説 </summary>

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

```python
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'
```

```python
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'
```

```python
tpl = ('P', 'y', 't', 'h', 'o', 'n')
print('tpl[1:-1]: ', tpl[1:-1])  #  ('y', 't', 'h', 'o')
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
tpl = (1, 2) + (3, 4)
print(tpl)
print(len(tpl))
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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

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


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

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


<details>
<summary> 回答例 </summary>

```python
st = {1, 2, 3}
st.add(1)
print(st)

st.remove(1)
print(st)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

また、集合型オブジェクトの `remove()` メソッドは指定した要素を削除します。
指定した要素が存在しない場合、`remove` メソッドはエラーとなります。
```python
st = {1, 2, 3}
st.remove(1)  # 1 が削除されて {2, 3} になる
st.remove(1)  # 削除対象の要素が存在しないためエラーが発生する
```

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

```python
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 | 「付け足す」「末尾に追加する」→ 指定した要素を末尾に追加（順序を保持し、重複チェックは行われない） | リスト型 |
</details>


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


<details>
<summary> 回答例 </summary>

```python
x = {1, 2, 3}
y = {3, 4, 5}
print(x.union(y))
print(x.intersection(y))
```
</details>


<details>
<summary> 解説 </summary>

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

また、`|` 演算子も同様の結果を得ることができます。
```python
st = {1, 2, 3} | {3, 4, 5}
print(st)  # {1, 2, 3, 4, 5} が出力される
```

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

```python
st = {1, 2, 3} & {3, 4, 5}
print(st)  # {3} が出力される
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
st = {1, 2, 3}
for x in st:
    print(x)
```
</details>


<details>
<summary> 解説 </summary>

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

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

```python
st = {1, 2, 3}
print(1 in st) # True が表示される
print(9 in st) # False が表示される
```
集合は要素の重複を許さず、順序を持たないという特徴があります。
処理したい内容や目的に応じて、リストやタプルなどの他のデータ構造から最適なものを使用できます。

もし、処理の中で他のデータ構造の機能を使いたくなった場合、データ構造を変換することも可能です。
次の例は、リストから重複要素を削除する目的で集合に変換する例です。
```python
lst = [1, 1, 2, 3]
st = set(lst)  # 集合に変換、重複要素 1 が削除される
print(st)
print(type(st))
```

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

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


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


### 問題 41 **辞書の作成と操作**
変数 `dct` に辞書 `{'id': '0001', 'name': 'guest'}` を代入してください。
変数 `dct` からキー `'name'` の値を取得して print 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
dct = {'id': '0001', 'name': 'guest'}
print(dct['name'])
```
</details>


<details>
<summary> 解説 </summary>

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

次の例では、辞書型の変数 `x` に対し、`x['key']` の形で代入と取得の両方の処理を行います。
 
```python
dct = {'id': '0001', 'name': 'guest'}
dct['key'] = 'value'
print(dct['id'], dct['name'], dct['key'])  # 0001 guest value が表示される
```


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

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


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

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

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


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


<details>
<summary> 回答例 </summary>

```python
dct = {'id': '0001', 'name': 'guest'}
for key, value in dct.items():
    print(key, value)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
even_list = [x for x in range(1, 10) if x % 2 == 0]
print(even_list)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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


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


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

```python
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倍したリストと読み替えて、次のような記述で同じ結果を得ることもできます。
```python
even_list = [2 * x for x in range(1, 5)]
print(even_list)
```
</details>


## その他の基本操作


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


<details>
<summary> 回答例 </summary>

```python
x = '1, 2, 3'
print(x.replace(',', '&'))
```
</details>


<details>
<summary> 解説 </summary>

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

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

一方で、リスト型オブジェクトの `append` メソッドや `remove` メソッドは元のリストを変更します。
```python
lst = [1, 2, 3]
lst.append(4)
print(lst)  # [1, 2, 3, 4] が出力される
```

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


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


<details>
<summary> 回答例 </summary>

```python
x = '1, 2, 3'
lst = x.split(',')
print(lst)
y = '&'.join(lst)
print(y)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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

しかし、結合対象のリストの要素は文字列型である必要があります。
次の例は、数値型の要素からなるリストを指定した場合にエラーが発生することを示しています。
```python
lst = [1, 2, 3]
'&'.join(lst)  # エラーが発生する
```

また、結合対象のリストを省略することはできません。
```python
'&'.join()  # エラーが発生する
```
</details>


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

(1) `x` と `y` を掛けた結果（積）  
(2) `x` を `y` で割った結果（小数表示）  
(3) `x` を `y` で割った整数部分  
(4) `x` を `y` で割った余り


<details>
<summary> 回答例 </summary>

```python
x = 10
y = 3
print(x * y)
print(x / y)
print(x // y)
print(x % y)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3]
print(max(lst))
```
</details>


<details>
<summary> 解説 </summary>

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


## エラー処理


### 問題 48 **エラーハンドリング**
変数 `x` に数値 `0` を代入してください。
数値 `1` を `x` で割った値を計算し、結果を `print` 関数で出力してください。  
ただし、`try` と `except` を使ってゼロ除算エラー `ZeroDivisionError` を検知し、エラーが発生した場合は "ゼロ除算エラーが発生しました" と出力してください。


<details>
<summary> 回答例 </summary>

```python
x = 0
try:
    print(1 / x)
except ZeroDivisionError:
    print('ゼロ除算エラーが発生しました')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

つまりエラーハンドリングを行わない場合、エラーが発生した箇所以降の処理が行われず、期待していた処理が実行されない可能性があります。
```python
x = 0
y = 1 / x
print('処理を終了しました')  # 前の行でエラーが発生するため、この行は処理が行われない
```

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

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

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


### 問題 49 **エラー処理**
変数 `x` に `None` を代入してください。
変数 `x` の値を `2` 倍した値を `print` 関数で出力してください。
ただし、`try` と `except` を使って型エラー `TypeError` を検知し、エラーが発生した場合はエラー内容を出力してください。


<details>
<summary> 回答例 </summary>

```python
x = None
try:
    print(x * 2)
except TypeError as e:
    print(e)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
x = -1
if x < 0:
    raise ValueError('負の値が入力されました')
```
</details>


<details>
<summary> 解説 </summary>

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






2.実践問題10問
==========================================================

## 実践問題（前半）


### 問題 51 **九九の計算表示**
２重の `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


<details>
<summary> 回答例 </summary>

```python
for i in range(1, 10):
    for j in range(1, 10):
        print(f'{i} × {j} = {i * j}')
```
</details>


<details>
<summary> 解説 </summary>

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


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

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

もちろん、f文字列を使わずに他の方法で計算式を表示する方法もあります。
```python
i = 1
j = 2
print(i, '×', j, '=', i * j)
```

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

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

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

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

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

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

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

```python
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` を変えながら同じ処理を繰り返していくことになります。

```python
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` 文で記載すると、回答例のようなコードになります。
</details>


### 問題 52 **ループと条件分岐と文字列操作**
`100` 未満の正の整数の内、いずれかの桁が `3` である数を全て表示してください。


<details>
<summary> 回答例 </summary>

```python
for i in range(1, 100):
    if '3' in str(i):
        print(i)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```python
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桁の文字を取得することになります。

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

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

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

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

```python
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` 演算子を使用して包含関係の比較を行う方針です。

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

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


### 問題 53 **ループの終了条件**
正の整数の内、いずれかの桁が `3` である数を小さいほうから100個表示してください。


<details>
<summary> 回答例 </summary>

```python
num = 1
count = 0
while count < 100:
    if '3' in str(num):
        print(num)
        count += 1
    num += 1
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

```python
num = 1
count = 0
```

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

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

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

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

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in list_2d:
    for item in row:
        print(item * 2)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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


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

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

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

```python
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` 文を使う方法と組み合わせて使用することも可能です。

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

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

```python
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` 倍する処理を追加します。
回答例では２重の `for` 文を使用し、取得した数値要素を `2` 倍して出力しています。
</details>


### 問題 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']


<details>
<summary> 回答例 </summary>

```python
list_2d = [[f'{x}-{y}' for y in range(10)] for x in range(10)]
print(list_2d)
```
</details>


<details>
<summary> 解説 </summary>

この問題では、要素としてリストを含むリストを作成する必要があります。
通常のリスト内包表記はリストを作成する機能です。
この問題のように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}'` と記述できます。

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


```python
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" の形の文字列になっています。
 そのため、先頭のリストと同様に考えることで、次のリスト内包表記で作成できることが分かります。
 
```python
row = [f'1-{y}' for y in range(10)] 
print(row)
```

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
ascii_dict = {'0x30': '0', '0x40': '@', '0x50': 'P'}
reversed_dict = {value: key for key, value in ascii_dict.items()}
print(reversed_dict)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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


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

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

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

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


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

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


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


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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


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

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

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

```python
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` 演算子を使用できます。

```python
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` メソッドは辞書型オブジェクトの値を変更しません。

```python
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} が表示される
```

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

```python
target_text = 'hello'
count_dict = {}
for char in target_text:
    count_dict.setdefault(char, 0)
    count_dict[char] += 1
print(count_dict)
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

```python
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` の要素を文字列 "&" で結合しようとしています。

```python
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` 関数をリスト内包表記を使用する方法があります。

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

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

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

```python
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行にまとめて処理しています。
</details>


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


<details>
<summary> 回答例 </summary>

```python
lst = [1, 2, 3, None, 5]
for x in lst:
    try:
        print(x * 2)
    except TypeError as e:
        print(e)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

ここで、`try` 句の位置と `for` 文の位置関係も重要です。
例えば、次のように `for` 文を含む形で `try` 句を記述してみましょう。
        
```python
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` 句も含まれている状態です。
この場合、エラーが発生した場合はエラーの内容を表示し、次の繰り返し処理に進むことができます。
</details>


### 問題 60 **辞書のリスト**
次のリスト型の変数 `employees` から情報を取得し、`location_id` が "L01" または "L02" に一致するデータの `id` と `name` を全て表示してください。

```python
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'}
]
```


<details>
<summary> 回答例 </summary>

```python
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'])
```
</details>


<details>
<summary> 解説 </summary>

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

```python
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` の各要素が特定の条件を満たす場合に `id` と `name` を出力する必要があります。
そのため、次のような `for` 文を用いた処理を行うことが考えられます。

```python
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" に一致するかどうかです。
辞書型変数 `employee` の `location_id` キーの値は、既に確認したように `employee['location_id']` で取得できます。
これが "L01" または "L02" に一致するという条件は、論理演算子 `or` を使えば実現できます。


```python
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'}` に含まれると言い換えることもできます。
回答例では、集合を用いた条件分岐を行っています。
</details>



3.後半問題30問
==========================================================

## 関数


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


<details>
<summary> 回答例 </summary>

```python
def sample():
    print('Hello World')
    
sample()
sample()
sample()
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

```python
print('Hello World')
print('Hello World')
print('Hello World')
```

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

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

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

# 前処理
main_process()

# メイン処理
pre_process()

# 後処理
pro_process()
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
def greet(name):
    print(f'こんにちは、{name} さん')
    
greet('テスト')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

```python
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つの引数を持つ関数を定義しています。

```python
def greet_three(name_1, name_2, name_3):
    print(f'こんにちは、{name_1} さん')
    print(f'こんにちは、{name_2} さん')
    print(f'こんにちは、{name_3} さん')
    
greet_three('名前１', '名前２', '名前３')
```

この関数の定義では3つの引数 `name_1`、`name_2`、`name_3` がこの順序で指定されています。
呼び出し時に "名前１"、"名前２"、名前３" の順序で指定しているため、`name_1='名前１'`、`name_2='名前２'`、`name_3='名前３'` が対応します。
</details>


### 問題 63 **関数の戻り値**
引数 `a` と `b` を受け取り、それらの和を返す関数 `add` を定義してください。
さらに、引数に `1` と `2` を渡して実行し、結果を出力してください。


<details>
<summary> 回答例 </summary>

```python
def add(a, b):
    return a + b
    
x = add(1, 2)
print(x)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

```python
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) 戻り値： 関数が出力する値
</details>


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

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


<details>
<summary> 回答例 </summary>

```python
def welcome_message(name='ゲスト'):
    print(f'ようこそ、{name} さん')
    
welcome_message()
welcome_message('管理者')
```
</details>


<details>
<summary> 解説 </summary>

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

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


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

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
def print_args(*args, **kwargs):
    print(args)
    print(kwargs)

print_args('A', 'B', key1='X', key2='Y')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

```python
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')
```

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


## クラス


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

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


<details>
<summary> 回答例 </summary>

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

x = SimpleClass('サンプル')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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


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

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

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

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

x = Sample('サンプル')
```

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


### 問題 67 **メソッドの作成**
前の問題で作成したクラス `SimpleClass` に次の２つの機能を追加してください。

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


<details>
<summary> 回答例 </summary>

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

    def print_name(self):
        print(self.name)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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


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


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

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

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

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

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

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

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


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


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

```python
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 が表示される
```

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

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


### 問題 68 **クラスメソッドの作成**
前の問題で作成したクラス `SimpleClass` に次の２つの機能を追加してください。

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


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

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

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

 クラスの定義内であっても、関数内に記述された変数はクラス変数として扱われません。
 
```python
class Sample:
    def __init__(self):
        value = '変数'  # class 文内に直接記述されていないため、クラス変数ではない
```


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


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

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


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

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

```python
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` デコレータを追加します。

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

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

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

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

```python
class Sample:
    count = 0

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

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


```python
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` にこのような仕組みを実装すると、回答例のようなコードになります。
</details>


## 自作ライブラリ


### 問題 69 **自作モジュールの使用**
次のような関数 `sample` が定義されたモジュール `import_test` を作成してください。
そのモジュールを別のモジュールからインポートし、`sample` 関数を呼び出してください。
```python
def sample():
    print('Hello World')
```


<details>
<summary> 回答例 </summary>

```python
import import_test
import_test.sample()
```
</details>


<details>
<summary> 解説 </summary>

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


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

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

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

```python
import import_test
```

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

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

```python
import import_test
import_test.sample()
```

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
from library_test import import_test
import_test.sample()
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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

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

```python
from library_test import import_test
```

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


```python
from library_test import import_test
import_test.sample()
```

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

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

```python
import library_test.import_test
library_test.import_test.sample()
```

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


## マジック変数


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


<details>
<summary> 回答例 </summary>

```python
print(__file__)
```
</details>


<details>
<summary> 解説 </summary>

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

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


### 問題 72 **実行中モジュールのパッケージ**
マジック変数 `__package__` を使用して、実行している Python ファイルが属しているパッケージ名を表示してください。


<details>
<summary> 回答例 </summary>

```python
print(__package__)
```
</details>


<details>
<summary> 解説 </summary>

変数 `__package__` は、モジュールがどのパッケージに属しているかを示すマジック変数です。
ここでパッケージとは、複数のモジュールをまとめて管理するフォルダ構造を指します。
似たような言葉にライブラリがありますが、ライブラリの方が広い概念です。
ライブラリは特定の機能を提供するモジュールやパッケージの集合を指し、ツールとしての役割に注目した概念です。
        
ライブラリとして他のモジュールからインポートされた場合、変数 `__package__` は所属するパッケージ名になります。
一方で、スクリプトが直接実行された場合、変数 `__package__` は `None` もしくは空文字列になります。
</details>


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


<details>
<summary> 回答例 </summary>

```python
if __name__ == '__main__':
    print('直接実行')
```
</details>


<details>
<summary> 解説 </summary>

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

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


## 標準ライブラリ


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


<details>
<summary> 回答例 </summary>

```python
import datetime
now = datetime.datetime.now()
print(now)
```
</details>


<details>
<summary> 解説 </summary>

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

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

```python
import datetime
```

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


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


<details>
<summary> 回答例 </summary>

```python
from datetime import date
today = date.today()
print(today)
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
import datetime as dt
now = dt.datetime.now()
print(now)
```
</details>


<details>
<summary> 解説 </summary>

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

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


## ファイル操作


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


<details>
<summary> 回答例 </summary>

```python
from pathlib import Path

filepath = Path('test.txt')
print(filepath.exists())
```
</details>


<details>
<summary> 解説 </summary>

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


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

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

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

```python
from pathlib import Path

filepath = Path('test.txt')
```

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


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


<details>
<summary> 回答例 </summary>

```python
with open('test.txt', 'w') as file:
    file.write('Hello, World!')
```
</details>


<details>
<summary> 解説 </summary>

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

```python
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!" を書き込む処理を追加したものです。

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

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

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
with open('test.txt', 'a') as file:
    file.write('Hello, World!')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

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


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


<details>
<summary> 回答例 </summary>

```python
try:
    with open('test.txt', 'r') as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print('ファイルが存在しません')
```
</details>


<details>
<summary> 解説 </summary>

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

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

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

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

エラー処理を使用した実装であれば、次のように他のエラーに対しても対応することが可能です。
        
```python
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}')     
```
</details>


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


<details>
<summary> 回答例 </summary>

```python
from pathlib import Path

filepath = Path('test.txt')
if filepath.exists():
    filepath.unlink()
else:
    print('ファイルが存在しません')
```
</details>


<details>
<summary> 解説 </summary>

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

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


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

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


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

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

この書き込み用オブジェクトの書き込み用メソッド `writerows` は、2次元リストを一度にまとめて書き込みます。
今回の問題では書き込む内容が2次元リストで与えられているため、回答例では `writerows` メソッドを使用しています。

このとき、`open` 関数を使用する際の引数で `newline` を指定することを推奨します。
例えば、次のように `newline` を指定せずに `open` 関数を使用した場合、出力されるファイルに意図しない空行が挿入されることがあります。

```python
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行ずつ書き込むことができます。
例えば、次のコードは回答例のコードと同様の結果を得ることができます。

```python
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)
```
</details>


### 問題 83 **csvのリスト型読み込み**
標準ライブラリの `csv` モジュールをインポートしてください。
カレントディレクトリのcsv形式のテキストファイル "test.csv" を読み込み、カンマ部分で分割したリストを1行ずつ出力してください。


<details>
<summary> 回答例 </summary>

```python
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)
```
</details>


<details>
<summary> 解説 </summary>

`csv` モジュールを使用してファイル読み込みを行う場合、`open` 関数で取得したファイルオブジェクトを指定して読み込み用オブジェクトを作成します。
`csv` モジュールの `reader` 関数は、ファイルオブジェクトからリスト型でテキストを読み込むためのオブジェクトを作成します。
この読み込み用オブジェクトはテキストファイルの行データを要素として順番に取り出すことができるため、`for` 文と組み合わせて使用することができます。

```python
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)
```

このコードでは、`for` 文の繰り返し処理が動く際にファイルから行データを取得する処理が動きます。
変数 `reader` はファイルを読み込むオブジェクトであり、テキストファイルのテキストを保持しているわけではありません。
結果として、もしファイルが非常に大きなサイズのものであっても、1行ずつ処理するため問題が発生しにくいです。

一方で、次のように読み込み用オブジェクトをリスト型の変数に変換する方法もあります。

```python
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` 句の外でリスト型に変換しようとした場合、ファイル読み込みに失敗するため注意が必要です。

```python
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.reader(file)
    
rows = list(reader)  # ここでエラーが発生する
for row in rows:
    print(row)
```
</details>


### 問題 84 **csvの辞書型書き込み**
標準ライブラリの `csv` モジュールをインポートしてください。
次の変数 `lst` の情報をカレントディレクトリのテキストファイル "test.csv" にヘッダ行を持つcsv形式で書き込んでください。

```python
lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
```


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

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

`csv` モジュールの `DictWriter` 関数は、多くの点で `writer` 関数と共通しています。
ただし、 `DictWriter` 関数では書き込む項目の順序を定める情報として、辞書型データのキーを引数 `fieldnames` で指定する必要があります。
        
```python
import csv

with open('test.csv', 'w', newline='') as file:
    writer_1 = csv.writer(file)  # リスト型データを書き込むオブジェクトを作成
    writer_2 = csv.DictWriter(file, fieldnames=('id', 'name'))    # 辞書型データを書き込むオブジェクトを作成
```

一方で、`writer` 関数で取得した書き込み用オブジェクトに定義されたメソッド `writerow` や `writerows` は `DictWriter` 関数で作成した書き込み用オブジェクトでも定義されています。
例えば、`writerows` メソッドを使用する場合、次のようなコードは正常に動作します。


```python
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` メソッドを使用する場合、次のコードも同様に動作します。

```python
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)
```

リスト型の書き込みで使用したメソッド `writerow` や `writerows` と比較すると、指定するデータがリスト型か辞書型かの違いとなっています。


最後に、この問題では「ヘッダ行を持つcsv形式で」という条件が付いています。
`DictWriter` 関数で作成した書き込み用オブジェクトには、ヘッダを書き込むためのメソッドが定義されています。

```python
import csv

with open('test.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=('id', 'name'))
    writer.writeheader()
```

このコードでは、引数 `fieldnames` で指定されていた、"id" と "name" という列情報が、`writeheader` メソッドによりヘッダ情報として書き込まれます。
以上を踏まえ、ヘッダ行と辞書リストを書き込む処理を実装すると回答例のようになります。
</details>


### 問題 85 **csvの辞書型読み込み**
標準ライブラリの `csv` モジュールをインポートしてください。
カレントディレクトリのcsv形式のテキストファイル "test.csv" を読み込み、ヘッダ行をキーとする辞書を1行ずつ出力してください。


<details>
<summary> 回答例 </summary>

```python
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.DictReader(file)
    for dct in reader:
        print(dct)
```
</details>


<details>
<summary> 解説 </summary>

`csv` モジュールを使用してファイル読み込みを行う場合、`open` 関数で取得したファイルオブジェクトを指定して読み込み用オブジェクトを作成します。
`csv` モジュールの `DictReader` 関数は、ファイルオブジェクトから辞書型でテキストを読み込むためのオブジェクトを作成します。

基本的には、`csv` モジュールの `DictReader` 関数は、`reader` 関数とほぼ同様に使用することができます。
これにより、テキストファイルの内容をリスト型と辞書型のどちらで読み込むかを使い分けることができます。

ただし、読み込んだ結果の辞書データは、ヘッダ情報をキー、行の情報を値として持つ辞書となっています。
もしヘッダ行がないcsv形式のテキストファイルを読み込む場合、次のように引数 `fieldnames` で指定する必要があります。

```python
import csv

with open('test.csv', 'r', newline='') as file:
    reader = csv.DictReader(file, fieldnames=['id', 'name'])
    for dct in reader:
        print(dct)
```
</details>


## 入力と出力


### 問題 86 **コマンドライン引数**
標準ライブラリの `sys` モジュールをインポートしてください。
コマンドライン引数が1つだけ指定されているか判定し、次の分岐処理を行ってください。

(1) コマンドライン引数が1つだけ指定されている場合、そのコマンドライン引数の値を `print` 関数で出力してください。  
(2) 指定されているコマンドライン引数が1つではない場合、メッセージ "引数の数が不正です" を `print` 関数で出力してください。


<details>
<summary> 回答例 </summary>

```python
import sys

if len(sys.argv) == 2:
    print(sys.argv[1])
else:
    print("引数の数が不正です")
```
</details>


<details>
<summary> 解説 </summary>

コマンドライン引数とは、プログラムを実行する際にコマンドラインから渡す文字情報のことです。
例えば次のように Python スクリプトファイル "script.py" を実行することで、コマンドライン引数として `'test'` という文字列を渡すことができます。

```cmd
python script.py test
```

このとき、スクリプトファイル "script.py" 側でコマンドライン引数を取得し、その値を処理に使用することが可能です。
コマンドライン引数はスペース部分で区切られたリスト `sys.argv` に格納されます。
ただし、`sys.argv` の先頭項目は実行ファイル名が格納されます。
そのため、上記の例では `sys.argv` は "script.py" と "test" という2つの要素からなるリストになります。

```python
import sys
print(sys.argv)
```

このリスト `sys.argv` からコマンドライン引数で渡された "test" を取得するには、通常のリスト操作で次のように記述します。

```python
import sys
print(sys.argv[0])  # 実行ファイル名が出力される
print(sys.argv[1])  # 1つ目のコマンドライン引数が出力される
```

このとき、`sys.argv` はスペース部分で区切られたコマンドライン引数のリストです。
例えばコマンドラインで次のように実行すると、`sys.argv` は2つのコマンドライン引数をもつリストになります。

```cmd
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` となります。
これを用いて条件分岐を行うと、回答例のコードになります。
</details>


### 問題 87 **input**
入力プロンプトとして "文字を入力してください" と表示してユーザが入力する値を取得し、入力された値を `print` 関数で表示してください。


<details>
<summary> 回答例 </summary>

```python
user_input = input('文字を入力してください')
print(user_input)
```
</details>


<details>
<summary> 解説 </summary>

組み込み関数 `input` を使用すると、ユーザの入力操作を待つ処理が開始し、入力された文字列を取得することができます。
プログラムの処理はユーザが入力を完了するまで待機し、ユーザは `Enter` キーによって入力を完了することができます。

`input` 関数は、引数に指定された文字列を表示します。
どのような入力を受け付けているかを表示することで、ユーザはその内容に従って入力することができます。

ユーザが入力したテキストは `input` 関数の戻り値として取得できます。
このとき、`input` 関数の戻り値は常に文字列型です。
数字が入力された場合でも文字列として扱われるため、数値として扱いたい場合は `int` 関数や `float` 関数などで型変換を行う必要があります。

回答例は `input` 関数でユーザの入力を待ち、その結果を表示しています。
</details>


### 問題 88 **pprint**
標準ライブラリの `pprint` モジュールから `pprint` 関数をインポートしてください。
次の変数 `lst` を `print` 関数と `pprint` 関数でそれぞれ表示してください。

```python
lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
```


<details>
<summary> 回答例 </summary>

```python
from pprint import pprint
lst = [
    {'id': '0001', 'name': 'admin'},
    {'id': '0002', 'name': 'guest'},
    {'id': '0003', 'name': 'test'},
]
print(lst)
pprint(lst)
```
</details>


<details>
<summary> 解説 </summary>

`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次元リストや他の複雑なデータ構造でも整形されて表示されます。
</details>


### 問題 89 **メッセージボックス**
標準ライブラリの `tkinter` モジュールのサブモジュール `messagebox` から `showinfo` 関数と `askyesno` 関数をインポートしてください。
それぞれの関数を使用して、"タイトル" と "メッセージ" 設定したメッセージウィンドウを表示してください。


<details>
<summary> 回答例 </summary>

```python
from tkinter.messagebox import showinfo, askyesno
showinfo("タイトル", "メッセージ")
askyesno("タイトル", "メッセージ")
```
</details>


<details>
<summary> 解説 </summary>

`tkinter` はウィンドウやボタンによる GUI を作成できる標準ライブラリです。
この問題では既存の関数によるメッセージウィンドウを表示しますが、独自にカスタマイズしてウィンドウを作成することも可能です。

`tkinter` モジュールのサブモジュール `messagebox` から関数をインポートする場合、`from tkinter.messagebox import 関数名` のように記述します。
複数の関数をインポートする場合、カンマ区切りで複数の関数を指定することが可能です。

```python
from tkinter.messagebox import showinfo, askyesno
```

この問題で指定されている `showinfo` 関数と `askyesno` 関数は、それぞれ異なる機能をもつメッセージウィンドウを表示する関数です。
 `showinfo` 関数は標準的な情報を通知するための関数で、選択できるボタンは "OK" のみのウィンドウを表示します。
 似たような関数として、異なるアイコンでエラーや警告を通知するための `showerror` 関数や `showwarning` 関数があります。

一方で、`askyesno` 関数は "Yes" と "No" の2つのボタンを持つウィンドウが表示される関数です。
メッセージの内容に対してユーザがどちらのボタンをクリックしたかを取得し、その結果に応じて処理を分岐することができます。
ただし、環境によっては "Yes" と "No" ではなく、"はい" と "いいえ" などで表示されることもあります。
        
次のコードは、`askyesno` の戻り値を `print` 関数で出力して確認するものです。
表示されるウィンドウで "Yes" をクリックした場合は `True`、"No" をクリックした場合は `False` を取得できます。

```python
from tkinter.messagebox import askyesno
result = askyesno("タイトル", "メッセージ")
print(result) 
```

ウィンドウに表示されるタイトルとメッセージは関数の引数で指定することができます。
処理の分岐をユーザに確認する際に、状況を伝えるためのタイトルやメッセージを表示することができます。
次のコードはメイン処理を実行するかどうかを確認して条件分岐する例です。

```python
from tkinter.messagebox import showinfo, askyesno

def main():
    showinfo('メイン処理', 'メイン処理が動作します')
                
result = askyesno("実行確認", "メイン処理を開始しますか？")
if result:
    main()
else:
    showinfo('中断通知', '処理を中断します')
```

この問題では分岐処理についての指示がないため、回答例では関数を呼び出すだけとなっています。
</details>


### 問題 90 **ログ出力**
標準ライブラリの `logging` モジュールをインポートし、下記の `basicConfig` を実行してください。
ロガーオブジェクトを使用して、情報ログ「正常終了しました」と異常ログ「予期せぬエラーが発生しました」をログ出力してください。

```python
logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)
```


<details>
<summary> 回答例 </summary>

```python
import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)

logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')
```
</details>


<details>
<summary> 解説 </summary>

標準ライブラリ `logging` モジュールはログを管理するためのライブラリです。
ログ出力を行うには、`getLogger` 関数を用いてログを出力するためのロガーオブジェクトを作成します。
ロガーオブジェクトを作成する際は、`getLogger` 関数の引数にロガーの識別名を指定することができます。
識別名は省略したり、目的に応じて名前を付けたりしても良いですが、マジック変数 `__name__` を指定することが一般的です。

```python
import logging
logger = logging.getLogger(__name__)
```

取得したロガーオブジェクトは、出力するログレベルに応じてメソッドを使い分けます。
ログレベルに対する使用可能なメソッドは次のとおりです。


| レベル | 数値 | 説明 | メソッド呼び出し例 |
|------|------|------|------|
| `CRITICAL` | 50 | 重大なエラー。プログラムの継続が困難であることを意味します。 | `critical(ログメッセージ)` |
| `ERROR` | 40 | エラー。処理中に問題が発生したことを意味します。 | `error(ログメッセージ)`, `exception(ログメッセージ)` |
| `WARNING` | 30 | 警告。処理は続行できるが注意が必要であることを意味します。 | `warning(ログメッセージ)` |
| `INFO` | 20 | 情報。通常の動作に関する記録であることを意味します。| `info(ログメッセージ)` |
| `DEBUG` | 10 | デバッグ情報。開発時の詳細なログ出力であることを意味します。 | `debug(ログメッセージ)` |
| ユーザ定義 | 任意の数値 | 独自に定義可能なログレベルです。 | `log(任意の数値, ログメッセージ)` |


表の上部の数値が大きいレベルは重大なログであり、逆に数値が小さいレベルは詳細な情報やデバッグ用のログとなります。


結果として、この問題で指定されている情報ログと異常ログは次のようにログ出力を行います。
```python
import logging
 
logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')
```

上記のコードはログ出力の処理は実行されますが、ログが出力されない可能性があります。
これは、ログがどのように出力するかを設定していないことが原因です。

ログ出力の設定はファイル出力やファイルローテーション、ログのフィルタリングなど詳細な設定が可能です。
例えば、指定した INFO` レベル以上のログのみが出力されるようにフィルタリング設定することで、デバッグログを行わないように切り替えることができます。

また、詳細な設定が必要ない場合は `basicConfig` 関数を使うことで簡単にログの設定を行うことができます。
`basicConfig` 関数は最初の呼び出し時のみ設定が適用されるため、プログラムの先頭付近で一度だけ呼び出すのが一般的です。
 
この問題では `basicConfig()` 関数を使ったログ設定が指定されています。
指定された設定を追加した次のコードを実行すると、コンソールにログが出力されるはずです。

```python
import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s][%(levelname)-5s] %(message)s',
)

logger = logging.getLogger(__name__)
logger.info('正常終了しました')
logger.error('予期せぬエラーが発生しました')
```

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


また、上記の表ではログレベルが `ERROR` の際に使用するメソッドとして `error` と `exception` の2つが記載されています。
`error()` メソッドは単にエラーメッセージを記録するのに対し、`exception()` メソッドは例外処理の中で呼び出すことでエラーに関する情報も自動でログに含めます。
つまり、エラー時にログメッセージだけをログ出力する処理は `error` メソッドを使用し、詳細なエラー情報をログ出力処理には `exception` メソッドを使用します。

例えば、次のコードは0除算エラーが発生した際のエラーハンドリングを用いて、`error` メソッドと `exception` メソッドの動作を確認しています。


```python
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("メソッドの動作確認終了")
```
</details>






4.実践問題10問
==========================================================

## 実践問題（後半）


### 問題 91 **九九の計算とファイル操作**
計算結果のいずれかの桁が3である九九の計算式をカレントディレクトリのテキストファイル "output.txt" に書き出してください。
作成する計算式は次のような書式とします。
> 1 × 3 = 3  
> 3 × 1 = 3  
> 4 × 8 = 32  
> 4 × 9 = 36  
> ...  
> 9 × 4 = 36  
> 9 × 7 = 63


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

回答例では、改行を含むリスト内包表記を使用し、2重ループを実現しています。
リスト内包表記を使用せず、次のようなコードでも同じ結果を得ることができます。

```python
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')
```

このコードは、`with`、`for`、`for`、`if` とインデントが深くなってしまっています。
インデントが深くなりすぎてしまうとコードが見づらくなることがあるため、必要以上に深くならないように気を付けることをお勧めします。
同じ結果を得るにも様々なコードが検討できるため、様々な選択肢を検討し適切なコードを選ぶことが理想です。
</details>


### 問題 92 **ループと条件分岐の関数**
引数で指定された範囲の正の整数の内、いずれかの桁が `3` である整数の集合を返す関数 `find_numbers` を作成してください。
ただし、関数 `find_numbers` は2つの引数 `start` と `end` を持ち、`start` 以上 `end` 未満の正の整数を対象範囲として扱ってください。


<details>
<summary> 回答例 </summary>

```python
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)}
```
</details>


<details>
<summary> 解説 </summary>

問題文では引数で指定された範囲は、「`start` 以上 `end` 未満の正の整数を対象」となっています。
あまり強調されていませんが、`0` 以下の数が引数に指定された場合は正の整数のみが対象となるように調整する必要があります。

また、関数に整数ではなく少数（浮動小数点型）が指定された場合や、数値以外が指定された場合の挙動については詳しく指定されていません。
ここは練習問題なので細かく気にする必要はありませんが、実務では慎重に検討することをお勧めします。
</details>


### 問題 93 **ループの終了条件**
コマンドラインに "1桁の整数を入力してください" と表示し、ユーザの入力を取得してください。
ユーザが入力した値を `x` として、いずれかの桁が `x` に一致する正の整数を小さいほうから10個表示してください。
ただし、入力された値が1桁の整数でない場合は、ユーザに入力を求める処理に戻って処理を継続してください。


<details>
<summary> 回答例 </summary>

```python
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
```
</details>


<details>
<summary> 解説 </summary>

この問題では「小さいほうから10個表示」と指定されています。
しかし、この10個という数字は本質的には重要ではなく、11個でも12個でも100個でも、必要に応じて変更できる数字です。
状況によっては、次のように変更しやすくする仕様も考えられます。

1. ユーザが入力する  
2. コマンドライン引数で指定する  
3. 設定ファイルから取得する

今回は「小さいほうから10個表示」という条件に従い、回答例では処理の先頭で `COUNT_MAX` という変数を使用して設定しています。
このような変数の使い方は "定数" と呼ばれ、ファイル入出力のためのファイルパスなどにも使用されます。

さらに、ユーザが入力する値は「1桁の整数」を想定しており、回答例では「1桁の整数」を定数 `NUMBER_SET` で管理しています。
例えば、ユーザが `'exit'` と入力した場合は処理を終了する、という仕様も考えられます。
何でも定数にすれば良いというわけではないですが、必要に応じて工夫できると良いでしょう。

また、この問題では入力された値が1桁の整数でない場合、単純にユーザの入力処理に戻る仕様となっています。
もしかすると、入力された値が条件を満たしていないことをユーザに通知した方が親切な仕様かもしれません。

```python
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')
```

実務で勝手に仕様を変更すると危険ですが、仕様の考慮漏れや記載漏れの可能性もあります。
改善の提案が喜ばれることもあれば、仕様どおりに対応する必要があることもあるでしょう。
立場や環境に応じて、ぜひ柔軟に対応してください。
</details>


### 問題 94 **複雑なデータ構造**
次の3次元構造を持つ変数 `data` から、タプル内の集合の要素数が `1` であるタプルデータを出力してください。

```python
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}),
]
```


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

この問題では、リストの中にタプル、タプルの中に集合という3次元のデータ構造を持つ変数を扱っています。
複雑なデータ構造は様々な場面で登場します。
次の例はその一部です。

- 設定ファイルの階層構造
- WebAPI における JSON 形式のデータ構造
- Webスクレイピングにおける HTML 形式のデータ構造
- 画像データの位置と色を表すデータ構造
</details>


### 問題 95 **文字列の抽出**
下記の変数 `target_text` に含まれる単語のうち、`4` 文字以内の単語を `print` 関数で表示してください。
ただし、単語は文章中のスペースで区切られた文字列を指し、言語上の文法は考えないものとします。

```python
target_text = 'Python is a programming language that lets you work quickly and integrate systems more effectively'
```


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

今回の問題では「言語上の文法は考えない」という条件が指定されているため、`split` 関数を用いて単純なスペース区切りで処理できます。
ただし、より現実的な文章は複雑であり、実務では次のような観点を考慮する必要が出てくることもあります。

1. 単語の抽出方法
実際の文章には "."、"、"、"?"、"!" といった記号が含まれます。
これだけでも、スペースで分割するだけでは単語を抽出できないことが分かります。
さらに、日本語の場合は単語がスペースで分かれているわけでもありません。

2. 単語の活用形
文章の中では単語は活用した形で現れます。
この問題の文章でも、"programming" は "program"、"lets" は "let" として抽出するとなると格段に難しくなります。

3. 表記揺れの統一
英語であれば大文字と小文字、日本語では平仮名と片仮名、漢字や全角半角を用いて、同じ単語を別の表記で表すことがあります。
そのような表記の違いを無視し、同じものとして扱う必要に迫られるかもしれません。
</details>


### 問題 96 **辞書の操作関数**
引数で与えられた辞書のキーと値を逆にした辞書を返す関数 `reverse_dict` を作成してください。
ただし、処理に失敗した場合は `None` を返してください。


<details>
<summary> 回答例 </summary>

```python
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
```
</details>


<details>
<summary> 解説 </summary>

この問題では辞書内包表記を用いて関数 `reverse_dict` を定義します。
「処理に失敗した場合は `None`」という仕様であるため、エラーハンドリングを行う必要があります。

この問題では、基本的に全てのエラーを処理するという方針になります。
そのための方法の1つに、具体的なエラーを指定せず `except` 句を記述する方法があります。

```python
import sys
try:
    sys.exit()
except:
    print('予期せぬエラーが発生しました')
```

この方法は処理可能な全ての例外に対応するため、通常は推奨されません。
もしこの方法を使用する場合、次のような例外もエラーとして処理されてしまいます。

- Pyhton スクリプトがプログラムを終了させる際に発生させる例外 `SystemExit`  
- 開発者がプログラムを終了させる際に発生させる例外 `KeyboardInterrupt`

このように意図せずプログラムの終了を阻害する可能性があるため、具体的なエラーを指定しない方法でのエラーハンドリングは危険です。
特定の例外だけを処理したい場合は、`TypeError` や `AttributeError` などと具体的に記述するのが安全です。

それでも予期しないエラーに対応したい場合、`Exception` を指定します。
この方法であれば `SystemExit` や `KeyboardInterrupt` は捕捉されず、通常のエラーのみ処理できます。

```python
try:
    raise ValueError('指定された値が不正です')
except Exception as e:
    print(f'予期せぬエラーが発生しました \n{e}')
```

この問題では、キーと値を逆にすることでキー重複が発生する可能性があります。
しかし、辞書内包表記で同じキーが複数回現れた場合、最後に出現した値が適用されてエラーは発生しません。
そこで回答例では、キー重複により辞書の長さが減少した場合は `None` とする処理を行っています。

これにより、キーと値を逆にすることに成功した場合のみ辞書を返す関数を実現できます。

```python
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 を返す
```
</details>


### 問題 97 **辞書を使った文字列のカウント**
テキストファイルを読み込み、その中に現れる各文字の出現回数を全て表示してください。


<details>
<summary> 回答例 </summary>

```python
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} 回')
```
</details>


<details>
<summary> 解説 </summary>

回答例は、テキストファイルを読み込み、各文字の出現回数を辞書オブジェクトを使って保持しています。
問題文の条件は満たしていますが、出現する文字が多い場合は出力行数が多く、出現回数を把握しづらいと言えます。
例えば、一番多い文字は何回出現しているのか、どの文字の出現回数が多いのか、といった情報がすぐに分かりません。

出現回数が一番多いものは組み込み関数 `max` を使うことで調べることができます。

```python
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` で出現回数順に並び変えることで調べることができます。

```python
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 の便利な機能が多数存在します。
とはいえ、それらを全て覚える必要があるわけではありません。
その多くの機能は、基本的な知識で使いこなせるものばかりです。
まずはクラスやオブジェクト、関数やメソッドといった基本的なプログラミングの知識を身に着けることが上達の近道です。
</details>


### 問題 98 **特定条件の文字列結合クラス**
変数 `target_list` にリスト `[1, 2, 3, None, 5, None, 7]` を代入してください。
次の条件を満たすクラスを作成し、変数 `target_list` の `None` でない要素を文字列 "&" で結合した文字列 "1&2&3&5&7" を出力してください。

**クラス名**：  
`StringJoiner`

**概要**：  
追加されたデータを文字列として保持し、指定された区切り文字でそれらを結合する機能を提供します。

**インスタンス変数**：  
1. `items`: 追加されたデータを文字列として保持するためのリストデータです。

**メソッド**：  
1. `__init__(self)`: インスタンス変数 `items` を空のリストに初期化します。  
2. `append(self, item)`: 引数 `item` が `None` でない場合、文字列に変換してインスタンス変数 `items` に追加します。  
3. `join(self, delimiter)`: 引数で指定された文字列 `delimiter` でインスタンス変数 `items` を結合した文字列を返します。
引数 `delimiter` は省略可能で、デフォルト値は空文字列です。


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

この問題で期待する出力文字列  "1&2&3&5&7" は、クラスを使用なくても次のようなコードで取得することが可能です。

```python
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)
```

しかし、より複雑な抽出条件や結合条件がある場合には、クラスを用いることで分かりやすいコードになることがあります。
</details>


### 問題 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},  
> ]


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

この問題では、変数 `lst` を元に新たな変数 `converted_list` を作成します。
このとき、処理内容によっては変数 `lst` の値が意図せず変更されてしまいます。

```python
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_list` は `lst` と同一のオブジェクトとして扱われることになります。

例えば、次のコードはオブジェクトが同一であるかを確認しています。

```python
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_2` は `lst_1` とは別のオブジェクトであるため、変数 `lst_2` の要素を変更しても `lst_1` に影響はありません。
一方で、変数 `lst_3` は `lst_1` と同一のオブジェクトであるため、変数 `lst_3` の要素を変更すると `lst_1` も同様に変更されます。


```python
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) # 変更されている
```

```mermaid
graph LR;
    subgraph lst_1
        lst_1-0["lst_1[0]"]
        lst_1-1["lst_1[1]"]
        lst_1-2["lst_1[2]"]
    end

    subgraph lst_2
        lst_2-0["lst_2[0]"]
        lst_2-1["lst_2[1]"]
        lst_2-2["lst_2[2]"]
    end

    subgraph lst_3
        direction LR
        lst_3-0["lst_3[0]"]
        lst_3-1["lst_3[1]"]
        lst_3-2["lst_3[2]"]
    end

    subgraph memory["メモリ"]
        lst_ref_1-0[1]
        lst_ref_1-1[2]
        lst_ref_1-2[3]
        lst_ref_2-0[1]
        lst_ref_2-1[2]
        lst_ref_2-2[3]
    end

    lst_1-0 -- 参照 --> lst_ref_1-0
    lst_1-1 -- 参照 --> lst_ref_1-1
    lst_1-2 -- 参照 --> lst_ref_1-2
    
    lst_3 <== 同一オブジェクト ==> lst_1
    lst_3 ~~~ lst_2
    
    lst_2-0 -- 参照 --> lst_ref_2-0
    lst_2-1 -- 参照 --> lst_ref_2-1
    lst_2-2 -- 参照 --> lst_ref_2-2
```


これを避けるためには、新たな変数を作成する際に同一のオブジェクトとならないようにする必要があります。
その方法の1つが、リストオブジェクトの `copy` メソッドを使用するものです。

```python
lst_1 = [1, 2, 3]  
lst_4 = lst_1.copy()  # lst_1 と同じ値を持ったリストオブジェクト

lst_4[0] = 0
print(lst_1) # 変更されていない
print(lst_4) # 変更されている
```

他にも様々な方法でこれを回避できますが、意図せずデータが変更されてしまう原因も様々です。
期待どおりの結果を得られていることと同様に、意図せずデータが書き換わっていないことも、プログラムを作成する上で大切な観点です。
</details>


### 問題 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


<details>
<summary> 回答例 </summary>

```python
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)
```
</details>


<details>
<summary> 解説 </summary>

この問題は、システム利用ログファイルのサンプルデータを調査するものです。
ファイル読込とリスト内包表記を用いて、特定の条件を満たしたデータを抽出することができます。

実務では、関連データベースに蓄積されたデータを調査することもあります。
関連データベースはデータを蓄積し管理する点で優れた機能を持っています。
この100問の練習問題には含まれていませんが、Python から関連データベースを操作することも可能です。
</details>