Repository: rinatz/python-book
Branch: master
Commit: be023357b139
Files: 37
Total size: 84.3 KB
Directory structure:
gitextract_9ud2e2c_/
├── .github/
│ └── workflows/
│ └── mkdocs.yml
├── .gitignore
├── LICENSE
├── README.md
├── docs/
│ ├── ch01-01-installation.md
│ ├── ch01-02-hello-world.md
│ ├── ch02-01-variables.md
│ ├── ch02-02-functions.md
│ ├── ch02-03-comments.md
│ ├── ch02-04-conditions.md
│ ├── ch02-05-loops.md
│ ├── ch02-06-list-comprehensions.md
│ ├── ch02-07-lambdas.md
│ ├── ch03-01-classes.md
│ ├── ch03-02-scopes.md
│ ├── ch03-03-special-attributes.md
│ ├── ch03-04-properties.md
│ ├── ch04-01-modules.md
│ ├── ch04-02-packages.md
│ ├── ch04-03-pip.md
│ ├── ch04-04-venv.md
│ ├── ch04-07-project-structures.md
│ ├── ch05-01-files.md
│ ├── ch05-02-contexts.md
│ ├── ch05-03-csv.md
│ ├── ch05-04-json.md
│ ├── ch06-01-exceptions.md
│ ├── ch07-01-generators.md
│ ├── ch08-01-doctest.md
│ ├── ch08-02-pytest.md
│ ├── ch09-01-tools.md
│ ├── ch09-02-pipenv.md
│ ├── ch09-03-poetry.md
│ └── index.md
├── mkdocs.yml
├── pyproject.toml
└── requirements.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/mkdocs.yml
================================================
name: MkDocs
on:
push:
branches:
- master
paths:
- mkdocs.yml
- docs/**
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git fetch origin gh-pages
- name: Install MkDocs
run: |
python3 -m pip install setuptools
python3 -m pip install -r requirements.txt
- name: Deploy HTML files
run: python3 -m mkdocs gh-deploy
================================================
FILE: .gitignore
================================================
site/
# Created by https://www.gitignore.io/api/macOS
# Edit at https://www.gitignore.io/?templates=macOS
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# End of https://www.gitignore.io/api/macOS
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Ida Kenichiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ゼロから学ぶ Python
このリポジトリはオンライン学習サイト[ゼロから学ぶ Python]のソースコードリポジトリです。
## 必要なもの
ソースコードから HTML ページを生成するには下記のものが必要です。
- Python3
## ビルド
HTML を生成するには下記のコマンドを実行してください。
```shell
$ pip install -r requirements.txt
$ mkdocs build
```
ビルド結果をブラウザ上で確認するには次のコマンドを実行します。
```shell
$ mkdocs serve
```
http://localhost:8000 にアクセスすると Web ページが表示されます。
[ゼロから学ぶ Python]: https://rinatz.github.io/python-book
================================================
FILE: docs/ch01-01-installation.md
================================================
# インストール
Python 開発に必要な下記のツールをインストールします。
- Python
- Visual Studio Code
## Python
各プラットフォームに合わせて Python のインストール手順が下記サイトにまとまっていますのでインストールをしてください。
- [macOS](https://www.python.jp/install/macos/install_python.html)
- [Windows](https://www.python.jp/install/windows/install.html)
コマンドを打って Python が正しくインストールされたかどうかを確認します。
=== "macOS"
ターミナル上で下記コマンドを実行します。
```shell
$ python3 --version
```
バージョンが表示されれば成功です。
=== "Windows"
コマンドプロンプト上で下記コマンドを実行します。
```shell
$ py -3 --version
```
バージョンが表示されれば成功です。
## Visual Studio Code のインストール
Visual Studio Code (VS Code) はエンジニアの間で人気のエディタです。
拡張機能を取り入れることで見た目の変更や機能追加などを自由にカスタマイズできるのが特徴です。
下記のサイトで VS Code をダウンロードし、インストールをしてください。
!!! Info "VS Code"
[https://code.visualstudio.com/](https://code.visualstudio.com/)
次に Python の開発環境を整えるための VS Code 拡張機能をインストールします。
| 拡張機能 | 概要 |
|-------------------------------------------|-----------------------------|
| [Python extension for Visual Studio Code] | Python 開発のための基本機能 |
| [Pylance] | コード補完 |
| [Visual Studio IntelliCode] | オートコンプリート |
[Python extension for Visual Studio Code]: https://marketplace.visualstudio.com/items?itemName=ms-python.python
[Pylance]: https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance
[Visual Studio IntelliCode]: https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode
インストールは VS Code を起動した後、左側にあるペインから拡張機能のタブを選択し、
インストールする拡張機能を検索してインストールします。
[](img/vscode-01.png)
================================================
FILE: docs/ch01-02-hello-world.md
================================================
# Hello, World!
標準出力に `Hello, World!` と出力する簡単な Python プログラムを書いてみます。
---
まずターミナル上で `hello` というディレクトリを作成し、それを VSCode で開きます。
```shell
$ mkdir hello
$ cd hello
$ code .
```
[](img/vscode-02.png)
---
次にファイルの新規作成のアイコンを押して `hello.py` という名前のファイルを作成します。
[](img/vscode-03.png)
---
ファイルを作成してそのファイルを開くとウィンドウの左下に使用する Python のバージョンが表示されます。
このバージョンがインストールした Python のバージョンと異なる場合は Python のバージョンをクリックすると
使用する Python を変更することができます。
[](img/vscode-04.png)
複数の Python をインストールしている場合にはこのようにして使用する Python を変更してください。
---
ここまでできたら `hello.py` にコードを書いてみます。
**hello.py**
```python
#!/usr/bin/env python
def main():
print('Hello, World!')
if __name__ == '__main__':
main()
```
できたらファイルを保存してプログラムを実行してみます。
ウィンドウの右上にある再生ボタンをクリックするとプログラムが実行されます。
[](img/vscode-05.png)
## 構文の説明
### シバン
```python hl_lines="1"
#!/usr/bin/env python
def main():
print('Hello, World!')
if __name__ == '__main__':
main()
```
`#` から始まる 1 行目は [シバン] といいます。シバンはこのソースコードを実行する際に使用するコマンドを記述します。`/usr/bin/env python` は `python` コマンドを呼び出しているという意味になります。
[シバン]: https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%90%E3%83%B3_(Unix)
!!! warning
Windows では `python` コマンドの代わりに `py` コマンドを使用するように説明しましたが、シバンは
```
#!/usr/bin/env python
```
と書いて下さい。
### `main()`
```python hl_lines="4 5"
#!/usr/bin/env python
def main():
print('Hello, World!')
if __name__ == '__main__':
main()
```
これは関数の定義をしています。関数の詳細な説明は [関数] の章で説明します。
`print()` がインデントされていることはとても重要です。なぜなら
```python
def main():
print('Hello, World!')
```
と書くと構文エラーになるからです。関数の実装は必ずインデントしてから記述するルールになっています。
### `__name__`
```python hl_lines="8 9"
#!/usr/bin/env python
def main():
print('Hello, World!')
if __name__ == '__main__':
main()
```
`main()` は関数を呼び出しています。`if __name__ == '__main__':` については [モジュール] の章で説明しますので、今はおまじないだと思って下さい。`main()` の呼び出しは必ずインデントをして下さい。さもないと構文エラーになります。
### シンプルな書き方
Python の処理はソースコードの上の行から逐次実行されます。そのため、処理を関数内に収めなくても正しく実行することができます。
```python
#!/usr/bin/env python
print('Hello, World!')
```
しかし特別な理由がある場合を除いて、関数内に定義する方が望ましいです。その理由は [モジュール] の章で明らかになります。
[関数]: ch02-02-functions.md
[モジュール]: ch04-01-modules.md
================================================
FILE: docs/ch02-01-variables.md
================================================
# 変数
次のようなコードを書いて色々な変数を出力してみましょう。
```python hl_lines="6"
#!/usr/bin/env python
def main():
x = 10 # 変数の定義
print(x)
if __name__ == '__main__':
main()
```
## 数値型
### 整数 `int`
```python
x = 10
```
### 浮動小数点 `float`
```python
x = 3.14
```
### 数値演算
数値に対しては四則演算ができます。
```python
# 加算
total = 5 + 10
# 減算
difference = 95.5 - 4.3
# 乗算
product = 4 * 30
# 除算
division = 56.7 / 32.2
# 商
quotient = 62 // 12
# 剰余
remainder = 43 % 5
```
!!! warning "整数 / 整数 の結果"
整数に対する `/` は Python 2 系と 3 系では意味が変わります。
| バージョン | 挙動 | 5 / 2 = ? |
|------------|------|-----------|
| 2.x.x | 商 | 2 |
| 3.x.x | 除算 | 2.5 |
## 真偽値 `bool`
真偽値を扱うための型です。
```python
x = True
y = False
```
## 文字列 `str`
```python
x = 'Hello, World!'
y = '日本語'
```
### f-string
文字列の先頭に `f` を付けると文字列内に変数や式の埋め込みができるようになります。
```python
x = 10
s1 = f'The value of x is {x}' # 'The value of x is 10'
s2 = f'The value of x * x is {x * x}' # 'The value of x * x is 100'
```
f-string を使わなくても次のようにも書けます。
```python
s1 = 'The value of x is {}'.format(x)
s2 = 'The value of x * x is {}'.format(x * x)
```
f-string は `'...'.format()` のシンプルな書き方を提供する機能です。
## バイト `byte`
バイト列を扱う型です。
```python
x = b'0xDEADBEEF'
```
## コレクション型
コレクション型は複数の値をまとめて扱える型の総称です。
### リスト `list`
配列を扱うための型です。リストの各要素は必ずしも同じ型である必要はありません。
```python
x = [0, 1, 2, 3, 4]
y = [10, 3.14, 'Hello, World!']
```
要素の末尾に `,` が入っていても構文として正しいです。
```python
x = [
10,
3.14,
'Hello, World!', # OK
]
```
要素の追加は次のようにします。
```python
x.append(100)
```
また要素参照は次のようにします。
```python
x = [10, 3.14, 'Hello, World!']
x[0] # 10
x[1] # 3.14
x[2] # 'Hello, World!'
```
添字には負の数も指定できます。負の数を指定した場合は末尾の要素から参照されます。
```python
x = [10, 3.14, 'Hello, World!']
x[-1] # 'Hello, World!'
x[-2] # 3.14
x[-3] # 10
```
要素が空のリストを作る場合は `x = []` とします。
### タプル `tuple`
リストとよく似た扱いができる型ですが、タプルは要素の変更も追加もできません。
```python
x = (0, 1, 2, 3, 4)
y = (10, 3.14, 'Hello, World!')
```
要素参照はリストと同じようにしてできます。`()` はただの飾りであり、必須ではありません。タプルになるかどうかは `,` が含まれているかどうかで決まります。
```python
x = 10, 3.14, 'Hello, World!' # 3 要素のタプル
y = 20, # 1 要素のタプル
```
ただし空のタプルを作りたいときは `()` を使います。
```python
x = ()
```
!!! note
要素の変更が必要ない場合はリストよりタプルを使う方が安全です。
### アンパック代入
リストやタプルの要素を複数の変数に同時に代入する構文を **アンパック代入** といいます。
```python
x = (10, 3.14, 'Hello, World!')
a, b, c = x # a: 10, b: 3.14, c: 'Hello, World!'
```
代入元と代入先の要素数は一致している必要があります。
```python
x = [0, 1, 2, 3, 4]
a, b, c = x # エラー
```
しかし代入先の変数に `*` を付けておくと、その変数はリスト型になるため、要素数が一致しなくてもアンパック代入ができるようになります。
```python
x = [0, 1, 2, 3, 4]
a, b, *c = x # a: 0, b: 1, c: [2, 3, 4]
```
### 辞書 `dict`
キーに対する値を管理するための型です。
```python
x = {'name': 'John Doe', 'age': 30}
x['name'] # 'John Doe'
x['age'] # 30
```
要素が空の辞書を作る場合は `x = {}` とします。
### 集合 `set`
重複を取り除いて複数の要素を扱うための型です。
```python
x = {0, 1, 2, 2, 3, 4, 4} # {0, 1, 2, 3, 4}
```
`set()` を使うとリストやタプルから集合を作ることができます。
```python
x = set([1, 1, 2, 3, 4, 4]) # {1, 2, 3, 4}
y = set(('x', 'x', 3.14, [1, 2], [1, 2])) # {'x', 3.14, [1, 2]}
```
空の集合を作る場合は `x = set()` とします。`x = {}` としても空の辞書となるので注意してください。
## `None`
変数に `None` というキーワードを代入すると、その変数はどの型にも属さない変数になります。
```python
x = None
```
変数は用意したいけど、どのような値を入れるかは後で決めたいといったケースでは `None` が使用されます。
## 定数
Python には定数の概念がありません。しかし、書き換えを想定しない変数は大文字で書いて定数であることを示すというルールがあります。
```python
PI = 3.14
```
================================================
FILE: docs/ch02-02-functions.md
================================================
# 関数
関数を定義するには `def` キーワードを使います。
```python
#!/usr/bin/env python
def another_function():
print('Another function')
def main():
print('Hello, World!')
another_function()
if __name__ == '__main__':
main()
```
関数名は自由に与えることができますが、慣習的に英数字かつ *snake_case* (小文字を `_` でつなぐ記法)で表記します。
!!! note
関数定義の間は慣習的に 2 行開けるルールになっています。
## 引数
関数には引数を渡すことができます。
```python
def another_function(x, y):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
def main():
another_function(5, 6)
```
## デフォルト引数
関数には引数を明示的に渡さなかった場合にデフォルト値を暗黙的に渡す機能があります。
```python
def another_function(x, y=10):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
def main():
another_function(5, 3) # x: 5, y: 3
another_function(7) # x: 7, y: 10
```
デフォルト引数は引数内で一番最後に渡す必要があります。
```python
# OK
def f1(x, y=10):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
# OK
def f2(x, y=10, z=20):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
print(f'The value of z is {z}')
# NG
def f3(y=10, x, z=20):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
print(f'The value of z is {z}')
```
!!! note
デフォルト引数の定義は慣習的に `=` の両端にはスペースを入れずに書きます。
## キーワード引数
関数を呼び出すときに引数名を指定すれば引数を順不同で渡すこともできます。
```python hl_lines="7"
def another_function(x, y):
print(f'The value of x is {x}')
print(f'The value of y is {y}')
def main():
another_function(y=10, x=20)
```
## `*args, **kwargs`
関数の引数名に `*` が付いたものがあると、その変数は複数の引数を 1 つのタプルとして受け取るようになります。
```python
def another_function(x, *args):
print(f'The value of x is {x}') # x: 0
print(f'The value of args is {args}') # args: (1, 2, 3)
def main():
another_function(0, 1, 2, 3)
```
また引数名に `**` を付けると複数のキーワード引数を 1 つの辞書として受け取るようになります。
```python
def another_function(x, **kwargs):
print(f'The value of x is {x}') # x: 0
print(f'The value of kwargs is {kwargs}') # kwargs: {'y': 10, 'z': 20}
def main():
another_function(0, y=10, z=20)
```
`*args, **kwargs` は併用することも可能です。
```python
def another_function(x, *args, **kwargs):
print(f'The value of x is {x}') # x: 0
print(f'The value of args is {args}') # args: (1, 2)
print(f'The value of kwargs is {kwargs}') # kwargs: {'y': 10, 'z': 20}
def main():
another_function(0, 1, 2, y=10, z=20)
```
!!! note
`*` 引数と `**` 引数はどのような名前にしても構いませんが、慣習的に `*args, **kwargs` が使われます。
## 引数のアンパック
関数に引数を渡すときにタプルに `*` を付けて渡すとタプルの各要素を個別の引数として渡せるようになります。
```python
def another_function(x, y, z):
print(f'The value of x is {x}') # x: 0
print(f'The value of y is {y}') # y: 1
print(f'The value of z is {z}') # z: 2
def main():
args = (0, 1, 2)
another_function(*args) # another_function(0, 1, 2)
```
また辞書に `**` を付けて渡すと辞書の各要素をキーワード引数として渡せるようになります。
```python
def another_function(x, y, z):
print(f'The value of x is {x}') # x: 0
print(f'The value of y is {y}') # y: 1
print(f'The value of z is {z}') # z: 2
def main():
kwargs = {'x': 0, 'y': 1, 'z': 2}
another_function(**kwargs) # another_function(x=0, y=1, z=2)
```
## 型ヒント
引数に特定の型だけを渡せるようにしたければ **型ヒント** を使用します。
```python
def another_function(x: int, y: int):
print(f'The value of x * y is {x * y}')
```
型ヒントを使用しなければどんな型の変数も渡せます。これはしばしば混乱の元となるので、なるべく型ヒントを使うようにしましょう。
## 戻り値
関数の呼び出し側に値を返却する場合は `return` を使用します。
```python
def plus_one(x):
return x + 1
def main():
x = plus_one(5) # x: 6
print(f'The value of x is {x}')
```
戻り値として返す型は複数あっても構いません。
```python
def another_function(x):
if x == 0:
return x + 3.14
else:
return x + 1
```
この関数は `x` が `0` のときは `float` 型を返し、そうでないときは `int` 型を返します。複数の型を返すことを想定してなければ、型ヒントを使うことで誤って意図しない型を返却してしまうことを防ぐことができます。
```python
def plus_one(x: int) -> int:
return x + 1
```
Python の関数は必ず戻り値を持っており、`return` を明示的に使わなかった場合は `None` が返ります。
```python
def f(x):
print(f'The value of x is {x}')
```
この関数は下記と同じ意味になります。
```python
def f(x):
print(f'The value of x is {x}')
return None
```
## 空実装
関数の実装を空にしたい場合は `pass` と書いておきます。
```python
# 何もしない関数
def empty_function():
pass
```
================================================
FILE: docs/ch02-03-comments.md
================================================
# コメント
`#` から始まる行はコメントと見なされます。コメントはプログラム上は無視されるため、自由にメッセージを書くことができます。
```python
# この行はコメントです。
```
コードの末尾に置くことも可能です。
```python
def main():
lucky_number = 7 # I’m feeling lucky today.
```
================================================
FILE: docs/ch02-04-conditions.md
================================================
# 条件文
ある条件を満たしている時だけ行いたい処理がある場合は条件文を使って処理を書きます。
## `if`
```python
#!/usr/bin/env python
def main():
check(3)
check(7)
def check(x):
if x < 5:
print('condition was true')
else:
print('condition was false')
if __name__ == '__main__':
main()
```
`x` の値が `5` 未満かどうかで出力されるメッセージが変化します。`if` に渡す値は必ずしも評価式である必要はありません。次のように値そのものを渡すこともできます。
```python
x = 10
if x:
print('x is not 0')
```
この条件は `x` を `bool` に変換した値が評価されます。値を `bool` に変換した場合の結果は次のように評価されます。
- `0, 0.0, '', b'', [], (), {}, set(), None` のとき `False`
- それ以外のとき `True`
## `elif`
複数の条件を見たい場合は `if, elif, else` を使います。
```python
def check(number):
if number % 4 == 0:
print('number is divisible by 4')
elif number % 3 == 0:
print('number is divisible by 3')
elif number % 2 == 0:
print('number is divisible by 2')
else:
print('number is not divisible by 4, 3, or 2')
def main():
check(10)
```
`else if` ではなく `elif` であることに注意してください。
!!! note "switch 文"
Python には switch 文がありませんので `if, elif, else` を使用して下さい。
## 空の条件文
条件文内の処理を空にしたい場合は `pass` と書いておきます。
```python
if x == 0:
pass
```
## 演算子
条件文で使用できる演算子には次のようなものがあります。
### `and`
複数の条件が成立するかどうかを調べるときに使用します。
```python
def check(x):
if x >= 0 and x < 10:
print('x is in [0, 10)')
def main():
check(10)
```
ただし変数の範囲チェックをする場合は `and` を使わなくてもシンプルな書き方ができます。
```python
def check(x):
if 0 <= x < 10: # x >= 0 and x < 10 と同じ
print('x is in [0, 10)')
def main():
check(10)
```
### `or`
複数の条件のうちどれか 1 つが成立するかどうかを調べるときに使用します。
```python
def check(x, y):
if x > 0 or y > 0:
print('Either x or y is a positive value')
def main():
check(10, -3)
```
### `in`
リスト・タプル・集合内に特定の要素が含まれているかどうかを調べます。
```python
def check(data, x):
if x in data:
print(f'{data} contains {x}')
def main():
check([0, 1, 2, 3, 4], 3)
```
辞書に対して使用するとキーの存在を調べることができます。
```python
def check(data, key):
if key in data:
print(f'{data} contains the value of key {key}')
def main():
check({'x': 0, 'y': 1, 'z': 2}, 'x')
```
### `is`
`is` は 2 つの変数が同じインスタンスを参照しているかどうかを調べるときに使用します。
```python
def check(x, y):
if x is y:
print('x is y == True')
def main():
x = [0, 1, 2]
y = x
z = x
check(y, z)
```
ここでいう「同じ」とは保持している値が等価という意味ではありません。参照しているインスタンスが同じかどうかを意味しています。言い換えれば変数 `x, y` に対して、これらの代入元の変数をたどっていって同じ変数にたどり着くなら `x is y` は `True` になります。
```python
x = [0, 1, 2]
y = x
z = y
x is y # True
y is z # True
z is x # True
a = [0, 1, 2]
b = a
x is a # False
x is b # False
```
変数に `None` が代入されているかどうかを調べるときは `==` ではなく `is` を使用します。
```python
x is None
```
================================================
FILE: docs/ch02-05-loops.md
================================================
# ループ文
リストの要素に対して繰り返し同様の処理を実行する場合は `for` や `while` を使います。
## `for`
リストに対して `for` を使う場合は次のように書きます。
```python
values = [0, 1, 2, 3, 4]
for value in values:
print(f'The value is {value}')
```
タプルの場合も同様にして `for` に渡すことができます。辞書も渡すことができますが、この場合キーが各ループで参照されます。
```python
items = {'a': 1, 'b': 2, 'c': 3}
for key in items:
print(f'The key is {key}')
```
値のループあるいはキーと値の両方をループで参照したい場合はそれぞれ `values(), items()` メソッドを使用します。
```python
items = {'a': 1, 'b': 2, 'c': 3}
for value in items.values():
print(f'The value is {value}')
for key, value in items.items():
print(f'The pair of key and value is ({key}, {value})')
```
### `range()`
整数を順にループさせたい場合は `range()` という関数を使って実現できます。
```python
for i in range(10):
print(f'The value is {i}')
```
`range(10)` で 0 以上 10 未満(すなわち 0 ~ 9) の整数をループします。`range()` は終了値だけでなく、開始値と刻み幅を指定することもできます。
```python
range(stop, start=0, step=1)
```
!!! note "例"
- `range(2, 10)`: 2, 3, 4, 5, 6, 7, 8, 9
- `range(2, 10, 2)`: 2, 4, 6, 8
- `range(10, 0, -1)`: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
## `while`
`while` は特定の条件が `True` である間ループ処理を続けるというものです。
```python
x = [10, 20, 30, 40, 50]
index = 0
while index < 5:
print(f'The value is {x[index]}')
index += 1
```
================================================
FILE: docs/ch02-06-list-comprehensions.md
================================================
# リスト内包表記
リスト内包表記とはリストを簡単に作成するための構文のことです。今 `[0, 2, 4, 6, ..., 98]` というリストを作ろうとした場合、
```python
x = []
for i in range(50):
x.append(i * 2)
```
のようにループ処理をして要素を詰めて作ることができますが、リスト内包表記を使うと下記のようにもっとシンプルに書くことができます。
```python
x = [i * 2 for i in range(50)]
```
リスト内包表記は別のコレクション型から変換を行う際によく使われます。例えば下記は辞書からタプルを要素とするリストを作成する例です。
```python
x = {'a': 10, 'b': 20, 'c': 30}
y = [(key, val) for key, val in x.items()] # [('a', 10), ('b', 20), ('c', 30)]
```
ループ処理を短く書くことができますので活用するとコードがシンプルになります。
================================================
FILE: docs/ch02-07-lambdas.md
================================================
# ラムダ式
ラムダ式とは関数を変数に代入して扱えるようにするものです。ラムダ式が必要になってくるシーンは関数を別の関数の引数として渡す必要があるときです。
!!! Tips
引数として渡される関数のことをコールバックといいます。
例として `map()` という組み込み関数について考えてみます。`map()` はリストなどのコレクションの各要素に何らかの変換を加えたコレクションを新たに作成する関数です。
```python
a = [1, 2, 3, 4, 5]
```
上記のリストの各要素を 2 乗して `[1, 4, 9, 16, 25]` というリストを `map()` で作成するには次のようにします。
```python
list(map(lambda x: x * x, a))
```
`map()` の第 1 引数にはラムダ式が渡されています。ラムダ式は機能的には通常の関数と同様の機能を提供しており
```python
lambda x: x * x
```
というラムダ式は下記の関数と等価なものになっています。
```python
def power(x):
return x * x
```
`map()` は第 2 引数に渡されたコレクションの各要素をラムダ式に渡し、その戻り値を逐次返却していくという振る舞いをします。ちなみに `map()` には上記の `power()` のような通常の関数を渡すこともできます。
```python
list(map(power, a))
```
ラムダ式が通常の関数と異なる点は
- 名前を持たない(無名関数)
- 必要なときに即時に定義できる
という点です。`map()` に渡すためのちょっとした処理に対して、わざわざ関数定義をするのも面倒だといったケースではラムダ式が使われます。その他の例もいくつか挙げておきます。
```python
list(map(lambda x: 2 * x, a)) # [2, 4, 6, 8, 10]
list(map(lambda x: (x, 2 * x + 1), a)) # [(1, 3), (2, 5), (3, 7), (4, 9), (5, 11)]
```
`map()` の戻り値は [ジェネレータ](./ch07-01-generators.md) というオブジェクトが返ります。ジェネレータ自体はリストではないですが、ループ文に渡すことでリストと同じように変換結果の要素を取り出すことができます。
```python
for item in map(lambda x: x * x, a):
print(item) # 1, 4, 9, 16, 25
```
ジェネレータを `list()` に渡すとリストに変換してくれますが、ループ文で要素を参照するだけであればリストに変換する必要はありません。インデックス参照などがしたい場合にリストに変換すると良いでしょう。`map()` のようにコレクションとコールバックを受け取りジェネレータを返す関数は他にもいくつか用意されています。代表的なものには [itertools] があります。
[itertools]: https://docs.python.org/ja/3/library/itertools.html
================================================
FILE: docs/ch03-01-classes.md
================================================
# クラス
クラスとは新しい型を定義するための仕組みです。クラスを使うことで複数の変数と関数を集約した変数を作ることができるようになります。
## 定義
クラスを定義するには `class` キーワードにクラス名を付けて定義します。下記の `Rectangle` クラスは長方形に関する情報を扱うクラスで、長方形の幅と高さをメンバ変数として持ったクラスになります。
```python hl_lines="5 6 7 8"
#!/usr/bin/env python
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def main():
rectangle = Rectangle(10, 20)
print(f'The value of width is {rectangle.width}')
print(f'The value of height is {rectangle.height}')
if __name__ == '__main__':
main()
```
## `__init__()`
`__init__()` はいわゆるコンストラクタです。コンストラクタには 2 つの役割があります。
1. インスタンス化をするときに最初に呼び出される
1. クラスのメンバ変数を定義し、それを初期化する
クラスにどのようなメンバ変数を用意したいかということも含めてコンストラクタで定義することに注意して下さい。
引数には初期化に必要な値を渡すことができます。ただし第 1 引数は必ず `self` という引数にして下さい。`self` については後述します。
クラスで扱うメンバ変数を定義したいときは次のようにします。
```python hl_lines="3 4"
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
```
これはクラスのメンバ変数として `width, height` を用意するという意味になります。これで `Rectangle` という型が作成され、その型は `width, height` をメンバ変数として持っていることになります。
`width` に `10` 、`height` に `20` を渡して `Rectangle` の変数を作るには次のようにします。
```python
def main():
rectangle = Rectangle(10, 20)
print(f'The value of width is {rectangle.width}')
print(f'The value of height is {rectangle.height}')
```
## `self`
`Rectangle` をインスタンス化する際は `10, 20` という 2 つの値しか渡してないのに `__init__()` の第 1 引数には `self` という変数が含まれており、合計 3 つの引数が用意されています。これは一体どういうことでしょうか。この謎はクラスのインスタンス化の仕組みを理解すれば分かってきます。
`rectangle = Rectangle(10, 20)` という処理をイメージで説明すると、だいたいこんな感じになります。
```python
rectangle = (Rectangle クラスの変数を用意) # この時点では rectangle.width, rectangle.height は存在しない
Rectangle.__init__(rectangle, 10, 20) # rectangle.width = width, rectangle.height = height という処理が実行される
```
すなわち `self` というのはクラスインスタンスを表す変数です。
## 関数
クラスには関数を定義することもできます。クラスのメンバとして定義された関数はメソッドとも呼ばれます。
```python hl_lines="10 11 16"
#!/usr/bin/env python
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def main():
rectangle = Rectangle(10, 20)
area = rectangle.area()
print(f'The area is {area}')
if __name__ == '__main__':
main()
```
クラスに関数を定義するときは第 1 引数が `self` である必要があります。これは `rectangle.area()` という呼び出しは実際には `Rectangle.area(rectangle)` のように呼び出されるためです。
## 継承
定義済みのクラスのメンバを持つ新たなクラスを作成することもできます。
```python
#!/usr/bin/env python
class Square(Rectangle):
def __init__(self, size):
super().__init__(size, size)
def main():
square = Square(10)
area = square.area()
print(f'The area is {area}')
if __name__ == '__main__':
main()
```
`Square` クラスは `Rectangle` クラスのメンバを全て引き継いだクラスになっており、これをクラスの継承といいます。`Rectangle` が長方形を扱うクラスなのに対し、`Square` は正方形を扱うクラスになっており、`Rectangle` が持つ全てのメンバ `square.width, square.height, square.area()` が参照できます。
## インターフェース
Python にはインターフェースが存在しません。また多態性を実現するのにクラスに継承関係を作る必要もありません。それを確認するために継承関係にない 2 つのクラス `A, B` を作ってみます。またメソッドとして `greet()` という関数をそれぞれのクラスで定義しておきます。
```python hl_lines="5 6 13 14"
class A:
def __init__(self):
pass
def greet(self):
print('This is class A')
class B:
def __init__(self):
pass
def greet(self):
print('This is class B')
```
次に `greet()` 関数をメンバに持った変数を受け取るような関数 `call()` を次のように作成します。
```python
def call(x):
x.greet()
```
この関数に `A, B` の変数をそれぞれ渡してみます。
```python
def main():
call(A())
call(B())
```
ソースコードの全体は次のようになります。
```python
#!/usr/bin/env python
class A:
def __init__(self):
pass
def greet(self):
print('This is class A')
class B:
def __init__(self):
pass
def greet(self):
print('This is class B')
def call(x):
x.greet()
def main():
call(A())
call(B())
if __name__ == '__main__':
main()
```
このコードは問題なく実行することができます。Python は例え継承関係を持っていなくてもクラスの構造(メンバ変数や関数の名前・引数)が一致していれば同種の型として扱えるようになります。このような多態性の実現方法のことを **ダックタイピング** といいます。
================================================
FILE: docs/ch03-02-scopes.md
================================================
# スコープ
他の言語ではクラスメンバに対して `public, protected, private` といったアクセス指定子を指定することができますが Python ではそのような指定はできず、すべてのメンバが `public` として扱われます。ですが習慣的に下記のような命名規則でメンバのスコープを区別するようになっています。
| スコープ | 命名規則 |
|-----------|--------------|
| public | `method()` |
| protected | `_method()` |
| private | `__method()` |
Python では `__method()` のような private メソッドを作ることはそれほど多くはありません。隠蔽したいメンバを定義するときは `_method()` のような protected メソッドを定義することのほうが多いです。
================================================
FILE: docs/ch03-03-special-attributes.md
================================================
# 特殊属性
クラスには特殊属性と呼ばれるメソッドが存在しており、クラスを使う際に使用される構文はどれも特殊属性の呼び出しに変換されて実行されます。代表的な特殊属性をいくつか紹介します。
下記のようなクラスを定義したときの特殊属性には次のようなものがあります。
```python
import math
class Point:
"""Point クラスです。"""
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self):
return math.sqrt(self.x * self.x + self.y * self.y)
```
## `__doc__`
`Point.__doc__` という変数には `"""Point クラスです。"""` という文字列が入ります。`""" ... """` というトリプルクオテーションでくくられた文字列は改行を含む複数行の文字列を定義できる文字列です。
```python
"""この文字列は
一つの文字列として
扱われます。"""
```
`__doc__` は docstring と呼ばれ、ドキュメンテーションをする際に使用される文字列として扱われます。
## `__init__`
クラスインスタンスを作成するときは `__init__()` が呼び出されます。
```python
point = Point(10, 20) # point.__init__(10, 20)
```
## `__getattribute__`
クラスのメンバやメソッドを参照したときは `__getattribute__()` が呼び出されます。
```python
point = Point(10, 20)
point.x # point.__getattribute__('x')
point.distance() # point.__getattribute__('distance')()
```
## `__getattr__`
`__getattribute__()` でのメンバ参照に失敗した場合は `__getattr__()` が呼び出されます。このような事が起こるのはクラスのメンバとして定義されていないものにアクセスしようとしたときに起こります。
```python
point.x2 # point.__getattr__('x2')
```
`__getattr__()` は明示的には定義されません。使用するには自分で定義する必要があります。
```python
class Point:
...
def __getattr__(self, item):
if item == 'x2':
return self.x * self.x
elif item == 'y2':
return self.y * self.y
```
## `__getitem__`
クラスインスタンスに対して `[]` を使用した際には `__getitem__()` が呼び出されます。
```python
point = Point(10, 20)
point['x'] # point.__getitem__('x')
```
`__getitem__()` を使用するには自分で定義する必要があります。
```python
class Point:
...
def __getitem__(self, item):
if item == 'x':
return self.x
elif item == 'y':
return self.y
```
## `__setattr__`
メンバへの代入が行われた場合は `__setattr__()` が呼び出されます。
```python
point = Point(10, 20)
point.x = 30 # point.__setattr__('x', 30)
```
## `__eq__`
インスタンスに対して `==` が使用された場合は `__eq__()` が呼ばれます。
```python
point1 = Point(10, 20)
point2 = Point(30, 40)
point1 == point2 # point1.__eq__(point2)
```
その他類似の特殊属性が下記の通り用意されています。
| 特殊属性 | 意味 |
|----------|--------------------|
| `__ne__` | `point1 != point2` |
| `__le__` | `point1 <= point2` |
| `__lt__` | `point1 < point2` |
| `__ge__` | `point1 >= point2` |
| `__gt__` | `point1 > point2` |
## `__str__`
クラスインスタンスに対して文字列型へのキャストを行うと `__str__()` が呼び出されます。明示的なキャストでなくても文字列への変換が必要とされるケースでも同様の振る舞いをします。
```python
point = Point(10, 20)
print(point) # print(point.__str__())
```
`__str__()` はデフォルトで定義されていますが、大抵の場合は有益な文字列にはなっていないので自分で定義したほうが良いです。
```python
class Point:
...
def __str__(self):
return f'<Point(x={self.x}, y={self.y})>'
```
================================================
FILE: docs/ch03-04-properties.md
================================================
# プロパティ
クラスのメンバで変数のように参照することのできる関数のことをプロパティといいます。
```python
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@property
def distance(self):
return math.sqrt(self.x * self.x + self.y * self.y)
```
`@property` のように `@` ではじまるキーワードは **デコレータ** といって関数やクラスに特殊な振る舞いを注入することのできる機能になっています。`distance()` は関数として定義されていますが `@property` デコレータがついていると変数のように参照することができるようになります。
```python
point = Point(10, 20)
point.distance # 22.360679775
```
プロパティは `point.distance()` のように呼ぶことはできません。
メンバ変数 `x, y` を隠蔽して、代わりにプロパティを提供すると代入ができなくなるので安全です。
```python
import math
class Point:
def __init__(self, x, y):
# _ をつけて隠蔽していることを示す
self._x = x
self._y = y
@property
def x(self):
return self._x
@property
def y(self):
return self._y
@property
def distance(self):
return math.sqrt(self.x * self.x + self.y * self.y)
```
```python
point = Point(10, 20)
print(point.x, point.y) # _x, _y の参照は可能
point.x = 30 # _x, _y への代入はできない
```
プロパティに対して代入を行いたい場合は `setter` プロパティを定義します。
```python
@property
def x(self):
return self._x # getter
@x.setter
def x(self, value):
self._x = value # setter
```
```python
point.x = 30 # OK
```
プロパティの利点は `get_x()` のような関数を定義しなくても、まるで変数を直接参照しているようなコードが書ける点にあります。これはクラスを利用する人からするとシンプルなコードが書けるので便利です。ただしプロパティは変数と同じくらいに気軽に参照するメンバになるので、パフォーマンスが遅い関数をむやみにプロパティとして定義するのは避けたほうが良いです。
================================================
FILE: docs/ch04-01-modules.md
================================================
# モジュール
モジュールとは関数やクラスなどを別ファイルで利用できる状態で整理した Python のソースコードのことです。これまでのソースコードは実行することを念頭に実装しましたが、モジュールは別のファイルから取り込まれることを念頭に実装を行います。
## モジュールの書き方
拡張子 `.py` を持った通常の Python ファイルとして作成すれば問題ありません。ただしシバンはモジュールを実装するときには不要です。たとえ書いても悪さをすることはありませんが、実行するスクリプトとしては使用しないので書く意味はあまりありません。
フィボナッチ数列 `0, 1, 1, 2, 3, 5, 8, ...` を出力するような関数 `fib(n)` をモジュールとして書いてみましょう。
**fib.py**
```python
def fib(n):
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a + b
print()
```
`fib.py` を別のファイル `main.py` に取り込んでこの関数を実行するには次のように書きます。
**main.py**
```python
#!/usr/bin/env python
import fib # fib.py を取り込む
def main():
fib.fib(10) # fib.py 内の fib() を呼び出す
if __name__ == '__main__':
main()
```
## モジュール作成の注意点
Python のコードは必ずしも関数内に含める必要はなく、ファイル内に直接処理を書くこともできます。
**fib.py**
```python
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a + b
print()
```
しかしこの状態で `main.py` から `import fib` をすると import した時点でフィボナッチ数列を出力する処理が突然実行されてしまいます。こういった問題が起こらないようにモジュールとして提供する機能は基本的に関数やクラスにまとめておく必要があります。
## `from-import`
モジュール内の関数やクラスにアクセスするときは `モジュール名.関数名` のように `.` を使ってアクセスしますが、モジュール名や関数名が長くなってくると少し書き方が面倒になります。こういうときは `from` 文を使って参照名を短くすることができます。
```python
from (モジュール名) import (関数名)
```
このようにすると `モジュール名.関数名` としていたところが `関数名` だけで参照できるようになります。
```python
#!/usr/bin/env python
from fib import fib # fib.py 内の fib() をインポート
def main():
fib(10) # fib() を呼び出す
if __name__ == '__main__':
main()
```
ただし `import fib` とインポートしたときは `fib.py` 内のすべての関数やクラスが参照できるのに対し、 `from fib import fib` は `fib()` のみが参照できます。複数の定義をインポートしたいときは次のようにします。
```python
from (モジュール名) import (関数名), (関数名), ...
```
モジュール内の全定義をまとめて `from` でインポートしたければ次のようにします。
```python
from (モジュール名) import *
```
ただしこのインポート文は余計な定義をインポートしてしまう恐れがあるため推奨されません。モジュールのインポート方法は次の優先度で検討して下さい。
| おすすめ | インポート文 |
|--------------|---------------------------------------|
| :heart_eyes: | `import (モジュール名)` |
| :smiley: | `from (モジュール名) import (関数名)` |
| :confounded: | `from (モジュール名) import *` |
## `__name__`
`__name__` は Python で使用できる隠し変数の 1 つで、モジュール名を表す文字列が入っています。例えば `foo.py` というモジュールであれば `__name__` は `'foo'` になります。
```python
import foo
print(foo.__name__) # 'foo'
```
しかしこの変数がモジュール名になるのは別のモジュールからインポートされたときだけです。`foo.py` を直接実行した場合は `__name__` には `'__main__'` が入ります。
**foo.py**
```python
#!/usr/bin/env python
print(__name__)
```
```shell
$ python foo.py
__main__
```
つまり `__name__` の値がモジュール名になるのか `'__main__'` になるのかを見ることでインポートされようとしているのかどうかを判断することができるようになります。
[Hello, World!](./ch01-02-hello-world.md) のソースコードで出てきた
```python
if __name__ == '__main__':
main()
```
という構文は自分がインポートされてないときだけ `main()` を呼び出すという意味になります。 `__name__` の値を見ることで 1 つのファイルで実行スクリプトとモジュールの両方を実装することができるようになります。
================================================
FILE: docs/ch04-02-packages.md
================================================
# パッケージ
モジュールを使うことで関数やクラスをまとめることができますが、1 つのモジュール内にたくさんの定義を含めてしまうとコードが長くなってしまい分かりづらくなってしまいます。そういうときはファイルを分割して複数のモジュールを作成し、それを 1 つのディレクトリに集約することで綺麗に整理することができます。このように複数のモジュールを集約したディレクトリのことをパッケージといいます。
モジュールとパッケージの関係はファイルシステムでいうところのファイルとディレクトリの関係に一致します。
| ファイルシステム | Python |
|------------------|------------|
| ファイル | モジュール |
| ディレクトリ | パッケージ |
ファイル・ディレクトリのことを Python の世界ではモジュール・パッケージと呼んでいると理解して概ね問題ありません。
## パッケージの作り方
パッケージはディレクトリのことだと説明しましたが、単に Python のソースコードをディレクトリに集約しただけではパッケージにはなりません。ディレクトリをパッケージとして扱うためには `__init__.py` というファイルをディレクトリ内に用意しておく必要があります。
```
example
├── __init__.py # これがないとパッケージとは呼べない
├── a.py
└── b.py
```
`__init__.py` は空ファイルとして作成してもらって問題ありません。Python のソースコードをディレクトリに集約するときは必ず `__init__.py` を作るようにしましょう。
## パッケージを使用する
パッケージもモジュールと同様、他のファイルに取り込まれることを想定した機能なので `import` 構文を使って取り込む
ことができます。いま、パッケージの構成が次のようになっていたとしましょう。
```
example
├── __init__.py
├── a.py
│ └── def fib(n)
└── b.py
```
このとき、関数 `fib(n)` を呼び出したいときには次のようにします。
```python
#!/usr/bin/env python
import example.a
def main():
example.a.fib(10)
if __name__ == '__main__':
main()
```
`パッケージ名.モジュール名.関数名` のように `.` で区切って関数にアクセスすることができます。ちなみにパッケージ内にパッケージが含まれていても問題ありません。
!!! warning "モジュール・パッケージ名の命名規則"
モジュール名・パッケージ名に `-` を使用するのは避けましょう。なぜなら `xxx-yyy` というモジュール・パッケージを作ったとき
```python
import xxx-yyy
```
のように import することになりますが `-` が引き算だと解釈されてしまいます。一般的に命名規則は次のようなルールになっています。
- 全て小文字
- パッケージ名に区切り文字は禁止
- モジュール名の区切り文字は `_` を使用
モジュール名であっても区切り文字はできるだけ使用しない方が好まれます。パッケージ名は例え複数語であっても区切り文字は使用しません。
## `__init__.py`
`__init__.py` というファイルがどのような働きをするのか見ていきたいと思います。上記のサンプルにある
```python
import example.a
```
というインポートを下記のように変更すると `example.a.fib()` の参照が正しくできなくなります。
```python
import example
```
両者の違いはインポートにモジュール名 `example.a` を指定しているか、パッケージ名 `example` を指定しているかにあります。インポートでパッケージ名を指定したときは `example` 配下にある `__init__.py` を探してインポートを実行するという振る舞いをします。
```python
import example # example/__init__.py を参照する
```
もし `__init__.py` 内に何も記述がなければ `import example` では何も取り込まれないことになります。そのため `example.a.fib()` の参照はエラーになってしまいます。たとえ `example` ディレクトリ配下に `a.py` があったとしても Python からは `__init__.py` の定義に従ってパッケージの階層を参照しているということです。
これに対して `import example.a` の場合だとうまくいくのは、モジュール `example/a.py` を直接インポートしており `__init__.py` の参照が起こらないからです。
`import example` として `example.a.fib()` を参照できるようにするためには `__init__.py` 内でモジュール `a` が参照できるようにしてあげる必要があります。これは下記のようにすることで実現できます。
**`example/__init__.py`**
```python
from . import a
```
これは `__init__.py` と同一のディレクトリ階層から `a` をインポートするという構文で **相対インポート** といいます。こうすることで `__init__.py` 内でモジュール `a` が参照できるようになるため `import example` としても `example.a.fib()` が参照できるようになります。
## モジュール検索パス
`__init__.py` 内の記述を
```python
import a
```
と書いてもうまくいきません。 `import example` としてもモジュール `a` が見つからないというエラーが起こってしまいます。これは Python がモジュールを探しに行くときに、決まったルールで検索をしているからです。Python はモジュールを探しに行くときに次の順序でモジュールを検索します。
1. ビルトインモジュール(Python に最初から組み込まれたモジュール)
2. `python` コマンドに渡したファイルのあるディレクトリ直下
例えばファイル構成が下記のようになっていた場合 `python main.py` と実行して Python がモジュールを探しに行くディレクトリは `root` 直下になります。
```
root # ここ
├── example # ここは探しに行かない
│ ├── __init__.py
│ └── a.py
└── main.py
```
ソースコードのどの場所で `import a` を書いても `root` 直下から `a.py` を探そうとします。相対インポートはこのルールに従わずにモジュールを検索してくれるため、うまくいくということです。
================================================
FILE: docs/ch04-03-pip.md
================================================
# pip
`pip` はインターネットで公開されている Python パッケージを取得するためのパッケージ管理ツールです。
## 使い方
[requests] という HTTP に関する機能を取り扱う有名なライブラリがあります。これを `pip` で取得して使ってみましょう。
[requests]: http://docs.python-requests.org/en/master/
ターミナル上で次のコマンドを実行して下さい。
```shell
$ pip3 install requests
```
成功したら次のようなソースコードを書いてみましょう。
```python
#!/usr/bin/env python
import requests
def main():
response = requests.get('http://example.com')
print(response.text)
if __name__ == '__main__':
main()
```
実行すると [http://example.com](http://example.com) のページの HTML 文字列が出力されると思います。
## PyPI
`pip` によってインストールされるパッケージはどこから取得されているのでしょうか。 Python は [PyPI] というパッケージを登録しておける Web サイトがあり、 `pip` を実行すると PyPI からパッケージがインストールされます。上記の requests も下記の通り PyPI に登録されています。
!!! info "PyPI"
[https://pypi.org/project/requests/](https://pypi.org/project/requests/)
[PyPI]: https://pypi.org/
## freeze
インストール済みのパッケージ一覧を確認するには `freeze` というコマンドを実行します。
```shell
$ pip3 freeze
certifi==2019.3.9
chardet==3.0.4
idna==2.8
requests==2.21.0
urllib3==1.24.1
```
`requests` とその依存パッケージがバージョン番号とともに表示されます。
## requirements.txt
インストールしたいパッケージをあらかじめファイルに列挙しておき、そのファイルを指定することでもインストールすることができます。インストールしたいパッケージを記述したファイルは通常 `requirements.txt` という名前で保存します。`requirements.txt` を指定してインストールをするには次のようにします。
```shell
$ pip3 install -r requirements.txt
```
`requirements.txt` は自分の環境にパッケージをインストールする目的で使用することはあまりありません。むしろ他の人の環境で自分が使っているパッケージをインストールして欲しいときに使用します。また `requirements.txt` は手で作成する必要はなく、`pip freeze` の結果を保存しておくだけで使用できます。`pip freeze` の結果にあるような `==version` という形式のものを `pip` でインストールすると指定されたバージョンをインストールしてくれるため、自分で入れたバージョンと全く同じものを他の人の環境でもインストールしてもらえるようになり、環境差分をなくすことができます。
まとめるとパッケージ管理は次のような手順で行うことになります。
**自分の環境**
```shell
$ pip3 install name1 name2 ...
$ pip3 freeze > requirements.txt
```
`requirements.txt` は Git などでバージョン管理をしておきます。
**他の人の環境**
```shell
$ pip3 install -r requirements.txt
```
================================================
FILE: docs/ch04-04-venv.md
================================================
# venv
`pip` を使ってサードパーティ製のパッケージをインストールすることができましたが、インストールしたいパッケージが稀に競合を起こすことがあります。
| プログラム | `requests` の要求バージョン |
|------------|-----------------------------|
| program1 | 2.21.0 |
| program2 | 2.20.1 |
program1 では `requests` 2.21.0 を使おうとしているのに対し program2 では 2.20.1 を使いたかったとすると、システムの中に複数バージョンの `requests` をインストールしておく必要が出てきます。もし使用したいパッケージのバージョンが意図しないものになっているとプログラムが正しく動かない可能性が出てきます。
## 仮想環境の作成
このような競合を解決するために Python には各プログラムで使用したいパッケージを互いに影響がない形で個別に管理するための仕組みが用意されており、仮想環境と呼ばれます。仮想環境を作成するためのツールは `venv` と呼ばれ、Python に標準で同梱されています。
試しにこれから 2 つの仮想環境を作成し、それぞれに異なるバージョンの `requests` をインストールしてみます。まず下記のように 2 つのディレクトリを作成します。
```shell
$ mkdir program1 program2
```
次に program1 配下で仮想環境を作成します。
=== "macOS"
```shell
$ cd program1
$ python3 -m venv .venv
```
=== "Windows"
```shell
$ cd program1
$ py -3 -m venv .venv
```
program1 配下に `.venv` というディレクトリが作成されます。仮想環境を有効にするために下記のコマンドを実行します。
=== "macOS"
```shell
$ . .venv/bin/activate
(.venv) $
```
=== "Windows"
```shell
$ .venv\Scripts\activate.bat
(.venv) $
```
この状態で `requests` 2.21.0 をインストールしてみます。パッケージをバージョン指定でインストールしたければ次のように実行します。
```shell
(.venv) $ pip install requests==2.21.0
```
!!! note
仮想環境が有効な場合であれば pip コマンドは `pip` が使用できます。
`pip3` コマンドも使用できますが、どちらを使用しても効果は同じです。
一方で仮想環境が無効な状態であれば `pip` は Python 2 系の古い pip を呼び出すことになり
`pip3` とは挙動が異なるので注意してください。
この `requests` は .venv ディレクトリ配下にインストールされ、仮想環境が有効でない限りは参照することができないようになっています。 `pip freeze` を使ってインストールされているパッケージ一覧を確認してみます。
```shell
(.venv) $ pip freeze
certifi==2019.3.9
chardet==3.0.4
idna==2.8
requests==2.21.0
urllib3==1.24.1
```
仮想環境を無効にする場合は `deactivate` というコマンドを実行します。このコマンドは仮想環境が有効なときだけ使用することができます。
=== "macOS"
```shell
(.venv) $ deactivate
$
```
=== "Windows"
```shell
(.venv) $ venv\Scripts\deactivate.bat
$
```
program2 配下でも同様に仮想環境を作成してみます。`requests` のバージョンは 2.20.1 を使用します。
=== "macOS"
```shell
$ cd program2
$ python3 -m venv .venv
$ . .venv/bin/activate
(.venv) $ pip install requests==2.20.1
(.venv) $ pip freeze
certifi==2019.3.9
chardet==3.0.4
idna==2.7
requests==2.20.1
urllib3==1.24.1
```
=== "Windows"
```shell
$ cd program2
$ py -3 -m venv .venv
$ .venv\bin\activate.bat
(.venv) $ pip install requests==2.20.1
(.venv) $ pip freeze
certifi==2019.3.9
chardet==3.0.4
idna==2.7
requests==2.20.1
urllib3==1.24.1
```
program1, program2 配下いずれかの仮想環境を有効にすることで異なるバージョンの `requests` が使用できるようになっています。ちなみに仮想環境が有効でない状態でインストールされたパッケージは全プログラムから参照可能なので競合を起こす可能性があり、使用すべきではありません。そのため、前章の `pip` の章でインストールしたパッケージは下記のように削除しておくことをおすすめします。
```shell
$ pip3 freeze > requirements.txt
$ pip3 uninstall -y -r requirements.txt
```
================================================
FILE: docs/ch04-07-project-structures.md
================================================
# プロジェクト構成
Python のソースコードを管理する際にディレクトリの構成をちゃんと考えておくことはとても重要なことです。なぜなら Python は適切な構成になっていないとプログラムを正しく動かすことができなくなるからです。そこで Python 開発ではどのような構成で管理すれば問題が起こりにくいのかについて説明します。
## The Hitchhiker's Guide to Python
Python の理想のプロジェクト構成は Kenneth Reitz 氏によって推奨されている構成に従うのがよいでしょう。どのような構成なのかは [The Hitchhiker’s Guide to Python] というサイトの [Structuring Your Project] の章で詳しく書かれていますのでそちらを参考にしてもらうことにして、ここでは特に注意すべきことについてまとめておきます。
ディレクトリの基本構成は次のとおりです。
```
(project)
├── (project) ............ プログラムのソースコードディレクトリ
│ ├── __init__.py
│ └── *.py
└── tests ................ 単体テストのソースコードディレクトリ
├── __init__.py
└── *.py
```
`(project)` の部分は開発する Python プログラムの名前を付けます。`sample` というプログラムを開発するなら
```
sample
├── sample
│ ├── __init__.py
│ └── *.py
└── tests
├── __init__.py
└── *.py
```
のようになります。大事なのはプログラムのソースコードは必ず `(project)` 配下の 1 つのディレクトリ内に集約させるということです(単体テストのソースコードは除く)。なぜならディレクトリは Python のパッケージを構成するものなので、複数のディレクトリでソースコードを管理すると複数の Python パッケージを開発していることになります。しかし通常 1 つのプロジェクト内に複数のパッケージを含めて開発することはありません。
ソースコードが 1 つで十分な場合はディレクトリを用意しなくても大丈夫です。
```
(project)
├── (project).py ........... プログラムのソースコードディレクトリ
└── test_(project).py ...... 単体テストのソースコードディレクトリ
```
下記は良くない構成の一例です。
```
sample
├── sample
│ ├── __init__.py
│ └── *.py
├── libs ................. よくないディレクトリ
│ ├── __init__.py
│ └── *.py
└── tests
├── __init__.py
└── *.py
```
下記は `sample/__init__.py` が用意されていないため問題のある構成です。ディレクトリ内に Python ファイルを入れる場合は必ず `__init__.py` を用意してください。
```
sample
├── sample
│ └── *.py
└── tests
└── *.py
```
下記のような構成もやってしまいがちですが適切ではありません。
```
sample
├── sample
│ ├── __init__.py
│ └── *.py
├── main.py
└── tests
├── __init__.py
└── *.py
```
**main.py**
```python
#!/usr/bin/env python
import sample
def main():
# sample パッケージを使った処理
...
if __name__ == '__main__':
main()
```
この構成の問題点はライブラリとアプリケーションの区別ができていない構成になっているという点です。
| 構成 | 説明 |
|------------------|------------------------------------------------|
| ライブラリ | 他のプログラムから `import` して使うプログラム |
| アプリケーション | 直接実行するプログラム |
この例だと `sample` はライブラリで `main.py` はアプリケーションという位置づけになります。もしライブラリとアプリケーションの両方の側面を持つプログラムを書きたいという場合は `main.py` を `sample/__main__.py` という名前で保存してください。
```
sample
├── sample
│ ├── __init__.py
│ ├── __main__.py
│ └── *.py
└── tests
├── __init__.py
└── *.py
```
そして `main()` を実行したい場合はターミナル上で次のようにします。
=== "macOS"
```shell
$ python3 -m sample
```
=== "Windows"
```shell
$ py -3 -m sample
```
こうすると `sample/__main__.py` が実行されるようになります。決して
=== "macOS"
```shell
$ python3 sample/__main__.py
```
=== "Windows"
```shell
$ py -3 sample/__main__.py
```
のように実行してはいけません。たとえ動いたとしてもいつもうまくいくと期待しないほうがいいでしょう。
[The Hitchhiker’s Guide to Python]: https://docs.python-guide.org/
[Structuring Your Project]: https://docs.python-guide.org/writing/structure/
================================================
FILE: docs/ch05-01-files.md
================================================
# ファイル操作
ファイルの読み書きをする方法について説明します。
## 読み込み
`file.txt` というファイルを読み込み、1 行ずつプリントするプログラムは次のように書きます。
**main.py**
```python
#!/usr/bin/env python
def main():
f = open('file.txt')
for line in f:
print(line)
f.close()
if __name__ == '__main__':
main()
```
`for` を使わずに 1 行読み込む場合は `readline()` メソッドを使います。
```python
print(f.readline())
```
注意が必要なのは読み込んだ各行の文字列は末尾の改行も含んでいるということです。例えば `file.txt` が次のような内容だった場合
**file.txt**
```
aaa
bbb
ccc
```
`for` や `readline()` で読み込んだ各行の文字列は次のようになります。
```python
'aaa\n'
'bbb\n'
'ccc\n'
```
`print()` は文字列をプリントした後に自動で改行を 1 つ書き出すため、これらの文字列をプリントすると改行が 2 つ入った状態で出力されてしまいます。
```shell
$ python main.py
aaa
bbb
ccc
```
これを解決するには読み込んだ文字列に対して `strip()` というメソッドを呼び出します。 `strip()` は文字列の両端にある空白や改行を削除するメソッドです。
```python
#!/usr/bin/env python
def main():
f = open('file.txt')
for line in f:
print(line.strip())
f.close()
if __name__ == '__main__':
main()
```
その他次のようなメソッドでも文字列を読み込むことができます。
| メソッド | 説明 | 例 |
|---------------|-----------------------------------|-------------------------------|
| `readlines()` | 各行をリストで読み込む | `['aaa\n', 'bbb\n', 'ccc\n']` |
| `read()` | 全行を 1 つの文字列として読み込む | `'aaa\nbbb\nccc\n'` |
## 書き込み
ファイルを書き込む処理は次のように書きます。
```python
#!/usr/bin/env python
def main():
f = open('file.txt', 'w')
f.write('Hello, World!\n')
f.close()
if __name__ == '__main__':
main()
```
`open()` の第 2 引数に `'w'` を付けることで書き込みモードでファイルをオープンします。そして `write()` メソッドに書き出したい文字列を渡すことでファイルに書き出すことができます。 `write()` には文字列しか渡すことができませんので、数値などを書き出したいときは f-string を使って文字列に変換する必要があります。また `write()` は `print()` とは違って改行を自動で付与しないので、改行したいときは明示的に `'\n'` を渡す必要があります。
================================================
FILE: docs/ch05-02-contexts.md
================================================
# コンテキスト
`open()` を使ってファイルの読み書きをした後は必ず `close()` を使ってファイルを閉じる必要があります。しかしファイルの閉じ忘れがよくあるミスの 1 つです。このファイルの閉じ忘れをなくすために Python にはコンテキストマネージャという機能が用意されています。コンテキストマネージャを使えばファイルの読み書きが不要になったときに暗黙的にファイルを閉じてくれるようになります。
## `with` 文
コンテキストマネージャを使うには `with` 文という構文を使用します。
```python
f = open('file.txt')
```
のように記述していた部分を
```python
with open('file.txt') as f:
```
という構文で書き直します。そうするとファイルインスタンス `f` は `with` のブロック内だけで使用できるようになり、ブロックを抜けると暗黙的に `f.close()` を読んでファイルをクローズしてくれるようになります。下記は `with` を使ってファイルを読み込む例です。
**main.py**
```python
#!/usr/bin/env python
def main():
with open('file.txt') as f:
for line in f:
print(line)
if __name__ == '__main__':
main()
```
ファイルの閉じ忘れを防ぐためにもファイル操作を行う際はいつもコンテキストマネージャを用いたほうが良いでしょう。
================================================
FILE: docs/ch05-03-csv.md
================================================
# CSV
Python で CSV の読み書きを行いたい場合は `csv` モジュールを使います。
## 読み込み
### reader
CSV ファイルを読み込む場合は `open()` でファイルを開いた後、ファイルオブジェクトを `csv.reader()` に渡します。
```python
#!/usr/bin/env python
import csv
def main():
with open('example.csv', newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row) # row は CSV の各行になる
if __name__ == '__main__':
main()
```
!!! warning "注意"
`newline=''` というのはファイルの改行コードをそのまま読み込むというオプションです。これを付けないと CRLF, LF, CR などの改行コードはすべて LF に変換されてファイルが読み込まれますが CSV を読むときはこの変換をすべきでないとされています。
`row` にはカンマ区切りの要素がリストで保存されます。
**example.csv**
```
0,A
1,B
2,C
```
上記のファイルに対しては `row` は下記のようになります。
```python
[0, 'A']
[1, 'B']
[2, 'C']
```
### DictReader
CSV が下記のようなヘッダを持つ場合は `reader()` の代わりに `DictReader` を使うと各行を辞書で読むことができるようになります。
**example.csv**
```
id,name
0,A
1,B
2,C
```
```python
#!/usr/bin/env python
import csv
def main():
with open('example.csv', newline='') as f:
reader = csv.DictReader(f)
for row in reader:
print(row)
```
実行結果は下記のようになります。
```python
{'id': 0, 'name': 'A'}
{'id': 1, 'name': 'B'}
{'id': 2, 'name': 'C'}
```
## 書き込み
### writer
CSV ファイルを作成するには `open()` でファイルを開いた後 `csv.writer()` にファイルオブジェクトを渡します。
```python
#!/usr/bin/env python
import csv
def main():
with open('example.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow([0, 'A'])
writer.writerow([1, 'B'])
writer.writerow([2, 'C'])
```
!!! warning "注意"
書き込みの場合も `newline=''` は必ず付けるようにしてください。
### DictWriter
ヘッダ付きの CSV を作成したい場合は `writer()` の代わりに `DictWriter` を使うと書き込む要素を辞書で指定できるようになります。
```python
#!/usr/bin/env python
import csv
def main():
with open('example.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['id', 'name'])
writer.writerow({'id': 0, 'name': 'A'})
writer.writerow({'id': 1, 'name': 'B'})
writer.writerow({'id': 2, 'name': 'C'})
if __name__ == '__main__':
main()
```
================================================
FILE: docs/ch05-04-json.md
================================================
# JSON
Python で JSON の読み書きを行いたい場合は `json` モジュールを使います。
## 読み込み
### ファイルから読み込み
JSON ファイルを読み込む場合は `open()` でファイルを開いた後、ファイルオブジェクトを `json.load()` に渡します。
```python
#!/usr/bin/env python
import json
def main():
with open('example.json') as f:
json_data = json.load(f)
if __name__ == '__main__':
main()
```
`json_data` は読み込んだ JSON の構造に応じて Python の辞書やリストになります。
**example.json**
```json
{
"message": "Hello, World!",
"items": [0, 1, 2],
"ok": true
}
```
上記のファイルに対しては `json_data` は下記のようになります。
```python
{
"message": "Hello, World!",
"items": [0, 1, 2],
"ok": True
}
```
### 文字列から読み込み
`json.loads()` を使うと文字列で作成された JSON をパースすることができます。
```python
json_string = """
{
"message": "Hello, World",
"items": [
0,
1,
2
],
"ok": true
}
"""
json_data = json.loads(json_string)
```
!!! note
`json.loads()` の `s` は `string` の `s` です。
## 書き込み
### ファイルに書き込み
JSON ファイルを作成するには `open()` でファイルを開いた後、`json.dump()` にファイルオブジェクトを渡します。
```python
#!/usr/bin/env python
import json
def main():
json_data = {
"message": "Hello, World!",
"items": [0, 1, 2],
"ok": True,
}
with open('example.json', 'w') as f:
json.dump(json_data, f)
if __name__ == '__main__':
main()
```
**example.json**
```json
{"items": [0, 1, 2], "message": "Hello, World!", "ok": true}
```
JSON データは改行されずに出力されます。もし整形して出力させたい場合は `json.dump()` に `indent` オプションを渡します。`indent` にはインデント数を渡します。
```python
json.dump(json_data, f, indent=4)
```
```json
{
"items": [
0,
1,
2
],
"message": "Hello, World!",
"ok": true
}
```
出力したいデータ内に日本語が含まれていると JSON に変換するときに `\uXXXX` というエスケープされた形式で変換されてしまいます。
```python
json_data = {
"message": "こんにちは",
"items": [0, 1, 2],
"ok": True,
}
```
**結果**
```json
{
"message": "\u3053\u3093\u306b\u3061\u306f",
"items": [
0,
1,
2
],
"ok": true
}
```
日本語をエスケープさせたくない場合は `json.dump()` に `ensure_ascii=False` オプションを渡します。
```python
json.dump(json_data, f, indent=4, ensure_ascii=False)
```
**結果**
```json
{
"message": "こんにちは",
"items": [
0,
1,
2
],
"ok": true
}
```
### 文字列に出力
JSON を文字列に出力したい場合は `json.dumps()` を使います。
```python
json_data = {
"message": "Hello, World!",
"items": [0, 1, 2],
"ok": True,
}
json_string = json.dumps(json_data, indent=4, ensure_ascii=False)
print(json_string)
```
**結果**
```json
{
"message": "Hello, World!",
"items": [0, 1, 2],
"ok": true
}
```
================================================
FILE: docs/ch06-01-exceptions.md
================================================
# 例外
Python のプログラムで不正な処理を行ったときは例外と呼ばれる割り込み処理が通常の処理に変わって実行されます。例外が発生するタイミングにはいろいろなものがありますが、代表的なものには次のようなものがあります。
## 不正な構文を実行
```python
#!/usr/bin/env python
def main():
print('Hello, World!'
if __name__ == '__main__':
main()
```
```shell
$ python main.py
File "main.py", line 8
if __name__ == '__main__':
^
SyntaxError: invalid syntax
```
`SyntexError` という種類の例外が発生しています。
## 配列のインデックスが不正
```python
#!/usr/bin/env python
def main():
x = [0, 1, 2]
print(x[3])
if __name__ == '__main__':
main()
```
```shell
$ python main.py
Traceback (most recent call last):
File "main.py", line 10, in <module>
main()
File "main.py", line 6, in main
print(x[3])
IndexError: list index out of range
```
`IndexError` という種類の例外が発生しています。
## 存在しないファイルをオープン
```python
#!/usr/bin/env python
def main():
with open('file.txt') as f:
print(f.read())
if __name__ == '__main__':
main()
```
```shell
$ python main.py
Traceback (most recent call last):
File "main.py", line 10, in <module>
main()
File "main.py", line 5, in main
with open('file.txt') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'
```
`FileNotFoundError` という種類の例外が発生しています。このように不正な処理が検知された時点で例外が割り込み、プログラムは停止します。
- `SyntexError`
- `IndexError`
- `FileNotFoundError`
はすべて例外クラスを呼ばれるものです。
## 例外を捕捉する
例外が発生したときにプログラムを停止させる代わりに、例外が発生したときに実行する処理を書くこともできます。`try-except` 文を使ってそれが実現できます。
```python
#!/usr/bin/env python
def main():
try:
# 例外を監視する処理
with open('file.txt') as f:
print(f.read())
except FileNotFoundError:
# 例外発生時の処理
print('ファイルが存在しません。')
if __name__ == '__main__':
main()
```
```shell
$ python main.py
ファイルが存在しません。
```
`try` ブロックで例外が発生しうる処理を記述し、`except` ブロックで例外が発生したときに実行する処理を記述します。 `except` ブロックは複数書くこともでき、そうすることで複数の例外を捕捉することができます。
```python
#!/usr/bin/env python
def main():
try:
with open('file.txt') as f: # file.txt が存在しなければ FileNotFoundError
contents = f.read()
print(contents[1000]) # ファイル内の文章が 1000 字以上なければ IndexError
except FileNotFoundError:
print('ファイルが存在しません。')
except IndexError:
print('文章は 1000 字以上必要です。')
if __name__ == '__main__':
main()
```
例外を捕捉するメリットには次のようなものがあります。
- `try-except` を `if-else` のような条件分岐の代わりとして使える
- どのようなエラーが発生しうるかがコード上で明らかになる
- エラー発生時の原因や解決策を `print()` などを使って伝えられる
## 例外を送出する
自分で例外を送出することもできます。例外を送出するには `raise` を使います。
```python
def factorial(n):
if not n >= 0:
raise ValueError('n >= 0 である必要があります')
...
```
自分で例外を送出する場合は独自の例外クラスを用意しておいたほうがエラーの起こった箇所が区別しやすくなります。例外クラスを作成するには `Exception` クラスを継承して作成する必要があります。
```python
class MyException(Exception):
pass
```
例外クラスの実装は空で問題ありません。 `Exception` を継承している組み込みの例外クラスを継承しても良いです。
!!! tips "組み込み例外の一覧"
下記ページに例外クラスの一覧が載っていますので参考にして下さい。
[https://docs.python.org/ja/3/library/exceptions.html](https://docs.python.org/ja/3/library/exceptions.html)
================================================
FILE: docs/ch07-01-generators.md
================================================
# ジェネレータ
ジェネレータとは処理を一時停止できる機能を持った関数のことです。処理を一時停止できるということがどういうことなのか、具体的なソースコードで説明していきます。
## ジェネレータの例
まず下記のような処理の挙動について確認しておきます。
**main.py**
```python
#!/usr/bin/env python
def f():
print('開始')
return
print('終了')
def main():
f()
f()
if __name__ == '__main__':
main()
```
```shell
$ python main.py
開始
開始
```
関数 `f()` は通常の関数ですが `return` の後ろに `print()` の呼び出しが入っています。`return` があると関数の処理はそこで終わってしまうので `f()` を実行しても `終了` という文字列はプリントされません。
次に `return` の箇所を `yield` というキーワードに変更して同様のプログラムを実行するとどうなるでしょうか。
```python hl_lines="7"
#!/usr/bin/env python
def f():
print('開始')
yield
print('終了')
def main():
f()
f()
if __name__ == '__main__':
main()
```
```shell
$ python main.py
開始
終了
```
2 回目の `f()` の呼び出しで `終了` という文字列がプリントされるはずです。`yield` は `return` とよく似た挙動をしますが `return` が関数の処理を終了するのに対し `yield` はそこで一時停止をして関数を抜けます。再度 `f()` が呼ばれると `yield` の箇所から関数の処理が再開されます。このように `yield` を使った関数のことをジェネレータといいます。
## ジェネレータを使ったループ
ジェネレータは `return` と同様 `yield` で任意の値を返すことができます。
**main.py**
```python
#!/usr/bin/env python
def f():
yield 0
yield 1
yield 2
def main():
print(f())
print(f())
print(f())
if __name__ == '__main__':
main()
```
```shell
$ python main.py
0
1
2
```
さらに `for` にジェネレータを渡して値を取り出すこともできます。
```python
def main():
for x in f():
print(x) # 0, 1, 2
```
ジェネレータを `for` に渡すとジェネレータに書かれた `yield` の個数分だけループが回ります。つまりジェネレータはリストのようなデータ構造とよく似た振る舞いをします。
## ループ処理のカスタマイズ
複雑なループ処理はジェネレータを使うことでシンプルに書くことができるようになります。例えば下記のような 2 つのリストの要素の総当たりの組み合わせを得るような処理を考えます。
```python
def main():
xs = [0, 1, 2]
ys = ['a', 'b', 'c']
for x in xs:
for y in ys:
print(x, y)
```
このような処理は 2 つのリストを受け取って総当たりの組み合わせを返すジェネレータで書き換えることができます。
```python
def product(xs, ys):
for x in xs:
for y in ys:
yield x, y
def main():
xs = [0, 1, 2]
ys = ['a', 'b', 'c']
for x, y in product(xs, ys):
print(x, y)
```
`product()` を使うと二重ループが単一ループで書き直せました。実は `product()` は標準ライブラリの [itertools] ですでに用意されているため、わざわざ自分で作らなくても使うことができます。
[itertools]: https://docs.python.org/ja/3/library/itertools.html#itertools.product
================================================
FILE: docs/ch08-01-doctest.md
================================================
# doctest
Python にはソースコードのドキュメンテーションをサポートするための docstring という文字列を使ってドキュメントを作成することができます。さらに docstring には関数の使用例を記述するための構文が用意されています。またその使用例を示すコードは関数が実際に記述したとおりに振る舞うかどうかをテストする機能も備わっており doctest と呼ばれます。
**main.py**
```python
#!/usr/bin/env python
"""
サンプルモジュールです。
このモジュールは factorial() という関数を提供しており、次のように使用します。
>>> factorial(5)
120
"""
import math
def factorial(n):
"""n >=0 であるような整数に対する階乗を計算します。
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n >= 0 である必要があります
浮動小数点型も渡せますが、その値は整数値である必要があります。
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n は整数でなければいけません
>>> factorial(30.0)
265252859812191058636308480000000
極端に大きな整数値を渡しても階乗の計算はできません。
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n の値が大きすぎます
"""
if not n >= 0:
raise ValueError('n >= 0 である必要があります')
if math.floor(n) != n:
raise ValueError('n は整数でなければいけません')
if n + 1 == n:
raise OverflowError('n の値が大きすぎます')
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == '__main__':
import doctest
doctest.testmod()
```
`"""..."""` のようにトリプルクオテーションで囲まれた文字列が docstring です。通常はソースコード・関数・クラスの先頭部分で記述します。`>>>` から始まる行
```python
>>> factorial(5)
120
```
は Python のコードを記述するための行で、その処理を実行したらどのように振る舞うのかを `>>>` の下部に記述します。実行時に例外を送出する場合はその旨を記述することもできます。
```python
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n >= 0 である必要があります
```
docstring 内のコードが記述したとおりに動くかどうかを確認するためには doctest モジュールを使用します。
```python
import doctest
doctest.testmod()
```
このように記述しておき
```shell
$ python main.py
```
と実行すると doctest が走ります。`factorial()` の実装が期待通りになっている場合は何も表示されませんが、もしバグが混入していた場合はエラーメッセージが出力されます。下記は `result = 1` の部分を `result = 10` と書いてしまっていた場合の doctest の実行結果になります。
```shell
$ python main.py
**********************************************************************
File "main.py", line 9, in __main__
Failed example:
factorial(5)
Expected:
120
Got:
1200
**********************************************************************
File "main.py", line 20, in __main__.factorial
Failed example:
[factorial(n) for n in range(6)]
Expected:
[1, 1, 2, 6, 24, 120]
Got:
[10, 10, 20, 60, 240, 1200]
**********************************************************************
File "main.py", line 23, in __main__.factorial
Failed example:
factorial(30)
Expected:
265252859812191058636308480000000
Got:
2652528598121910586363084800000000
**********************************************************************
File "main.py", line 35, in __main__.factorial
Failed example:
factorial(30.0)
Expected:
265252859812191058636308480000000
Got:
2652528598121910586363084800000000
**********************************************************************
2 items had failures:
1 of 1 in __main__
3 of 6 in __main__.factorial
***Test Failed*** 4 failures.
```
## なぜ doctest を使うのか
doctest を使うことで次のような機能を提供することができます。
- プログラムの使用例をドキュメントに記述できる
- その使用例が単体テストとして使用できる
- プログラムの実装と使用例が同期しているかどうか用意に確認できる
プログラムの使い方を記述しておくことはとても重要なことですが、ドキュメントというのは時間が経つと形骸化しやすいものです。しかし doctest を使ってドキュメントを管理しておくとプログラムの内容と同期が取れているかどうかを簡単に確認できるため、形骸化することがなくなります。
================================================
FILE: docs/ch08-02-pytest.md
================================================
# pytest
Python には単体テストを書くためのフレームワークがいくつかあります。
| フレームワーク | 説明 |
|----------------|--------------------|
| unittest | 標準ライブラリ |
| nose | かつては主流だった |
| pytest | 現在主流のもの |
上記の通り Python は標準ライブラリを使って単体テストを書くことができますが、サードパーティ製の pytest の使い勝手が良いため、pytest を使って書かれることが多いです。そこでここでは pytest の簡単な使い方について説明します。
## 準備
[プロジェクト構成](./ch04-07-project-structures.md) を参考に、単体テストのソースコードは `tests` 配下に作成するようにします。ソースコードが 1 つで十分な場合はディレクトリを作らなくても構いません。
下記のような構成でファイルを作成し、素数判定のコードをテストしてみましょう。
```shell
prime
├── prime.py
└── test_prime.py
```
!!! warning "注意"
テストを複数ファイルに分割して書く場合は `tests` ディレクトリを作成し、その中に `__init__.py` を含めるようにしてください。`__init__.py` がないとテストが正しく実行できなくなります。詳しくは [ディレクトリ構成](./ch04-07-project-structures.md) を参考にしてください。
## インストール
pipenv を使ってインストールします。
```shell
$ mkdir fibonacci
$ cd fibonacci
$ pipenv install -d pytest
```
`-d` というのは開発時にだけ必要となるパッケージをインストールするときに指定するフラグです。単体テストは通常開発時にしか必要ないため大抵のケースで pytest は `-d` を指定してインストールするのが良いでしょう。
## テストの書き方
まず素数判定を行う関数を書きます。
**prime.py**
```python
def is_prime(n: int) -> bool:
if n <= 1:
return False
if n == 2:
return True
if n % 2 == 0:
return False
i = 3
while i * i <= n:
if n % i == 0:
return False
i += 2
return True
```
今回はこのプログラムを直接実行するわけではないため、シバンや `main()` は不要です。次にこの関数に対するテストを下記のように記述します。
**test_prime.py**
```python
from prime import is_prime
def test_is_prime():
assert not is_prime(1)
assert is_prime(2)
assert is_prime(3)
assert not is_prime(4)
assert is_prime(5)
assert not is_prime(6)
assert is_prime(7)
assert not is_prime(8)
assert not is_prime(9)
assert not is_prime(10)
```
pytest は `test_` で始まるファイル・関数を単体テストのコードとみなします。テストしたい関数を `import` 文で取り込み、`assert` という文の後ろにテストしたい式を記述します。
## テスト実行
テストを実行するには `pytest` というコマンドを使います。
```shell
$ pipenv shell
(prime) $ pytest test_prime.py
============================================== test session starts ==============================================
platform darwin -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: /Users/kenichiro-ida/Documents/github.com/rinatz/prime
collected 1 item
test_prime.py . [100%]
=========================================== 1 passed in 0.02 seconds ============================================
```
素数判定が正しく実装されていなかった場合の挙動を確認するため、`is_prime()` から次の行を無効にして再度テストを実行してみます。
**prime.py**
```python hl_lines="8 9"
def is_prime(n: int) -> bool:
if n <= 1:
return False
if n == 2:
return True
# if n % 2 == 0:
# return False
i = 3
while i * i <= n:
if n % i == 0:
return False
i += 2
return True
```
```shell
(prime) $ pytest test_prime.py
============================================== test session starts ==============================================
platform darwin -- Python 3.7.3, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
rootdir: /Users/kenichiro-ida/Documents/github.com/rinatz/prime
collected 1 item
test_prime.py F [100%]
=================================================== FAILURES ====================================================
_________________________________________________ test_is_prime _________________________________________________
def test_is_prime():
assert not is_prime(0)
assert not is_prime(1)
assert is_prime(2)
assert is_prime(3)
> assert not is_prime(4)
E assert not True
E + where True = is_prime(4)
test_prime.py:9: AssertionError
=========================================== 1 failed in 0.09 seconds ============================================
```
`is_prime(4)` が `True` になっているというエラーメッセージが出力されています。
## パラメータ化したテスト
上記の例では `is_prime(4)` のテストに失敗すると、その時点でテストが終わってしまうため、`is_prime(5)` 以降のテストがどうなるかは分かりませんでした。このようなケースでは **パラメータ化したテスト** を作ることで `1~10` までのすべての値をテストできるようになります。
パラメータ化したテストはテスト内で使用するパラメータを関数の引数として渡せるように書き直したテストのことです。パラメータ化したテストでテストを記述した場合は、すべてのパラメータのテストを実行するまでテストが続行されます。チュートリアルの `test_is_prime()` をパラメータ化したテストで書き直すと次のようになります。
```python
import pytest
from prime import is_prime
@pytest.mark.parametrize(('number', 'expected'), [
(1, False),
(2, True),
(3, True),
(4, False),
(5, True),
(6, False),
(7, True),
(8, False),
(9, False),
(10, False),
])
def test_is_prime(number, expected):
assert is_prime(number) == expected
```
`@pytest.mark.parametrize()` はデコレータと呼ばれるもので、これにテストで使用するパラメータを記述します。デコレータの最初の引数 `('number', 'expected')` はテスト関数に渡すパラメータの引数名になります。第 2 引数は実際に渡すパラメータの値をタプルのリストとして記述します。
```python
@pytest.mark.parametrize(('number', 'expected'), [
(1, False),
])
```
のように記述すると `test_is_prime(1, False)` が実行されます。複数記述すればその分だけ `number, expected` に値が渡され `test_is_prime()` が実行されます。
### 注意点
デコレータの書き方には注意して下さい。次のいずれも正しい書き方ではありません。
**スペルミス**
!!! warning "誤"
`@pytest.mark.parameterized`
!!! check "正"
`@pytest.mark.parametrized`
**文字列をタプルにしていない**
!!! warning "誤"
```python
@pytest.mark.parametrized('number', 'expected', [
...
])
```
!!! check "正"
```python
@pytest.mark.parametrized(('number', 'expected'), [
...
])
```
**タプルをリストの要素としない**
!!! warning "誤"
```python
@pytest.mark.parametrized(('number', 'expected'),
(1, False),
(2, True),
...
)
```
!!! check "正"
```python
@pytest.mark.parametrized(('number', 'expected'), [
(1, False),
(2, True),
...
])
```
## フィクスチャ
フィクスチャはテストの実行前後で行いたい前処理・後処理を記述するために使用する関数のことです。各テストで同じ前処理・後処理を行う必要がある場合に暗黙的にそれが実行できるようになります。
### ファイルを扱うテスト
ファイルを扱う関数はフィクスチャが有効です。今ファイルから整数を受け取り、それを昇順に読み込む関数を考えます。
```python
from typing import List
# List[int] で要素が int のリスト型を表す型ヒントになる
def load_numbers_sorted(txt: str) -> List[int]:
numbers = []
with open(txt) as f:
numbers = sorted(map(lambda e: int(e), f))
return numbers
```
この関数は入力値としてファイルのパスを受け取ります。そのため、事前にファイルを用意しなければいけません。このファイルを用意するためにフィクスチャが利用できます。
!!! warning "注意"
関数がファイルを必要とするからと言ってテスト用のファイルをあらかじめリポジトリにコミットするようなことは避けるべきです。そのようなことをするとテストパターンが増えるたびにファイルも増えてしまい、管理が複雑になります。
### 前処理の書き方
下記のような整数を保存したファイルを用意して `load_numbers_sorted()` のためのテスト `test_load_numbers_sorted()` を作成してみます。
**numbers.txt**
```
2
5
4
3
1
```
`test_load_numbers_sorted()` が実行される前にファイルを用意する必要があるため次のようにフィクスチャを使ってファイルを作成します。
```python
import pytest
@pytest.fixture
def txt() -> str:
with open('numbers.txt', 'w') as f:
for n in [2, 5, 4, 3, 1]:
f.write('{}\n'.format(n))
yield 'numbers.txt'
```
`numbers.txt` というファイルを作り、そのファイル名を返却しています。このフィクスチャを使って `test_load_numbers_sorted()` を実行するには次のようにします。
```python
def test_load_numbers_sorted(txt):
assert load_numbers_sorted(txt) == [1, 2, 3, 4, 5]
```
テスト関数にフィクスチャと同じ名前の引数 `txt` を渡します。すると `txt` にはフィクスチャ `txt()` の戻り値 `numbers.txt` が入ってきます。このコードを実行すると
1. `txt()` が呼ばれる
1. `numbers.txt` が作成される
1. `test_load_numbers_sorted('numbers.txt')` が呼ばれる
という振る舞いをします。
### 後処理の書き方
`numbers.txt` はテストが終われば不要なため、後処理としてファイルを削除してあげましょう。ファイルを削除するにはフィクスチャ `txt()` に次の行を追加します。
```python
import os
@pytest.fixture
def txt() -> str:
...
yield 'numbers.txt'
os.remove('numbers.txt')
```
こうするとテストが終わると `os.remove('numbers.txt')` が呼び出され、ファイルが削除されます。つまりフィクスチャは
```python
@pytest.fixture
def txt():
# 前処理
yield ... # テスト関数に何らかの値を渡す
# 後処理
```
という構造をしています。`test_load_numbers_sorted(txt)` の引数 `txt` はフィクスチャ `txt()` で
何を返したかで型が決まります。
### フィクスチャの連携
フィクスチャから別のフィクスチャを呼び出すこともできます。
```python
@pytest.fixture
def txt_and_list(txt) -> Tuple[str, List[int]]:
yield txt, [1, 2, 3, 4, 5]
def test_load_numbers_sorted(txt_and_list):
assert load_numbers_sorted(txt_and_list[0]) == txt_and_list[1]
```
この場合 `txt() -> txt_and_list()` の順にフィクスチャが実行され、その結果が `test_load_numbers_sorted()` に渡されます。
### テンポラリの作成
pytest には安全にテンポラリを作成するための `tmpdir` というフィクスチャがあらかじめ用意されています。先に見た例ではファイルがローカルに作られるため、大量のファイルが作られるとディレクトリが汚れてしまいますが `tmpdir` を使うと `/tmp` 配下にファイルを作成するため、ファイル管理がスマートになります。
`tmpdir` の使い方は次のとおりです。
```python
@pytest.fixture
def txt(tmpdir) -> str:
tmpfile = tmpdir.join('numbers.txt')
with tmpfile.open('w') as f:
for n in [2, 5, 4, 3, 1]:
f.write('{}\n'.format(n))
yield str(tmpfile)
tmpfile.remove()
```
### フィクスチャのスコープ
通常フィクスチャはテスト単位で呼び出されます。
```python
def test_sample1(txt):
...
def test_sample2(txt):
...
```
この場合、フィクスチャ `txt()` は各テスト関数を実行するたびに毎回呼び出されます。場合によってはこれが非効率で冗長になることもあります。このような場合はフィクスチャが呼び出されるタイミングを次のようにして変更することができます。
```python
@pytest.fixture(scope='module')
def txt(tmpdir) -> str:
...
```
`scope` に指定できる値は次のとおりです。
| scope | 説明 |
| -------- | ----------------------------------------------------------- |
| function | テスト関数ごとにフィクスチャを実行(デフォルト) |
| module | 同一モジュール(ソースコード)内で1回だけフィクスチャを実行 |
| class | 同一クラス内で1回だけフィクスチャを実行 |
| session | テスト実行時に1回だけフィクスチャを実行 |
ただフィクスチャのスコープはむやみに広げないほうが良いです。フィクスチャの設定をテスト間で共有すると依存関係が生まれてしまい、不意にテストが成功してしまうケースがあるからです。テスト関数ごとにフィクスチャを実行しても問題ない場合はそのようにすべきです。
### conftest.py
複数のファイルをまたいで共通のフィクスチャを使用したいこともあると思います。そのような時はフィクスチャを `conftest.py` というファイルに定義しましょう。`conftest.py` 内のフィクスチャは pytest によって自動的にインポートされ、`conftest.py` があるディレクトリ配下で暗黙的に参照できるようになります。
```
.
└─tests
├─conftest.py .............. 全テストで参照可能
├─test_sample1
│ ├─conftest.py .......... test_sample2.py, test_sample3.py で参照可能
│ ├─test_sample2.py
│ └─test_sample3.py
└─test_sample4
├─conftest.py .......... test_sample5.py, test_sample6.py で参照可能
├─test_sample5.py
└─test_sample6.py
```
## 標準出力のキャプチャ
標準出力にメッセージを出力する関数をテストしたい時には標準出力をキャプチャして出力されたメッセージを確認することができます。例えば次のようなフィボナッチ数列を出力する関数を考えます。
```python
def fibonacci(n: int):
a = 0
b = 1
for _ in range(n):
print(b)
a, b = b, a + b
```
この関数を `fibonacci(5)` として呼び出すと標準出力には
```
1
1
2
3
5
```
と出力されます。本当にこのように出力されるかどうかをテストしたい時には次のように書きます。
```python
def test_fibonacci(capsys):
fibonacci(5)
out, _ = capsys.readouterr()
assert out == (
'1\n'
'1\n'
'2\n'
'3\n'
'5\n'
)
```
`capsys` は標準出力と標準エラー出力をキャプチャするためのフィクスチャです。
```python
capsys.readouterr()
```
はキャプチャした標準出力と標準エラー出力の文字列をタプルとして返します。
## モック
モックとは関数やクラスが相互に依存して動作する時に、依存する関数やクラスが正しく使われているかどうかをテストする時に使われるオブジェクトのことです。例えば次のコードを見てみましょう。
**interaction.py**
```python
def send(message: str):
receive(message)
def receive(message: str):
print('received: {}'.format(message))
```
いま関数 `send()` は引数で受け取った文字列を `receive()` にそのまま渡さなければならないという仕様があったとします。このとき、 `send()` が仕様どおりに実装されているかどうかをテストするためには
- `send()` は `receive()` を1回だけ呼び出しているか?
- `send()` が受け取った文字列は `receive()` にそのまま渡されているか?
を確認する必要があります。もしこの振る舞いをテストで確認することができれば
```python
def send(message: str):
receive('[1]: {}'.format(message))
receive('[2]: {}'.format(message))
```
のように仕様に沿っていない実装を間違った実装として検出できるようになります。モックを使うとこのような確認がテストできるようになります。
### モックの使い方
モックを使うには [pytest-mock] という pytest のプラグインを使用します。インストールは pipenv で次のようにできます。
```shell
$ pipenv install -d pytest-mock
```
`send()` が正しい形式で `receive()` を呼び出しているかどうかを確認するためには `receive()` が受け取った引数と呼び出し回数を記憶する仕組みが必要になります。それを実現するために `receive()` を偽の実装にすり替えて、引数や呼び出し回数を保存できるオブジェクト(すなわちモック)にするというアプローチを取ります(これをモンキーパッチといいます)。`pytest-mock` をインストールすると `mocker` というフィクスチャが使用できるようになります。この `mocker` を使って次のように `receive()` をモックにすることができます。
```python
def test_send(mocker):
receive = mocker.patch('studies.interaction.receive')
```
`mocker.patch()` は引数で受け取った文字列の関数をモック化して返す関数です。`mocker.patch()` を呼び出した後では `send()` が呼び出す `receive()` は `interaction.py` で定義された `receive()` の代わりにモック化された偽の `receive()` が呼び出されるようになります。
```python
def test_send(mocker):
receive = mocker.patch('studies.interaction.receive')
send('Hello World!')
```
ここで呼び出した `send()` は内部で `receive()` を呼んでいますがその `receive()` は `mocker.patch()` が作成したモック化された `receive` になります。そしてこの `receive` は引数で受け取った値や呼び出し回数を記録したオブジェクトになっています。
さらに次のような行をテストコードに追加してみましょう。
```python
receive.assert_called_once_with('Hello World!')
```
これは `receive()` が `'Hello World!'` という文字列を受け取って 1 回だけ呼び出されたかどうかを確認するテストになります。テストコード全体は次のとおりになります。
```python
def test_send(mocker):
receive = mocker.patch('studies.interaction.receive')
send('Hello World!')
receive.assert_called_once_with('Hello World!')
```
試しに `send()` の実装をわざと間違えた実装にしてみましょう。`receive.assert_called_once_with()` のところでテストが失敗するはずです。
### 呼び出し履歴の確認
モック `receive` は自分がどのような引数で何回呼ばれたのかを履歴として残しています。その呼び出し履歴を参照するには `receive.call_args_list` を参照します。
```python
>>> receive.call_args_list
[call('Hello World!')]
```
これは `receive()` が `'Hello World!'` を引数として1回だけ呼ばれたことを意味します。このリストの内容を確認しても `send()` が `receive()` を正しく呼んだかどうかをテストすることができます。
```python
def test_send(mocker):
receive = mocker.patch('studies.interaction.receive')
send('Hello World!')
assert receive.call_args_list == [
mocker.call('Hello World!'),
]
```
例えば `send()` が次のように実装されていたとすると
```python
def send(message: str):
receive('[1]: {}'.format(message))
receive('[2]: {}'.format(message))
```
呼び出し履歴のテストは
```python
assert receive.call_args_list == [
mocker.call('[1]: Hello World!'),
mocker.call('[2]: Hello World!'),
]
```
と書くことができます。
### 戻り値の定義
`send()` の振る舞いが `receive()` の戻り値に依存して変わるケースを考えます。
```python
def send(message: str):
ok = receive(message)
if ok:
print('success')
else:
print('failure')
def receive(message: str) -> bool:
print('received: {}'.format(message))
return True
```
この場合 `receive()` の戻り値に応じて `send()` が出力するメッセージが変わることをテストで確認する必要が出てきます。サンプルの `receive()` は常に `True` しか返さないので、 `False` を返した時の
`send()` の振る舞いが確認できません。このような場合でもモックを使って `receive()` の戻り値を上手く制御することができます。
```python
def test_send(mocker, capsys):
receive = mocker.patch('studies.interaction.receive', return_value=False)
send('Hello World!')
receive.assert_called_once_with('Hello World!')
out, _ = capsys.readouterr()
assert out == 'failure\n'
```
`mocker.patch()` の引数に `return_value=False` を渡すと `send()` 内で呼び出している `receive()` は `False` を返すように偽装されます。
### スパイ
モックを使うと `receive()` の実装は完全に別物に置き換わりますが場合によっては本物の `receive()` を呼びつつ、呼び出し回数を確認したいこともあると思います。そのような場合はスパイを作成することで実現できます。例えば `receive()` が `studies/interaction.py` に定義されている場合
```python
import studies.interaction
receive = mocker.spy(studies.interaction, 'receive')
```
とすることでスパイを作成することができます。`mocker.spy()` が返却する関数は本物の `receive()` に `assert_called_once_with()` などのメソッドが追加されたインスタンスになります。使い方はモンキーパッチの場合と同様です。
[pytest]: https://docs.pytest.org/en/latest/
[pytest-mock]: https://github.com/pytest-dev/pytest-mock
================================================
FILE: docs/ch09-01-tools.md
================================================
# ツール一覧
Python には pip や venv 以外にも開発で使用するツールが多く存在しており、ネットで調べるときに混乱することが多いです。
そこでどんなツールが使われているのかを整理して解説します。
ネットで検索して見つかる Python ツールは、おおむね次の 3 つに分類されます。
1. Python 自体のバージョンを管理する
2. 仮想環境を管理する
3. パッケージやパッケージ間の依存関係を管理する
ツールによっては 2. と 3. の両方をサポートするようなツールもあるため、どういうシーンで利用すればよいのかがわかりにくいところがあります。
ネットで検索して見つかるツールには次のようなものがあります。
| ツール | Python バージョン管理 | 仮想環境管理 | パッケージ管理 | 依存関係解決 | ロックファイル生成 | パフォーマンス | 備考 |
| ----------------------------- | --------------------- | -------------------------------- | -------------------------------- | ------------------ | ------------------ | ------------------ | ------------------------------ |
| pyenv | :white_check_mark: | | | | | :star: | Python のバージョン管理 |
| [venv](./ch04-04-venv.md) | | :white_check_mark: | | | | :star: | Python 標準ライブラリ |
| virtualenv | | :white_check_mark: | | | | :star::star: | `venv` で代用可能 |
| [pip](./ch04-03-pip.md) | | | :white_check_mark: | | | :star: | Python 標準のパッケージ管理 |
| pip-tools | | | :white_check_mark: | :white_check_mark: | :white_check_mark: | :star: | `requirements.txt` 強化 |
| pipx | | | :white_check_mark:(グローバル) | | | :star::star: | CLI ツール専用 |
| [pipenv](./ch09-02-pipenv.md) | | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :octicons-star-24: | 遅いため最近は非推奨気味 |
| [poetry](./ch09-03-poetry.md) | | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :star::star: | 近年の推奨ツール |
| rye | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :star::star: | 試験的だが統合ツールとして注目 |
| uv | | :white_check_mark:(内部で管理) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :star::star::star: | `pip-tools` の超高速代替 |
## どのツールを使うべきか
どのツールを使う場合でも `requirements.txt` は生成しておくのがベターです
(どのツールでも生成コマンドが用意されています)。
`requirements.txt` があれば、Python の標準機能である pip を使ってパッケージのインストールができるようになるためです。
`requirements.txt` さえ生成していれば、どのツールを使ってもよいですが、使い分けのおおまかな方針は次のようになります。
### シンプルな開発
- `venv` + `pip` (標準機能)
- `pip-tools` (依存関係を固定したい場合)
### より便利な管理を求める場合
- `poetry` (仮想環境+パッケージ管理)
- `pipx` (CLI ツール管理)
### 最新のトレンドを取り入れたい場合
- `rye` (統合環境管理の試験的ツール)
- `uv` (超高速なパッケージ管理)
### Python のバージョン管理をしたい場合
- `pyenv` (複数の Python バージョンを使いたい場合)
================================================
FILE: docs/ch09-02-pipenv.md
================================================
# pipenv
`pipenv` は `pip` と `venv` の両方の機能を兼ね備えたサードパーティ製のパッケージ管理ツールです。`venv` で仮想環境を作成してから `pip` でパッケージをインストールするまでの手順では下記のように異なるコマンドを実行する必要がありますが、これを 1 つのコマンドで実行できるようにしたものが `pipenv` です。
| 操作 | コマンド (macOS, Linux) | コマンド (Windows) |
|--------------------------|-------------------------|--------------------------------|
| 仮想環境の作成 | `python -m venv .venv` | `py -m venv .venv` |
| 仮想環境を有効にする | `. .venv/bin/activate` | `.venv/Scripts/activate.bat` |
| 仮想環境を無効にする | `deactivate` | `.venv/Scripts/deactivate.bat` |
| パッケージのインストール | `pip3 install [name]` | `py -3 -m pip install [name]` |
各操作に応じて実行するコマンドも異なれば OS ごとにも異なっており、とても複雑です。しかし `pipenv` を使うと上記の操作は次のようになります。
| 操作 | コマンド |
|--------------------------|-------------------------|
| 仮想環境の作成 | `pipenv --python 3` |
| 仮想環境を有効にする | `pipenv shell` |
| 仮想環境を無効にする | `exit` |
| パッケージのインストール | `pipenv install [name]` |
このように `pipenv` というコマンド 1 つで仮想環境の作成とパッケージのインストールの両方が実行できるため、操作がシンプルになります。また実行するコマンドは OS によらず同じです。
## 特徴
`pipenv` はパッケージのインストールを必ず仮想環境内で実行するように作られています。そのため仮想環境を有効にしていない状態で
```shell
$ pipenv install [name]
```
というコマンドを打っても自動的に仮想環境を作成して、それを有効にした上でパッケージのインストールを実行します。
## インストール
`pipenv` のインストールは下記のようにします。
```shell
$ pip3 install pipenv
```
!!! note
`pipenv` のインストールは仮想環境内で行う必要はありません。なぜなら `pipenv` 自体が仮想環境を作成するツールだからです。
## 使い方
まず作業用ディレクトリを用意します。
```shell
$ mkdir sandbox
```
`pipenv` を使ってサードパーティライブラリの `requests` をインストールするには次のようにします。
```shell
$ cd sandbox
$ pipenv install requests
```
自動的に仮想環境が作成され、その仮想環境内に `requests` がインストールされます。次に `requests` を使用した次のようなソースコードを用意します。
**main.py**
```python
#!/usr/bin/env python
import requests
def main():
response = requests.get('http://example.com')
print(response.text)
if __name__ == '__main__':
main()
```
ソースコードを作成したら仮想環境を有効にして実行してみます。
```shell
$ pipenv shell
(sandbox) $ python main.py
```
上記の 2 行のコマンドは次のように 1 行で実行することもできます。
```shell
$ pipenv run python main.py
```
`pipenv run [command]` は仮想環境を有効にした上で `[command]` を実行してくれる機能です。
## Pipfile
`pip` にはインストールしたいパッケージをテキストファイルに記述しておく `requirements.txt` という仕組みがありましたが、`pipenv` はこれの代替として `Pipfile` というテキストファイルが使用できます。先程の `requests` をインストールするとディレクトリ内に `Pipfile` が作成されていると思います。
**Pipfile**
```shell
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
requests = "*"
[requires]
python_version = "3.7"
```
`pipenv` はパッケージをインストールすると `Pipfile` にインストールしたパッケージを記録するようになっています。もしディレクトリ内に `Pipfile` がある場合、下記のコマンドを実行すると `Pipfile` 内に記述されたパッケージをインストールしてくれます。
```shell
$ pipenv install
```
つまり `Pipfile` をバージョン管理しておくことで他の人の環境でも自分がインストールしたパッケージと同じものをインストールできるようになります。
## Pipfile.lock
`Pipfile` と合わせて `Pipfile.lock` というファイルも作成されていると思います。このファイルはインストールしたパッケージのバージョンを保存しているファイルです。`pipenv install` でパッケージをインストールすると、インストールされるパッケージのバージョンはその時点での最新版が取得されるようになっているため、自分がインストールしたパッケージのバージョンとは厳密には異なるバージョンのパッケージが他の人の環境にインストールされる可能性があります。もしバージョンも含めて完全に一致するものをインストールしたい場合は `Pipfile` の代わりに `Pipfile.lock` を使ってインストールすることで実現できます。
`Pipfile.lock` を使ってインストールするには次のようにします。
```shell
$ pipenv sync
```
## 公式サイト
`pipenv` はここで説明した機能以外にも便利な機能がたくさんあります。詳細は公式サイトに説明がありますので参考にしてみてください。
!!! note "pipenv 公式サイト"
[https://pipenv.readthedocs.io/en/latest/](https://pipenv.readthedocs.io/en/latest/)
================================================
FILE: docs/ch09-03-poetry.md
================================================
# poetry
`poetry` は `pipenv`と同様の課題を解決するために作られたサードパーティ製のパッケージ管理ツールです。
!!! note "poetry 公式サイト"
[https://python-poetry.org/](https://python-poetry.org/)
poetry を使った場合の仮想環境の作成からパッケージのインストールまでの手順は下記のとおりです。
| 操作 | コマンド |
|--------------------------|-------------------------|
| 仮想環境の作成 | なし(暗黙的に作られる)|
| 仮想環境を有効にする | `poetry shell` |
| 仮想環境を無効にする | `exit` |
| パッケージのインストール | `poetry add [name]` |
## インストール
`poetry` のインストールは下記のようにします。
=== "Linux, macOS, Windows (WSL)"
```shell
$ curl -sSL https://install.python-poetry.org | python3 -
```
=== "Windows (Powershell)"
PowerShell 上で下記を実行します。
```shell
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
```
## 使い方
まず作業ディレクトリを用意します。
```shell
$ mkdir sandbox
```
次に poetry の設定ファイルを生成するコマンドを実行します。
```shell
$ poetry init -n
```
コマンドを実行すると `pyproject.toml` というファイルが作成されます。
次に `pipenv` のときと同様に `requests` をインストールしてみます。
```shell
$ poetry add requests
```
`pipenv` の使い方で使用したソースコードを実行するには下記のようにします。
```shell
$ poetry shell
(.venv) $ python main.py
```
上記の 2 行のコマンドは次のように 1 行で実行することもできます。
```shell
$ poetry run python main.py
```
## pyproject.toml と poetry.lock
`pyproject.toml` は poetry の設定ファイルです。`pipenv` でいうところの `Pipfile` と同じ位置づけのファイルになります。
**pyproject.toml**
```toml
[tool.poetry]
name = "sandbox"
version = "0.1.0"
description = ""
authors = ["..."]
[tool.poetry.dependencies]
python = "^3.6"
requests = "^2.24.0"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```
`poetry.lock` はインストールしたパッケージのバージョンを保存しているファイルです。`pipenv` でいうところの `Pipfile.lock` と同じ位置づけのファイルになります。
`pipenv` の場合 `Pipfile` や `Pipfile.lock` の内容をもとにパッケージをインストールするには下記のようにコマンドを使い分ける必要がありました。
| 使用ファイル | コマンド |
|----------------|------------------|
| `Pipfile` | `pipenv install` |
| `Pipfile.lock` | `pipenv sync` |
`poetry` にも同等の機能があるのですが `pyproject.toml` の内容をもとにパッケージをインストールする場合も `poetry.lock` の内容をもとにパッケージをインストールする場合もコマンドは同じです。
```shell
$ poetry install
```
`pyproject.toml` と `poetry.lock` の両方がある場合は `poetry.lock` の内容が優先されるという仕組みになっています。
## pipenv との違い
詳細は割愛しますが `poetry` の方が `pipenv` よりも機能が豊富です。また `pyproject.toml` は `poetry` 専用の設定ファイルではなく Python が公式に策定したパッケージ管理用の設定ファイルになっているため、他のパッケージ管理ツールの設定ファイルとしても使われます。`pip` も新しいバージョンでは `pyproject.toml` を使用することができるようになっています。
================================================
FILE: docs/index.md
================================================
# ゼロから学ぶ Python
<div align="center">
<img src="img/python.svg" />
</div>
このサイトは Python を学ぶ人向けのオンライン学習サイトです。
## 対象
- Python を初めて学ぶ人
- プログラム言語を 1 つ以上経験したことのある人
関数やクラス・オブジェクト指向に対する知識をある程度前提にします。
## Python の特徴
- 学習コストが低い
- 標準ライブラリが非常に豊富
- インデントをすることが言語仕様になっている
## Python のバージョンについて
- `2.x.x`: 2020 年 1 月 1 日でサポート終了
- `3.x.x`: 現行バージョン
`2.x.x` 系は新規開発では使用すべきではありません。このサイトでは `3.x.x` をベースに説明を行います。
================================================
FILE: mkdocs.yml
================================================
site_name: ゼロから学ぶ Python
site_description: ゼロから学ぶ Python
site_author: IDA Kenichiro
site_url: http://rinatz.github.io/python-book
repo_name: rinatz/python-book
repo_url: https://github.com/rinatz/python-book
copyright: Copyright © 2025 IDA Kenichiro
theme:
name: material
palette:
# ライトモード
- media: "(prefers-color-scheme: light)"
scheme: default
primary: orange
accent: blue
toggle:
icon: material/weather-sunny
name: ダークモードに切り替え
# ダークモード
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: orange
accent: blue
toggle:
icon: material/weather-night
name: ライトモードに切り替え
font:
text: Noto Sans
code: Inconsolata
language: ja
logo: img/python.svg
favicon: img/python.svg
icon:
repo: fontawesome/brands/github
features:
- navigation.instant
markdown_extensions:
- toc:
permalink: true
- admonition
- pymdownx.details
- pymdownx.superfences
- pymdownx.highlight
- pymdownx.inlinehilite
- pymdownx.tabbed
- footnotes
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.arithmatex:
generic: true
- meta
nav:
- ホーム: index.md
- 1. Python を始める:
- 1.1. インストール: ch01-01-installation.md
- 1.2. Hello, World!: ch01-02-hello-world.md
- 2. 基本仕様:
- 2.1. 変数: ch02-01-variables.md
- 2.2. 関数: ch02-02-functions.md
- 2.3. コメント: ch02-03-comments.md
- 2.4. 条件文: ch02-04-conditions.md
- 2.5. ループ文: ch02-05-loops.md
- 2.6. リスト内包表記: ch02-06-list-comprehensions.md
- 2.7. ラムダ式: ch02-07-lambdas.md
- 3. クラス:
- 3.1. クラス: ch03-01-classes.md
- 3.2. スコープ: ch03-02-scopes.md
- 3.3. 特殊属性: ch03-03-special-attributes.md
- 3.4. プロパティ: ch03-04-properties.md
- 4. モジュールとパッケージ:
- 4.1. モジュール: ch04-01-modules.md
- 4.2. パッケージ: ch04-02-packages.md
- 4.3. pip: ch04-03-pip.md
- 4.4. venv: ch04-04-venv.md
- 4.7. プロジェクト構成: ch04-07-project-structures.md
- 5. ファイル操作:
- 5.1. ファイル操作: ch05-01-files.md
- 5.2. コンテキスト: ch05-02-contexts.md
- 5.3. CSV: ch05-03-csv.md
- 5.4. JSON: ch05-04-json.md
- 6. 例外:
- 6.1. 例外: ch06-01-exceptions.md
- 7. ジェネレータ:
- 7.1. ジェネレータ: ch07-01-generators.md
- 8. テスト:
- 8.1. doctest: ch08-01-doctest.md
- 8.2. pytest: ch08-02-pytest.md
- 9. 便利ツール:
- 9.1. ツール一覧: ch09-01-tools.md
- 9.2. pipenv: ch09-02-pipenv.md
- 9.3. poetry: ch09-03-poetry.md
================================================
FILE: pyproject.toml
================================================
[project]
name = "python-book"
version = "1.0.0"
description = "ゼロから学ぶPython"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"mkdocs-material>=9.5.50",
]
================================================
FILE: requirements.txt
================================================
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml
babel==2.16.0
# via mkdocs-material
certifi==2024.12.14
# via requests
charset-normalizer==3.4.1
# via requests
click==8.1.8
# via mkdocs
colorama==0.4.6
# via mkdocs-material
ghp-import==2.1.0
# via mkdocs
idna==3.10
# via requests
jinja2==3.1.5
# via
# mkdocs
# mkdocs-material
markdown==3.7
# via
# mkdocs
# mkdocs-material
# pymdown-extensions
markupsafe==3.0.2
# via
# jinja2
# mkdocs
mergedeep==1.3.4
# via
# mkdocs
# mkdocs-get-deps
mkdocs==1.6.1
# via mkdocs-material
mkdocs-get-deps==0.2.0
# via mkdocs
mkdocs-material==9.5.50
# via python-book (pyproject.toml)
mkdocs-material-extensions==1.3.1
# via mkdocs-material
packaging==24.2
# via mkdocs
paginate==0.5.7
# via mkdocs-material
pathspec==0.12.1
# via mkdocs
platformdirs==4.3.6
# via mkdocs-get-deps
pygments==2.19.1
# via mkdocs-material
pymdown-extensions==10.14.1
# via mkdocs-material
python-dateutil==2.9.0.post0
# via ghp-import
pyyaml==6.0.2
# via
# mkdocs
# mkdocs-get-deps
# pymdown-extensions
# pyyaml-env-tag
pyyaml-env-tag==0.1
# via mkdocs
regex==2024.11.6
# via mkdocs-material
requests==2.32.3
# via mkdocs-material
six==1.17.0
# via python-dateutil
urllib3==2.3.0
# via requests
watchdog==6.0.0
# via mkdocs
gitextract_9ud2e2c_/ ├── .github/ │ └── workflows/ │ └── mkdocs.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs/ │ ├── ch01-01-installation.md │ ├── ch01-02-hello-world.md │ ├── ch02-01-variables.md │ ├── ch02-02-functions.md │ ├── ch02-03-comments.md │ ├── ch02-04-conditions.md │ ├── ch02-05-loops.md │ ├── ch02-06-list-comprehensions.md │ ├── ch02-07-lambdas.md │ ├── ch03-01-classes.md │ ├── ch03-02-scopes.md │ ├── ch03-03-special-attributes.md │ ├── ch03-04-properties.md │ ├── ch04-01-modules.md │ ├── ch04-02-packages.md │ ├── ch04-03-pip.md │ ├── ch04-04-venv.md │ ├── ch04-07-project-structures.md │ ├── ch05-01-files.md │ ├── ch05-02-contexts.md │ ├── ch05-03-csv.md │ ├── ch05-04-json.md │ ├── ch06-01-exceptions.md │ ├── ch07-01-generators.md │ ├── ch08-01-doctest.md │ ├── ch08-02-pytest.md │ ├── ch09-01-tools.md │ ├── ch09-02-pipenv.md │ ├── ch09-03-poetry.md │ └── index.md ├── mkdocs.yml ├── pyproject.toml └── requirements.txt
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (143K chars).
[
{
"path": ".github/workflows/mkdocs.yml",
"chars": 629,
"preview": "name: MkDocs\n\non:\n push:\n branches:\n - master\n paths:\n - mkdocs.yml\n - docs/**\n\njobs:\n deploy:\n "
},
{
"path": ".gitignore",
"chars": 567,
"preview": "site/\n\n# Created by https://www.gitignore.io/api/macOS\n# Edit at https://www.gitignore.io/?templates=macOS\n\n### macOS ##"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2025 Ida Kenichiro\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "README.md",
"chars": 391,
"preview": "# ゼロから学ぶ Python\n\nこのリポジトリはオンライン学習サイト[ゼロから学ぶ Python]のソースコードリポジトリです。\n\n## 必要なもの\n\nソースコードから HTML ページを生成するには下記のものが必要です。\n\n- Pyth"
},
{
"path": "docs/ch01-01-installation.md",
"chars": 1639,
"preview": "# インストール\n\nPython 開発に必要な下記のツールをインストールします。\n\n- Python\n- Visual Studio Code\n\n## Python\n\n各プラットフォームに合わせて Python のインストール手順が下記サイ"
},
{
"path": "docs/ch01-02-hello-world.md",
"chars": 2206,
"preview": "# Hello, World!\n\n標準出力に `Hello, World!` と出力する簡単な Python プログラムを書いてみます。\n\n---\n\nまずターミナル上で `hello` というディレクトリを作成し、それを VSCode で開"
},
{
"path": "docs/ch02-01-variables.md",
"chars": 3400,
"preview": "# 変数\n\n次のようなコードを書いて色々な変数を出力してみましょう。\n\n```python hl_lines=\"6\"\n#!/usr/bin/env python\n\n\ndef main():\n x = 10 # 変数の定義\n "
},
{
"path": "docs/ch02-02-functions.md",
"chars": 4218,
"preview": "# 関数\n\n関数を定義するには `def` キーワードを使います。\n\n```python\n#!/usr/bin/env python\n\n\ndef another_function():\n print('Another function"
},
{
"path": "docs/ch02-03-comments.md",
"chars": 197,
"preview": "# コメント\n\n`#` から始まる行はコメントと見なされます。コメントはプログラム上は無視されるため、自由にメッセージを書くことができます。\n\n```python\n# この行はコメントです。\n```\n\nコードの末尾に置くことも可能です。\n\n"
},
{
"path": "docs/ch02-04-conditions.md",
"chars": 2661,
"preview": "# 条件文\n\nある条件を満たしている時だけ行いたい処理がある場合は条件文を使って処理を書きます。\n\n## `if`\n\n```python\n#!/usr/bin/env python\n\n\ndef main():\n check(3)\n "
},
{
"path": "docs/ch02-05-loops.md",
"chars": 1224,
"preview": "# ループ文\n\nリストの要素に対して繰り返し同様の処理を実行する場合は `for` や `while` を使います。\n\n## `for`\n\nリストに対して `for` を使う場合は次のように書きます。\n\n```python\nvalues ="
},
{
"path": "docs/ch02-06-list-comprehensions.md",
"chars": 494,
"preview": "# リスト内包表記\n\nリスト内包表記とはリストを簡単に作成するための構文のことです。今 `[0, 2, 4, 6, ..., 98]` というリストを作ろうとした場合、\n\n```python\nx = []\n\nfor i in range(5"
},
{
"path": "docs/ch02-07-lambdas.md",
"chars": 1451,
"preview": "# ラムダ式\n\nラムダ式とは関数を変数に代入して扱えるようにするものです。ラムダ式が必要になってくるシーンは関数を別の関数の引数として渡す必要があるときです。\n\n!!! Tips\n 引数として渡される関数のことをコールバックといいます"
},
{
"path": "docs/ch03-01-classes.md",
"chars": 3989,
"preview": "# クラス\n\nクラスとは新しい型を定義するための仕組みです。クラスを使うことで複数の変数と関数を集約した変数を作ることができるようになります。\n\n## 定義\n\nクラスを定義するには `class` キーワードにクラス名を付けて定義します。下"
},
{
"path": "docs/ch03-02-scopes.md",
"chars": 432,
"preview": "# スコープ\n\n他の言語ではクラスメンバに対して `public, protected, private` といったアクセス指定子を指定することができますが Python ではそのような指定はできず、すべてのメンバが `public` とし"
},
{
"path": "docs/ch03-03-special-attributes.md",
"chars": 2662,
"preview": "# 特殊属性\n\nクラスには特殊属性と呼ばれるメソッドが存在しており、クラスを使う際に使用される構文はどれも特殊属性の呼び出しに変換されて実行されます。代表的な特殊属性をいくつか紹介します。\n\n下記のようなクラスを定義したときの特殊属性には次"
},
{
"path": "docs/ch03-04-properties.md",
"chars": 1455,
"preview": "# プロパティ\n\nクラスのメンバで変数のように参照することのできる関数のことをプロパティといいます。\n\n```python\nimport math\n\n\nclass Point:\n def __init__(self, x, y):\n "
},
{
"path": "docs/ch04-01-modules.md",
"chars": 2771,
"preview": "# モジュール\n\nモジュールとは関数やクラスなどを別ファイルで利用できる状態で整理した Python のソースコードのことです。これまでのソースコードは実行することを念頭に実装しましたが、モジュールは別のファイルから取り込まれることを念頭に"
},
{
"path": "docs/ch04-02-packages.md",
"chars": 3199,
"preview": "# パッケージ\n\nモジュールを使うことで関数やクラスをまとめることができますが、1 つのモジュール内にたくさんの定義を含めてしまうとコードが長くなってしまい分かりづらくなってしまいます。そういうときはファイルを分割して複数のモジュールを作成"
},
{
"path": "docs/ch04-03-pip.md",
"chars": 1832,
"preview": "# pip\n\n`pip` はインターネットで公開されている Python パッケージを取得するためのパッケージ管理ツールです。\n\n## 使い方\n\n[requests] という HTTP に関する機能を取り扱う有名なライブラリがあります。これ"
},
{
"path": "docs/ch04-04-venv.md",
"chars": 2801,
"preview": "# venv\n\n`pip` を使ってサードパーティ製のパッケージをインストールすることができましたが、インストールしたいパッケージが稀に競合を起こすことがあります。\n\n| プログラム | `requests` の要求バージョン |\n|---"
},
{
"path": "docs/ch04-07-project-structures.md",
"chars": 2883,
"preview": "# プロジェクト構成\n\nPython のソースコードを管理する際にディレクトリの構成をちゃんと考えておくことはとても重要なことです。なぜなら Python は適切な構成になっていないとプログラムを正しく動かすことができなくなるからです。そこ"
},
{
"path": "docs/ch05-01-files.md",
"chars": 1743,
"preview": "# ファイル操作\n\nファイルの読み書きをする方法について説明します。\n\n## 読み込み\n\n`file.txt` というファイルを読み込み、1 行ずつプリントするプログラムは次のように書きます。\n\n**main.py**\n\n```python"
},
{
"path": "docs/ch05-02-contexts.md",
"chars": 739,
"preview": "# コンテキスト\n\n`open()` を使ってファイルの読み書きをした後は必ず `close()` を使ってファイルを閉じる必要があります。しかしファイルの閉じ忘れがよくあるミスの 1 つです。このファイルの閉じ忘れをなくすために Pyth"
},
{
"path": "docs/ch05-03-csv.md",
"chars": 1993,
"preview": "# CSV\n\nPython で CSV の読み書きを行いたい場合は `csv` モジュールを使います。\n\n## 読み込み\n\n### reader\n\nCSV ファイルを読み込む場合は `open()` でファイルを開いた後、ファイルオブジェク"
},
{
"path": "docs/ch05-04-json.md",
"chars": 2555,
"preview": "# JSON\n\nPython で JSON の読み書きを行いたい場合は `json` モジュールを使います。\n\n## 読み込み\n\n### ファイルから読み込み\n\nJSON ファイルを読み込む場合は `open()` でファイルを開いた後、フ"
},
{
"path": "docs/ch06-01-exceptions.md",
"chars": 2979,
"preview": "# 例外\n\nPython のプログラムで不正な処理を行ったときは例外と呼ばれる割り込み処理が通常の処理に変わって実行されます。例外が発生するタイミングにはいろいろなものがありますが、代表的なものには次のようなものがあります。\n\n## 不正な"
},
{
"path": "docs/ch07-01-generators.md",
"chars": 2154,
"preview": "# ジェネレータ\n\nジェネレータとは処理を一時停止できる機能を持った関数のことです。処理を一時停止できるということがどういうことなのか、具体的なソースコードで説明していきます。\n\n## ジェネレータの例\n\nまず下記のような処理の挙動について"
},
{
"path": "docs/ch08-01-doctest.md",
"chars": 3429,
"preview": "# doctest\n\nPython にはソースコードのドキュメンテーションをサポートするための docstring という文字列を使ってドキュメントを作成することができます。さらに docstring には関数の使用例を記述するための構文が"
},
{
"path": "docs/ch08-02-pytest.md",
"chars": 14751,
"preview": "# pytest\n\nPython には単体テストを書くためのフレームワークがいくつかあります。\n\n| フレームワーク | 説明 |\n|----------------|--------------------|\n"
},
{
"path": "docs/ch09-01-tools.md",
"chars": 3373,
"preview": "# ツール一覧\n\nPython には pip や venv 以外にも開発で使用するツールが多く存在しており、ネットで調べるときに混乱することが多いです。\nそこでどんなツールが使われているのかを整理して解説します。\n\nネットで検索して見つかる"
},
{
"path": "docs/ch09-02-pipenv.md",
"chars": 3378,
"preview": "# pipenv\n\n`pipenv` は `pip` と `venv` の両方の機能を兼ね備えたサードパーティ製のパッケージ管理ツールです。`venv` で仮想環境を作成してから `pip` でパッケージをインストールするまでの手順では下記"
},
{
"path": "docs/ch09-03-poetry.md",
"chars": 2443,
"preview": "# poetry\n\n`poetry` は `pipenv`と同様の課題を解決するために作られたサードパーティ製のパッケージ管理ツールです。\n\n!!! note \"poetry 公式サイト\"\n [https://python-poetr"
},
{
"path": "docs/index.md",
"chars": 403,
"preview": "# ゼロから学ぶ Python\n\n<div align=\"center\">\n <img src=\"img/python.svg\" />\n</div>\n\nこのサイトは Python を学ぶ人向けのオンライン学習サイトです。\n\n## 対象"
},
{
"path": "mkdocs.yml",
"chars": 2551,
"preview": "site_name: ゼロから学ぶ Python\nsite_description: ゼロから学ぶ Python\nsite_author: IDA Kenichiro\nsite_url: http://rinatz.github.io/py"
},
{
"path": "pyproject.toml",
"chars": 176,
"preview": "[project]\nname = \"python-book\"\nversion = \"1.0.0\"\ndescription = \"ゼロから学ぶPython\"\nreadme = \"README.md\"\nrequires-python = \">="
},
{
"path": "requirements.txt",
"chars": 1482,
"preview": "# This file was autogenerated by uv via the following command:\n# uv pip compile pyproject.toml\nbabel==2.16.0\n # vi"
}
]
About this extraction
This page contains the full source code of the rinatz/python-book GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (84.3 KB), approximately 38.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.