diff --git a/app/sidebar.tsx b/app/sidebar.tsx index 91ed57e..eda32e1 100644 --- a/app/sidebar.tsx +++ b/app/sidebar.tsx @@ -8,17 +8,35 @@ export function Sidebar() { {/* サイドバーが常時表示されている場合のみ */} Navbar Title - +
  • + 4. 制御構文と関数 +
  • +
  • + 5. モジュールとパッケージ +
  • +
  • + 6. オブジェクト指向プログラミング +
  • +
  • + 7. ファイルの入出力とコンテキストマネージャ +
  • +
  • + 8. 例外処理 +
  • +
  • + 9. ジェネレータとデコレータ +
  • + ); } diff --git a/public/docs/python-4.md b/public/docs/python-4.md new file mode 100644 index 0000000..4cbdc94 --- /dev/null +++ b/public/docs/python-4.md @@ -0,0 +1,204 @@ +# 第4章: 制御構文と関数:Pythonらしい書き方 + +この章では、Pythonの基本的な制御構文(条件分岐、ループ)と関数の定義方法について学びます。他の言語にも同様の機能はありますが、特に`for`ループの振る舞いや、柔軟な引数の渡し方はPythonの大きな特徴です。これらの「Pythonらしい」書き方をマスターすることで、より簡潔で読みやすいコードを書けるようになります。 + +## if/elif/elseによる条件分岐 + +Pythonの条件分岐は`if`、`elif`(else ifの略)、`else`を使って記述します。C言語やJavaのような波括弧`{}`は使わず、**コロン`:`とインデント(通常は半角スペース4つ)**でコードブロックを表現するのが最大の特徴です。 + +```python +>>> score = 85 +>>> if score >= 90: +... print('優') +... elif score >= 80: +... print('良') +... elif score >= 70: +... print('可') +... else: +... print('不可') +... +良 +``` + +条件式に`and`や`or`、`not`といった論理演算子も使用できます。 + +```python +>>> temp = 25 +>>> is_sunny = True +>>> if temp > 20 and is_sunny: +... print("お出かけ日和です") +... +お出かけ日和です +``` + +## forループとrange()、enumerate() + +Pythonの`for`ループは、他の言語の`for (int i = 0; i < 5; i++)`といったカウンタ変数を使うスタイルとは少し異なります。リストやタプル、文字列などの**イテラブル(反復可能)オブジェクト**から要素を1つずつ取り出して処理を実行します。これは、Javaの拡張for文やC\#の`foreach`に似ています。 + +```python +>>> fruits = ['apple', 'banana', 'cherry'] +>>> for fruit in fruits: +... print(f"I like {fruit}") +... +I like apple +I like banana +I like cherry +``` + +### `range()` 関数 + +決まった回数のループを実行したい場合は、`range()`関数が便利です。`range(n)`は0からn-1までの連続した数値を生成します。 + +```python +>>> for i in range(5): +... print(i) +... +0 +1 +2 +3 +4 +``` + +### `enumerate()` 関数 + +ループ処理の中で、要素のインデックス(番号)と値の両方を使いたい場合があります。そのような時は`enumerate()`関数を使うと、コードが非常にスッキリします。これは非常にPythonらしい書き方の一つです。 + +```python +>>> fruits = ['apple', 'banana', 'cherry'] +>>> for i, fruit in enumerate(fruits): +... print(f"Index: {i}, Value: {fruit}") +... +Index: 0, Value: apple +Index: 1, Value: banana +Index: 2, Value: cherry +``` + +## whileループ + +`while`ループは、指定された条件が`True`である間、処理を繰り返します。ループを途中で抜けたい場合は`break`を、現在の回の処理をスキップして次の回に進みたい場合は`continue`を使用します。 + +```python +>>> n = 0 +>>> while n < 5: +... print(n) +... n += 1 +... +0 +1 +2 +3 +4 +``` + +## 関数の定義 (def) + +関数は`def`キーワードを使って定義します。ここでもコードブロックはコロン`:`とインデントで示します。値は`return`キーワードで返します。 + +```python +>>> def greet(name): +... """指定された名前で挨拶を返す関数""" # これはDocstringと呼ばれるドキュメント文字列です +... return f"Hello, {name}!" +... +>>> message = greet("Alice") +>>> print(message) +Hello, Alice! +``` + +## 引数の渡し方(位置引数、キーワード引数、デフォルト引数値) + +Pythonの関数は、非常に柔軟な引数の渡し方ができます。 + +* **位置引数 (Positional Arguments):** 最も基本的な渡し方で、定義された順序で値を渡します。 +* **キーワード引数 (Keyword Arguments):** `引数名=値`の形式で渡します。順序を問わないため、可読性が向上します。 +* **デフォルト引数値 (Default Argument Values):** 関数を定義する際に引数にデフォルト値を設定できます。呼び出し時にその引数が省略されると、デフォルト値が使われます。 + +```python +>>> def describe_pet(animal_type, pet_name, owner_name="Taro"): +... print(f"私には {animal_type} がいます。") +... print(f"名前は {pet_name} で、飼い主は {owner_name} です。") +... + +# 位置引数のみで呼び出し +>>> describe_pet("ハムスター", "ジャンボ") +私には ハムスター がいます。 +名前は ジャンボ で、飼い主は Taro です。 + +# キーワード引数で呼び出し(順序を逆にしてもOK) +>>> describe_pet(pet_name="ポチ", animal_type="犬") +私には 犬 がいます。 +名前は ポチ で、飼い主は Taro です。 + +# デフォルト引数を持つ引数を指定して呼び出し +>>> describe_pet("猫", "ミケ", "Hanako") +私には 猫 がいます。 +名前は ミケ で、飼い主は Hanako です。 +``` + +**注意点:** デフォルト引数を持つ引数は、持たない引数の後に定義する必要があります。 + +## 可変長引数 (\*args, \*\*kwargs) + +関数の引数の数が可変である場合に対応するための仕組みです。 + +### `*args` + +任意の数の**位置引数**をタプルとして受け取ります。慣習的に`args`という名前が使われます。 + +```python +>>> def sum_all(*numbers): +... print(f"受け取ったタプル: {numbers}") +... total = 0 +... for num in numbers: +... total += num +... return total +... +>>> print(sum_all(1, 2, 3)) +受け取ったタプル: (1, 2, 3) +6 +>>> print(sum_all(10, 20, 30, 40, 50)) +受け取ったタプル: (10, 20, 30, 40, 50) +150 +``` + +### `**kwargs` + +任意の数の**キーワード引数**を辞書として受け取ります。慣習的に`kwargs` (keyword arguments) という名前が使われます。 + +```python +>>> def print_profile(**user_info): +... print(f"受け取った辞書: {user_info}") +... for key, value in user_info.items(): +... print(f"{key}: {value}") +... +>>> print_profile(name="Sato", age=28, city="Tokyo") +受け取った辞書: {'name': 'Sato', 'age': 28, 'city': 'Tokyo'} +name: Sato +age: 28 +city: Tokyo +``` + +## ラムダ式(Lambda expressions) + +`lambda`キーワードを使うと、名前のない小さな**無名関数**を定義できます。複雑な処理には向きませんが、`sorted`関数のキーを指定したり、GUIのコールバック関数を定義したりと、簡単な処理をその場で記述したい場合に非常に便利です。 + +構文: `lambda 引数: 式` + +```python +# 通常の関数で2つの数を足す +>>> def add(x, y): +... return x + y +... + +# ラムダ式で同じ処理を定義 +>>> add_lambda = lambda x, y: x + y +>>> print(add_lambda(3, 5)) +8 + +# sorted関数のキーとして利用する例 +>>> students = [('Taro', 80), ('Jiro', 95), ('Saburo', 75)] +# 成績(タプルの2番目の要素)でソートする +>>> sorted_students = sorted(students, key=lambda student: student[1], reverse=True) +>>> print(sorted_students) +[('Jiro', 95), ('Taro', 80), ('Saburo', 75)] +``` diff --git a/public/docs/python-5.md b/public/docs/python-5.md new file mode 100644 index 0000000..324ac6d --- /dev/null +++ b/public/docs/python-5.md @@ -0,0 +1,292 @@ +# 第5章: コードの整理術:モジュールとパッケージ + +プログラミングを進めていくと、コードは必然的に長くなり、一つのファイルで管理するのが難しくなってきます。機能ごとにファイルを分割し、再利用しやすく、メンテナンスしやすい構造にすることが、効率的な開発の鍵となります。この章では、Pythonでコードを整理するための**モジュール**と**パッケージ**という仕組み、そしてPythonの強力な武器である**標準ライブラリ**の活用方法について学びます。 + + +## REPLからスクリプトファイルへ + +これまでの章では、コマンドを1行ずつ入力してすぐに結果が返ってくる**REPL (対話型実行環境)** を使ってきました。REPLは、ちょっとしたコードの動作確認や学習には非常に便利です。しかし、REPLを終了すると入力したコードは消えてしまいますし、複雑なプログラムを作るのにも向いていません。 + +実際の開発では、プログラムを**スクリプトファイル**に保存して実行するのが一般的です。 + +### Pythonファイルの作成と実行 + +1. **ファイルの作成**: + お使いのテキストエディタ(VS Code、サクラエディタ、メモ帳など何でも構いません)を開き、以下のコードを記述してください。そして、`hello.py` という名前で保存します。ファイルの拡張子は必ず `.py` にしてください。 + + ```python + # hello.py + message = "Hello, Python Script!" + print(message) + print(f"2 + 3 = {2 + 3}") + ``` + +2. **ファイルの実行**: + 次に、ターミナル(WindowsではコマンドプロンプトやPowerShell)を開き、`cd` コマンドで `hello.py` を保存したディレクトリに移動します。そして、以下のコマンドを実行してください。 + + ```bash + # python3コマンドの場合もあります + python hello.py + ``` + + すると、画面に以下のように出力されるはずです。 + + ``` + Hello, Python Script! + 2 + 3 = 5 + ``` + +このように、`python [ファイル名]` というコマンドで、ファイルに書かれたコードを上から順に実行することができます。これからは、このファイルベースでの開発を基本として進めていきましょう。 + + +## モジュール:コードを部品化する + +さて、ここからが本題です。Pythonでは、先ほど作成した **`hello.py` のような `.py` ファイルが1つのモジュール**として扱われます。モジュールを使うことで、関連する関数やクラスを一つのファイルにまとめ、他のプログラムから再利用可能な「部品」として扱うことができます。これは、他の言語におけるライブラリやソースファイルのインポート機能に似ています。 + +### `import`文の基本 + +モジュールを利用するには `import` 文を使います。Pythonには多くの便利なモジュールが標準で用意されています(これらを**標準ライブラリ**と呼びます)。例えば、数学的な計算を行う `math` モジュールをREPLで使ってみましょう。 + +```python +>>> # mathモジュールをインポート +>>> import math +>>> +>>> # mathモジュール内の変数や関数を利用する +>>> print(math.pi) +3.141592653589793 +>>> print(math.sqrt(16)) +4.0 +``` + +毎回 `math.` と書くのが面倒な場合は、いくつかの書き方があります。 + + * **`from ... import ...`**: モジュールから特定の関数や変数だけを取り込む + + ```python + >>> from math import pi, sqrt + >>> + >>> print(pi) # 直接piを参照できる + 3.141592653589793 + >>> print(sqrt(16)) # 直接sqrtを参照できる + 4.0 + ``` + + * **`as` (別名)**: モジュールに別名をつけて利用する + + ```python + >>> import math as m + >>> + >>> print(m.pi) + 3.141592653589793 + >>> print(m.sqrt(16)) + 4.0 + ``` + +> **注意** ⚠️: `from math import *` のようにアスタリスク (`*`) を使うと、そのモジュールのすべての名前(関数、変数、クラス)が現在の名前空間にインポートされます。一見便利に見えますが、どの名前がどこから来たのか分からなくなり、意図しない名前の上書きを引き起こす可能性があるため、**特別な理由がない限り避けるべき**です。 + +### 自作モジュールの作成と利用 + +自分でモジュールを作成するのも簡単です。関連する関数をまとめた `.py` ファイルを作成するだけです。ここからは複数のファイルが必要になるため、再びスクリプトファイルで説明します。 + +1. **`utils.py` の作成**: + まず、便利な関数をまとめた `utils.py` というファイルを作成します。 + + ```python + # utils.py + + def say_hello(name): + """指定された名前で挨拶を返す""" + return f"Hello, {name}!" + + def get_list_average(numbers): + """数値リストの平均を計算する""" + if not numbers: + return 0 + return sum(numbers) / len(numbers) + + # このファイルが直接実行された場合にのみ以下のコードを実行 + if __name__ == "__main__": + print("This is a utility module.") + print(say_hello("World")) + avg = get_list_average([10, 20, 30]) + print(f"Average: {avg}") + ``` + + > **`if __name__ == "__main__":` の重要性** + > この記述はPythonの定型句です。 + + > * スクリプトが**直接実行された**場合、そのスクリプトの `__name__` という特殊変数は `"__main__"` になります。 + > * スクリプトが**モジュールとして `import` された**場合、`__name__` はファイル名(この場合は `"utils"`)になります。 + > これにより、モジュールとしてインポートされた際には実行したくないテストコードやデモコードを記述することができます。他言語経験者にとっては、プログラムの「エントリーポイント」を定義する `main` 関数のような役割と考えると分かりやすいでしょう。 + +2. **`main.py` からの利用**: + 次に、`utils.py` と同じディレクトリに `main.py` を作成し、`utils` モジュールをインポートして使います。 + + ```python + # main.py + + # 自作のutilsモジュールをインポート + import utils + + greeting = utils.say_hello("Alice") + print(greeting) # => Hello, Alice! + + scores = [88, 92, 75, 100] + average_score = utils.get_list_average(scores) + print(f"Your average score is: {average_score}") # => Your average score is: 88.75 + ``` + +このように、機能ごとにファイルを分割することで、コードの見通しが良くなり、再利用も簡単になります。 + + +## パッケージ:モジュールをまとめる + +プロジェクトがさらに大きくなると、多数のモジュールを管理する必要が出てきます。**パッケージ**は、複数のモジュールをディレクトリ構造で階層的に管理するための仕組みです。 + +### パッケージの概念と `__init__.py` + +パッケージは、簡単に言うと**モジュールが入ったディレクトリ**です。Pythonにそのディレクトリをパッケージとして認識させるために、`__init__.py` という名前のファイルを置きます(近年のPythonでは必須ではありませんが、互換性や明示性のために置くのが一般的です)。 + +以下のようなディレクトリ構造を考えてみましょう。 + +``` +my_project/ +├── main.py +└── my_app/ + ├── __init__.py + ├── models.py + └── services.py +``` + + * `my_app` がパッケージ名になります。 + * `__init__.py` は空でも構いません。このファイルが存在することで、`my_app` ディレクトリは単なるフォルダではなく、Pythonのパッケージとして扱われます。 + * `models.py` と `services.py` が、`my_app` パッケージに含まれるモジュールです。 + +`main.py` からこれらのモジュールをインポートするには、`パッケージ名.モジュール名` のように記述します。 + +```python +# main.py + +# パッケージ内のモジュールをインポート +from my_app import services + +# servicesモジュール内の関数を実行 (仮の関数) +# user_data = services.fetch_user_data(user_id=123) +# print(user_data) +``` + +`__init__.py` には、パッケージがインポートされた際の初期化処理を記述することもできます。 + + +## 標準ライブラリ:Pythonに備わった強力なツール群 + +Pythonの大きな魅力の一つは、その「**バッテリー同梱 (Batteries Included)**」という哲学です。これは、Pythonをインストールしただけで、追加のインストールなしにすぐに使える膨大で強力な**標準ライブラリ**が付属していることを意味します。 + +### 標準ライブラリの探索 + +どんなライブラリがあるかを知るには、公式ドキュメントが最も信頼できます。 + + * [**The Python Standard Library — Python 3.x documentation**](https://docs.python.org/3/library/index.html) + +また、REPLの `help()` や `dir()` を使うと、モジュールの内容を簡単に確認できます。 + +```python +>>> import datetime +>>> # datetimeモジュールが持つ属性や関数のリストを表示 +>>> dir(datetime) +['MAXYEAR', 'MINYEAR', '__all__', '__builtins__', ..., 'date', 'datetime', 'time', 'timedelta', 'timezone', 'tzinfo'] +>>> +>>> # dateクラスのヘルプドキュメントを表示 +>>> help(datetime.date) +Help on class date in module datetime: + +class date(builtins.object) + | date(year, month, day) --> a date object + | + | Methods defined here: +... (ヘルプ情報が続く) ... +``` + +### よく使われる標準ライブラリの例 + +ここでは、日常的によく使われる標準ライブラリをいくつかREPLで試してみましょう。 + + * **`os`**: OSとの対話。ファイルやディレクトリの操作など。 + + ```python + >>> import os + >>> + >>> # カレントディレクトリのファイル一覧を取得 + >>> os.listdir('.') + ['hello.py', 'utils.py', 'main.py'] + >>> + >>> # OSに依存しない安全なパスの結合 + >>> os.path.join('data', 'file.txt') + 'data/file.txt' # Mac/Linuxの場合 + # 'data\\file.txt' # Windowsの場合 + ``` + + * **`sys`**: Pythonインタプリタに関する情報。コマンドライン引数など。 + + ```python + >>> import sys + >>> + >>> # Pythonのバージョンを表示 + >>> sys.version + '3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.3.0]' # 環境により異なります + ``` + + ( `sys.argv` はスクリプト実行時に意味を持つため、ここでは割愛します ) + + * **`datetime`**: 日付や時刻の操作。 + + ```python + >>> import datetime + >>> + >>> # 現在の日時を取得 + >>> now = datetime.datetime.now() + >>> print(now) + 2025-08-12 18:26:06.123456 # 実行時刻による + >>> + >>> # 日時をフォーマットして文字列にする + >>> now.strftime('%Y-%m-%d %H:%M:%S') + '2025-08-12 18:26:06' + ``` + + * **`json`**: JSON形式のデータの操作。 + + ```python + >>> import json + >>> + >>> # Pythonの辞書型データ + >>> user = {"id": 1, "name": "Ken", "email": "ken@example.com"} + >>> + >>> # 辞書型をJSON形式の文字列に変換 (dumps: dump string) + >>> json_string = json.dumps(user, indent=2) + >>> print(json_string) + { + "id": 1, + "name": "Ken", + "email": "ken@example.com" + } + >>> + >>> # JSON形式の文字列をPythonの辞書型に変換 (loads: load string) + >>> loaded_user = json.loads(json_string) + >>> loaded_user['name'] + 'Ken' + ``` + +これらの他にも、正規表現を扱う `re`、乱数を生成する `random` など、数え切れないほどの便利なモジュールが標準で提供されています。何かを実装したいと思ったら、まずは「Python 標準ライブラリ 〇〇」で検索してみると、車輪の再発明を防ぐことができます。 + + +## まとめ + +この章では、Pythonにおけるコードの整理術を学びました。 + + * **スクリプトファイル (`.py`)** にコードを保存し、`python` コマンドで実行するのが開発の基本です。 + * **モジュール**は `.py` ファイル単位のコードの部品であり、`import` 文で再利用します。 + * **パッケージ**はモジュールをまとめるディレクトリであり、大規模なプロジェクトの構造を整理します。 + * **`if __name__ == "__main__":`** は、モジュールが直接実行されたか、インポートされたかを判別するための重要な構文です。 + * Pythonには強力な**標準ライブラリ**が多数同梱されており、多くの一般的なタスクはこれらを活用することで効率的に実装できます。 + +コードを適切にモジュール化・パッケージ化することは、読みやすく、テストしやすく、再利用しやすい、質の高いソフトウェアを開発するための第一歩です。 \ No newline at end of file diff --git a/public/docs/python-6.md b/public/docs/python-6.md new file mode 100644 index 0000000..ebcc733 --- /dev/null +++ b/public/docs/python-6.md @@ -0,0 +1,208 @@ +# 第6章: Pythonicなオブジェクト指向プログラミング + +Pythonのオブジェクト指向プログラミング(OOP)は、他の言語と考え方は似ていますが、よりシンプルで柔軟な構文を持っています。この章では、クラスの定義から継承、そしてPython特有の「マジックメソッド」まで、その基本を学びます。 + +## `class`の定義とインスタンス化 + +Pythonでは、`class`キーワードを使ってクラスを定義します。JavaやC++のように波括弧`{}`は使わず、インデントでブロックを示します。非常にシンプルです。 + +クラスを定義したら、関数を呼び出すように`クラス名()`と書くことで、そのクラスの**インスタンス**(オブジェクト)を生成できます。 + +```python +>>> # 中身が何もない、最もシンプルなクラスを定義 +>>> class User: +... pass +... +>>> # Userクラスのインスタンスを生成 +>>> user1 = User() +>>> +>>> # user1はUserクラスのオブジェクト(インスタンス)であることがわかる +>>> user1 +<__main__.User object at 0x10e85a4d0> +``` + +## コンストラクタ (`__init__`) と `self` + +インスタンスが生成される際に、特定の初期化処理を行いたい場合があります。そのために使われるのが`__init__`という特殊なメソッドで、他の言語における**コンストラクタ**に相当します。 + +`__init__`メソッドの最初の引数には、慣習的に`self`という名前を付けます。この`self`は、生成されるインスタンス自身を指す参照で、JavaやJavaScriptの`this`と同じ役割を果たします。`self`を通じて、インスタンスに固有のデータを格納する**インスタンス変数**を定義します。 + +```python +>>> class Dog: +... # インスタンス生成時に呼び出されるコンストラクタ +... def __init__(self, name, age): +... print(f"新しい犬のインスタンスが作られます!") +... # self.変数名 の形でインスタンス変数を定義 +... self.name = name +... self.age = age +... +>>> # インスタンス化する際、__init__のself以外の引数を渡す +>>> my_dog = Dog("ポチ", 3) +新しい犬のインスタンスが作られます! +>>> +>>> # インスタンス変数にアクセス +>>> my_dog.name +'ポチ' +>>> my_dog.age +3 +``` + +## インスタンス変数とクラス変数 + +Pythonのクラスには2種類の変数があります。 + + * **インスタンス変数**: `self.変数名`のように`__init__`内などで定義され、**各インスタンスに固有**の値を持ちます。上の例の`name`や`age`がこれにあたります。 + * **クラス変数**: クラス定義の直下に書かれ、そのクラスから作られた**全てのインスタンスで共有**されます。 + + + +```python +>>> class Cat: +... # このクラスから作られるインスタンス全てで共有されるクラス変数 +... species = "ネコ科" +... +... def __init__(self, name): +... # このインスタンス固有のインスタンス変数 +... self.name = name +... +>>> tama = Cat("タマ") +>>> mike = Cat("ミケ") +>>> +>>> # インスタンス変数はそれぞれ異なる +>>> tama.name +'タマ' +>>> mike.name +'ミケ' +>>> +>>> # クラス変数は共有されている +>>> tama.species +'ネコ科' +>>> mike.species +'ネコ科' +>>> +>>> # クラス変数を変更すると、全てのインスタンスに影響する +>>> Cat.species = "イヌ科?" +>>> tama.species +'イヌ科?' +>>> mike.species +'イヌ科?' +``` + +## メソッドの定義 + +クラス内で定義された関数を**メソッド**と呼びます。メソッドは、そのクラスのインスタンスが持つべき振る舞いを定義します。 + +メソッドを定義する際も、最初の引数には必ず`self`を指定する必要があります。これにより、メソッド内から`self`を通じてインスタンス変数にアクセスできます。 + +```python +>>> class Dog: +... def __init__(self, name): +... self.name = name +... +... # barkというメソッドを定義 +... # selfを介してインスタンス変数nameにアクセスする +... def bark(self): +... return f"{self.name}: ワン!" +... +>>> pochi = Dog("ポチ") +>>> +>>> # メソッドを呼び出す +>>> pochi.bark() +'ポチ: ワン!' +``` + +## 継承 + +**継承**は、あるクラス(親クラス、スーパークラス)の性質を、新しいクラス(子クラス、サブクラス)が引き継ぐ仕組みです。コードの再利用性を高めるOOPの重要な概念です。 + +Pythonでは`class 子クラス名(親クラス名):`という構文で継承を表現します。 + +子クラスで親クラスのメソッドを上書き(**オーバーライド**)したり、`super()`関数を使って親クラスのメソッドを呼び出したりすることもできます。特に、子クラスの`__init__`で親クラスの`__init__`を呼び出すのは一般的なパターンです。 + +```python +>>> # 親クラス +>>> class Animal: +... def __init__(self, name): +... print("Animalの__init__が呼ばれました") +... self.name = name +... +... def eat(self): +... return f"{self.name}は食事中です。" +... +>>> # Animalクラスを継承した子クラス +>>> class Dog(Animal): +... def __init__(self, name, breed): +... print("Dogの__init__が呼ばれました") +... # super()で親クラスの__init__を呼び出し、nameを初期化 +... super().__init__(name) +... self.breed = breed # Dogクラス独自のインスタンス変数を追加 +... +... # Dogクラス独自のメソッド +... def bark(self): +... return "ワン!" +... +>>> # Dogクラスをインスタンス化 +>>> my_dog = Dog("ポチ", "柴犬") +Dogの__init__が呼ばれました +Animalの__init__が呼ばれました +>>> +>>> # 親クラスのメソッドも使える +>>> my_dog.eat() +'ポチは食事中です。' +>>> +>>> # もちろん子クラス独自のメソッドも使える +>>> my_dog.bark() +'ワン!' +>>> +>>> # 親クラスと子クラス両方で初期化されたインスタンス変数を持つ +>>> my_dog.name +'ポチ' +>>> my_dog.breed +'柴犬' +``` + +## 基本的なマジックメソッド (`__str__`, `__repr__`) + +`__init__`のように、`__`(ダブルアンダースコア)で囲まれたメソッドは**マジックメソッド**(または特殊メソッド)と呼ばれます。これらは、特定の操作(`print()`での表示や演算子の使用など)に対応して、Pythonが裏側で自動的に呼び出す特別なメソッドです。 + +特に重要なマジックメソッドとして`__str__`と`__repr__`があります。 + + * `__str__`: `print()`関数や`str()`でオブジェクトが呼ばれたときに使われます。**人間が読みやすい**、非公式な文字列表現を返すことを目的とします。 + * `__repr__`: `repr()`関数やREPL環境で変数を評価したときに使われます。**開発者向け**で、そのオブジェクトを再生成できるような、公式で曖昧さのない文字列表現を返すことが理想です (`eval(repr(obj)) == obj`となるのが望ましい)。 + + + +```python +>>> class Person: +... def __init__(self, name, age): +... self.name = name +... self.age = age +... +... # print()で表示したときの振る舞いを定義 +... def __str__(self): +... return f"名前: {self.name}, 年齢: {self.age}" +... +... # REPLでの評価やrepr()での振る舞いを定義 +... def __repr__(self): +... return f"Person(name='{self.name}', age={self.age})" +... +>>> bob = Person("ボブ", 30) +>>> +>>> # print()は__str__を呼び出す +>>> print(bob) +名前: ボブ, 年齢: 30 +>>> +>>> # str()も__str__を呼び出す +>>> str(bob) +'名前: ボブ, 年齢: 30' +>>> +>>> # REPLで変数をそのまま評価すると__repr__が呼び出される +>>> bob +Person(name='ボブ', age=30) +>>> +>>> # repr()も__repr__を呼び出す +>>> repr(bob) +"Person(name='ボブ', age=30)" +``` + +もし`__str__`が定義されていない場合、`print()`は代わりに`__repr__`を呼び出します。両方を定義しておくことで、オブジェクトの使われ方に応じた適切な文字列表現を提供できます。 \ No newline at end of file diff --git a/public/docs/python-7.md b/public/docs/python-7.md new file mode 100644 index 0000000..01a280c --- /dev/null +++ b/public/docs/python-7.md @@ -0,0 +1,196 @@ +# 第7章: ファイルの入出力とコンテキストマネージャ + +この章では、テキストファイルやJSON、CSVファイルの読み書きといった、実践的なファイル操作を学びます。特に、リソース管理を安全かつ簡潔に行うための **`with`** 文(コンテキストマネージャ)は、Pythonプログラミングにおいて必須の知識です。 + +## `open()`関数によるファイルのオープン + +Pythonでファイルを操作するには、まず組み込み関数の **`open()`** を使ってファイルオブジェクトを取得します。`open()` は少なくとも2つの引数、ファイルパスとモードを取ります。 + + * **ファイルパス**: 操作したいファイルへのパス(例: `'data.txt'`)。 + * **モード**: ファイルをどのように開くかを指定する文字列。 + * `'r'`: 読み込み専用(デフォルト) + * `'w'`: 書き込み専用(ファイルが存在すれば上書き) + * `'a'`: 追記(ファイルの末尾に書き足す) + * `'x'`: 新規作成して書き込み(ファイルが存在するとエラー) + * `'+'` を付けると読み書き両用になります(例: `'r+'`, `'w+'`)。 + * `'b'` を付けるとバイナリモードになります(例: `'rb'`, `'wb'`)。 + + + +```python +>>> # 'w' モードでファイルを開く(または新規作成する) +>>> f = open('spam.txt', 'w', encoding='utf-8') +>>> f +<_io.TextIOWrapper name='spam.txt' mode='w' encoding='utf-8'> +>>> # ファイルを使い終わったら必ず閉じる +>>> f.close() +``` + +**`encoding='utf-8'`** は、特に日本語のような非ASCII文字を扱う際に重要です。文字化けを防ぐため、テキストファイルを扱う際はエンコーディングを明示的に指定することを強く推奨します。ファイルを閉じる **`close()`** メソッドを呼び出すまで、ファイルリソースはプログラムによって確保されたままになります。 + + +## テキストファイルの読み書き + +ファイルオブジェクトのメソッドを使って、ファイルの内容を操作します。 + +### 書き込み + +**`write()`** メソッドは、文字列をファイルに書き込みます。このメソッドは書き込んだ文字数を返します。 + +```python +>>> f = open('test.txt', 'w', encoding='utf-8') +>>> f.write('こんにちは、世界!\n') +9 +>>> f.write('これは2行目です。\n') +9 +>>> f.close() +``` + +`write()` は自動的には改行しないため、必要であれば自分で改行コード `\n` を追加します。 + +### 読み込み + +ファイルからデータを読み込むには、いくつかの方法があります。 + + * **`read()`**: ファイルの内容全体を一つの文字列として読み込みます。 + * **`readline()`**: ファイルから1行だけを読み込み、文字列として返します。 + * **`readlines()`**: ファイルのすべての行を読み込み、各行を要素とするリストで返します。 + + + +```python +>>> # 先ほど書き込んだファイルを読み込む +>>> f = open('test.txt', 'r', encoding='utf-8') +>>> content = f.read() +>>> print(content) +こんにちは、世界! +これは2行目です。 + +>>> f.close() + +>>> # readline() を使って1行ずつ読む +>>> f = open('test.txt', 'r', encoding='utf-8') +>>> f.readline() +'こんにちは、世界!\n' +>>> f.readline() +'これは2行目です。\n' +>>> f.readline() # ファイルの終端に達すると空文字列を返す +'' +>>> f.close() +``` + + +## `with`文による安全なファイル操作(コンテキストマネージャ) + +ファイルを `open()` したら `close()` する必要がありますが、処理中に例外が発生すると `close()` が呼ばれない可能性があります。これを確実に、そして簡潔に書く方法が **`with`** 文です。 + +**`with`** 文のブロックを抜けると、ファイルオブジェクトは自動的に `close()` されます。エラーが発生した場合でも同様です。これは「コンテキストマネージャ」という仕組みによって実現されており、ファイル操作の標準的な方法です。 + +```python +>>> # with文を使った書き込み +>>> with open('spam.txt', 'w', encoding='utf-8') as f: +... f.write('withブロックを使っています。\n') +... f.write('ブロックを抜けると自動で閉じられます。\n') +... +>>> # ブロックの外ではファイルは閉じている +>>> f.closed +True + +>>> # with文を使った読み込み +>>> with open('spam.txt', 'r', encoding='utf-8') as f: +... data = f.read() +... +>>> print(data) +withブロックを使っています。 +ブロックを抜けると自動で閉じられます。 + +``` + +このように、`with` 文を使えば `close()` の呼び出しを忘れる心配がなく、コードもすっきりします。今後は常に `with` 文を使ってファイルを扱うようにしましょう。 + + +## `json`モジュールを使ったJSONの操作 + +**JSON (JavaScript Object Notation)** は、データ交換フォーマットとして広く使われています。Pythonの標準ライブラリである **`json`** モジュールを使うと、Pythonのオブジェクト(辞書やリストなど)をJSON形式のデータに、またはその逆に変換できます。 + + * **`json.dump(obj, fp)`**: Pythonオブジェクト `obj` をJSON形式でファイルオブジェクト `fp` に書き込みます。 + * **`json.load(fp)`**: JSON形式のファイルオブジェクト `fp` からデータを読み込み、Pythonオブジェクトに変換します。 + + + +```python +>>> import json + +>>> # 書き込むデータ(Pythonの辞書) +>>> data = { +... "name": "Taro Yamada", +... "age": 30, +... "is_student": False, +... "courses": ["Python", "Machine Learning"] +... } + +>>> # with文を使ってJSONファイルに書き込む +>>> with open('user.json', 'w', encoding='utf-8') as f: +... # ensure_ascii=Falseで日本語をそのまま出力 +... json.dump(data, f, indent=4, ensure_ascii=False) +... + +>>> # JSONファイルから読み込む +>>> with open('user.json', 'r', encoding='utf-8') as f: +... loaded_data = json.load(f) +... +>>> loaded_data +{'name': 'Taro Yamada', 'age': 30, 'is_student': False, 'courses': ['Python', 'Machine Learning']} +>>> loaded_data['name'] +'Taro Yamada' +``` + +`json.dump()` の `indent=4` は、人間が読みやすいように4スペースのインデントを付けて出力するオプションです。`ensure_ascii=False` は、日本語などの非ASCII文字をそのままの文字で出力するために指定します。 + + +## `csv`モジュールを使ったCSVの操作 + +**CSV (Comma-Separated Values)** は、スプレッドシートやデータベースでよく使われる表形式のデータを保存するためのフォーマットです。**`csv`** モジュールを使うと、CSVファイルの読み書きが簡単になります。 + +### CSVファイルへの書き込み + +**`csv.writer()`** を使ってライターオブジェクトを作成し、**`writerow()`** (1行) や **`writerows()`** (複数行) メソッドでデータを書き込みます。 + +```python +>>> import csv + +>>> # 書き込むデータ(リストのリスト) +>>> data_to_write = [ +... ["ID", "Name", "Score"], +... [1, "Alice", 95], +... [2, "Bob", 88], +... [3, "Charlie", 76] +... ] + +>>> # newline='' はWindowsでの不要な空行を防ぐためのおまじない +>>> with open('scores.csv', 'w', newline='', encoding='utf-8') as f: +... writer = csv.writer(f) +... writer.writerows(data_to_write) +... +``` + +### CSVファイルの読み込み + +**`csv.reader()`** を使ってリーダーオブジェクトを作成します。このオブジェクトをループで回すことで、1行ずつリストとしてデータを取得できます。 + +```python +>>> import csv + +>>> with open('scores.csv', 'r', newline='', encoding='-utf-8') as f: +... reader = csv.reader(f) +... # リーダーオブジェクトはイテレータなのでforループで回せる +... for row in reader: +... print(row) +... +['ID', 'Name', 'Score'] +['1', 'Alice', '95'] +['2', 'Bob', '88'] +['3', 'Charlie', '76'] +``` + +注意点として、`csv`モジュールはすべてのデータを文字列として読み込みます。数値として扱いたい場合は、自分で `int()` や `float()` を使って型変換する必要があります。 \ No newline at end of file diff --git a/public/docs/python-8.md b/public/docs/python-8.md new file mode 100644 index 0000000..c547e55 --- /dev/null +++ b/public/docs/python-8.md @@ -0,0 +1,147 @@ +# 第8章: エラーとの付き合い方:例外処理 + +プログラムの実行中に予期せぬ問題が発生すると、Pythonは「例外 (exception)」を送出して処理を中断します。これらのエラーを放置するとプログラムはクラッシュしてしまいますが、「例外処理」の仕組みを使うことで、エラーを優雅に捉えて対処できます。この章では、その方法を学びます。 + +## `try...except`による例外の捕捉 + +他の言語の `try...catch` と同様に、Pythonでは `try...except` ブロックを使います。エラーが発生する可能性のあるコードを `try` ブロックに記述し、エラーが発生した際の処理を `except` ブロックに記述します。 + +例えば、`0` で割り算をすると `ZeroDivisionError` という例外が発生します。 + +```python +>>> 10 / 0 +Traceback (most recent call last): + File "", line 1, in +ZeroDivisionError: division by zero +``` + +このエラーを `try...except` で捕捉してみましょう。 + +```python +>>> try: +... result = 10 / 0 +... except ZeroDivisionError: +... print("エラー: 0で割ることはできません。") +... +エラー: 0で割ることはできません。 +``` + +`try` ブロック内で `ZeroDivisionError` が発生したため、プログラムはクラッシュせずに `except` ブロック内の処理が実行されました。 + +## 複数の例外を処理する方法 + +`try` ブロック内では、複数の種類のエラーが発生する可能性があります。例えば、ユーザーの入力を数値に変換しようとして失敗した場合は `ValueError` が発生します。 + +複数の例外を処理するには、2つの方法があります。 + +**1. `except` ブロックを複数記述する** + +エラーの種類ごとに異なる処理を行いたい場合に適しています。 + +```python +>>> def calculate(a, b): +... try: +... a = int(a) +... b = int(b) +... result = a / b +... print(f"計算結果: {result}") +... except ValueError: +... print("エラー: 数値を入力してください。") +... except ZeroDivisionError: +... print("エラー: 0で割ることはできません。") +... +>>> calculate(10, 2) +計算結果: 5.0 +>>> calculate(10, 0) +エラー: 0で割ることはできません。 +>>> calculate('ten', 2) +エラー: 数値を入力してください。 +``` + +**2. 1つの `except` ブロックでタプルを使ってまとめる** + +複数の例外に対して同じ処理を行いたい場合に便利です。 + +```python +>>> def calculate_v2(a, b): +... try: +... a = int(a) +... b = int(b) +... result = a / b +... print(f"計算結果: {result}") +... except (ValueError, ZeroDivisionError) as e: +... print(f"入力エラーが発生しました: {e}") +... +>>> calculate_v2(20, 0) +入力エラーが発生しました: division by zero +>>> calculate_v2('twenty', 5) +入力エラーが発生しました: invalid literal for int() with base 10: 'twenty' +``` + +`as e` のように書くことで、発生した例外オブジェクトそのものを変数 `e` で受け取ることができます。これにより、具体的なエラーメッセージを表示できます。 + +## 独自例外の送出 (`raise`) + +特定の条件を満たした場合に、意図的に例外を発生させたいことがあります。その場合は `raise` 文を使います。 + +例えば、負の値を受け付けない関数を考えてみましょう。 + +```python +>>> def process_positive_number(num): +... if num < 0: +... raise ValueError("負の値は処理できません。") +... print(f"{num}を処理しました。") +... +>>> process_positive_number(100) +100を処理しました。 +>>> process_positive_number(-5) +Traceback (most recent call last): + File "", line 1, in + File "", line 3, in process_positive_number +ValueError: 負の値は処理できません。 +``` + +このように、`raise` を使うことで、関数の事前条件などを満たさない場合に、プログラムの実行を中断して呼び出し元にエラーを通知できます。 + +## `else`と`finally`節の役割 + +`try...except` ブロックには、`else` と `finally` という2つのオプションの節を追加できます。 + + * **`else` 節**: `try` ブロックで**例外が発生しなかった場合**にのみ実行されます。 + * **`finally` 節**: 例外の有無に**関わらず、必ず最後に**実行されます。ファイルクローズやデータベース接続の切断など、後片付け処理に最適です。 + +すべての節を使った例を見てみましょう。 + +```python +>>> def divider(a, b): +... print(f"--- {a} / {b} の計算を開始します ---") +... try: +... result = a / b +... except ZeroDivisionError: +... print("エラー: 0による除算です。") +... else: +... # 例外が発生しなかった場合に実行 +... print(f"計算成功! 結果: {result}") +... finally: +... # 常に最後に実行 +... print("--- 計算を終了します ---") +... +>>> # 成功するケース +>>> divider(10, 2) +--- 10 / 2 の計算を開始します --- +計算成功! 結果: 5.0 +--- 計算を終了します --- + +>>> # 例外が発生するケース +>>> divider(10, 0) +--- 10 / 0 の計算を開始します --- +エラー: 0による除算です。 +--- 計算を終了します --- +``` + +この例から、実行フローが明確にわかります。 + + * 成功ケースでは `try` -\> `else` -\> `finally` の順に実行されます。 + * 失敗ケースでは `try` -\> `except` -\> `finally` の順に実行されます。 + +`finally` 節は、`try` ブロック内で `return` が実行される場合でも、その `return` の直前に実行されることが保証されています。これにより、リソースの解放漏れなどを防ぐことができます。 \ No newline at end of file diff --git a/public/docs/python-9.md b/public/docs/python-9.md new file mode 100644 index 0000000..e21aeba --- /dev/null +++ b/public/docs/python-9.md @@ -0,0 +1,167 @@ +# 第9章: さらに先へ:ジェネレータとデコレータ + +この章では、より高度でPythonらしいコードを書くための強力な機能、**ジェネレータ**と**デコレータ**を学びます。これらの機能を使いこなすことで、メモリ効率の良いデータ処理を実装したり、既存の関数の振る舞いをエレガントに変更したりできるようになります。 + +## イテレータとイテラブル + +Pythonの`for`ループは非常にシンプルで強力ですが、その裏側では**イテレーションプロトコル**という仕組みが動いています。これを理解することが、ジェネレータを学ぶ上での第一歩です。 + + * **イテラブル (Iterable)**: `for`ループで繰り返し処理が可能なオブジェクトのことです。リスト、タプル、辞書、文字列などがこれにあたります。内部に `__iter__()` メソッドを持つオブジェクトと定義されます。 + * **イテレータ (Iterator)**: 「次の値」を返す `__next__()` メソッドを持ち、値を一つずつ取り出すためのオブジェクトです。イテレータは一度最後まで進むと、それ以上値を取り出すことはできません。 + +`for`ループは、まずイテラブルオブジェクトの `__iter__()` を呼び出してイテレータを取得し、次にそのイテレータの `__next__()` を繰り返し呼び出して要素を一つずつ取り出しています。 + +REPLで動きを見てみましょう。`iter()`関数でイテレータを取得し、`next()`関数で要素を取り出します。 + +```python +>>> my_list = [1, 2, 3] +>>> my_iterator = iter(my_list) +>>> type(my_iterator) + + +>>> next(my_iterator) +1 +>>> next(my_iterator) +2 +>>> next(my_iterator) +3 +>>> next(my_iterator) +Traceback (most recent call last): + File "", line 1, in +StopIteration +``` + +最後の`next()`呼び出しで `StopIteration` という例外が発生しているのがわかります。`for`ループはこの例外を検知して、ループを自動的に終了してくれます。 + +## ジェネレータ関数とyieldキーワード + +イテレータを自作するには、クラスに `__iter__()` と `__next__()` を実装する必要がありますが、少し手間がかかります。そこで登場するのが**ジェネレータ**です。ジェネレータは、イテレータを簡単に作成するための特別な関数です。 + +ジェネレータ関数は、通常の関数と似ていますが、値を返すのに`return`の代わりに`yield`を使います。 + + * **`yield`の働き**: `yield`は値を返すだけでなく、その時点で関数の実行を**一時停止**し、関数の状態(ローカル変数など)を保存します。次に`next()`が呼ばれると、停止した場所から処理を再開します。 + +これにより、巨大なデータセットを扱う際に、全てのデータを一度にメモリに読み込む必要がなくなります。必要な時に必要な分だけデータを生成するため、非常にメモリ効率が良いコードが書けます。 + +フィボナッチ数列を生成するジェネレータの例を見てみましょう。 + +```python +>>> def fib_generator(n): +... a, b = 0, 1 +... count = 0 +... while count < n: +... yield a +... a, b = b, a + b +... count += 1 +... +>>> f = fib_generator(5) +>>> type(f) + + +>>> next(f) +0 +>>> next(f) +1 +>>> next(f) +1 +>>> next(f) +2 +>>> next(f) +3 +>>> next(f) +Traceback (most recent call last): + File "", line 1, in +StopIteration + +# ジェネレータはもちろんforループで使うことができます +>>> for num in fib_generator(8): +... print(num, end=' ') +... +0 1 1 2 3 5 8 13 +``` + +## ジェネレータ式 + +リスト内包表記に似た構文で、より簡潔にジェネレータを作成する方法が**ジェネレータ式**です。リスト内包表記の `[]` を `()` に変えるだけで作れます。 + +リスト内包表記はリストオブジェクトを生成するため、要素数が多いとメモリを大量に消費します。一方、ジェネレータ式はジェネレータオブジェクトを返すため、遅延評価(必要になるまで計算しない)が行われ、メモリ使用量を抑えられます。 + +```python +# リスト内包表記 +>>> list_comp = [i * 2 for i in range(5)] +>>> list_comp +[0, 2, 4, 6, 8] +>>> type(list_comp) + + +# ジェネレータ式 +>>> gen_exp = (i * 2 for i in range(5)) +>>> gen_exp + at 0x...> +>>> type(gen_exp) + + +>>> next(gen_exp) +0 +>>> next(gen_exp) +2 +>>> list(gen_exp) # 残りの要素をリストに変換 +[4, 6, 8] +``` + +巨大なファイルの各行を処理する場合など、ジェネレータ式は非常に有効です。 + +## デコレータの概念と基本的な作り方 + +**デコレータ**は、既存の関数のコードを一切変更せずに、その関数に新しい機能を追加(装飾)するための仕組みです。これは、関数を受け取って、新しい関数を返す**高階関数**として実装されます。 + +ログ出力、実行時間の計測、認証チェックなど、複数の関数に共通して適用したい「横断的な関心事」を扱うのに非常に便利です。 + +### 基本的な作り方 + +デコレータの基本的な構造は、関数を入れ子にすることです。 + +1. 外側の関数(デコレータ関数)は、装飾したい対象の関数を引数として受け取ります。 +2. 内側の関数(ラッパー関数)で、受け取った関数を呼び出す前後に、追加したい処理を記述します。 +3. 外側の関数は、この内側の関数を返します。 + +関数の実行前後にメッセージを表示する簡単なデコレータを見てみましょう。 + +```python +>>> def my_decorator(func): +... def wrapper(): +... print("--- 処理を開始します ---") +... func() +... print("--- 処理が完了しました ---") +... return wrapper +... + +>>> def say_hello(): +... print("こんにちは!") +... + +# デコレートされた新しい関数を作成 +>>> decorated_hello = my_decorator(say_hello) +>>> decorated_hello() +--- 処理を開始します --- +こんにちは! +--- 処理が完了しました --- +``` + +この書き方をより簡単にするための構文が `@`(アットマーク)、シンタックスシュガーです。 + +```python +>>> @my_decorator +... def say_goodbye(): +... print("さようなら!") +... + +>>> say_goodbye() +--- 処理を開始します --- +さようなら! +--- 処理が完了しました --- +``` + +`@my_decorator` は、`say_goodbye = my_decorator(say_goodbye)` と同じ意味になります。こちらのほうが直感的で、Pythonのコードで広く使われています。 + +ジェネレータとデコレータは、最初は少し複雑に感じるかもしれませんが、使いこなせばよりクリーンで効率的なPythonコードを書くための強力な武器となります。ぜひ積極的に活用してみてください。 \ No newline at end of file