サンプルコードで理解するPython入門講座（後編）

## はじめに

このエントリはPythonを手っ取り早く利用できるためのもので

* 詳しい説明は抜きだ！
* とにかく最初は「使えた！」と思えることが大切である
* 理論はあとからちゃんと調べればよい

という考えで書いています。
実際には、自身が理解できておらず解説できない部分が多すぎるだけです。
ごごごめんなさい。


前編は以下です。

https://qiita.com/bashaway/items/4899e231f53c722c7f2c


# 関数/メソッド

関数は「**[呼び出し側に値を返す一連の文](https://docs.python.org/ja/3/glossary.html#term-function)**」のことらしい。
メソッドは「**[クラス本体の中で定義された関数](https://docs.python.org/ja/3/glossary.html#term-method)**」のことらしい。

* 関数には引数を渡すことができる
* 関数を実行すると戻り値が返される
* pythonでは関数はどこかのモジュールやクラスに所属している
  * モジュールで定義された関数は「関数」という
  * クラスで定義された関数は「メソッド」という

Pythonでは一般的に以下の表現をしていることが多い（が、原典が見当たらず。。。）

* モジュール： 複数クラスや関数が記述される「ファイル」
* パッケージ： 複数モジュールを持つ「ディレクトリ」
* ライブラリ： 複数のパッケージやモジュールで構成される「集合体」


ここでは、よく使う関数のサンプル/使い方を紹介する。

関数/メソッドなど詳しい説明は **[その他の組み込み型](https://docs.python.org/ja/3/library/stdtypes.html#other-built-in-types)** にもあります。（が、自分はあまり理解できていません）



## 関数の使い方

```:Pythonでよく出てくる関数の使い方
# ライブラリやモジュールを利用する場合
import ライブラリ名
import モジュール名

# 変数に関数の戻り値が代入される
変数 = モジュール.関数名(引数)
変数 = クラス.関数名(引数)
変数 = 変数.関数名(引数)

# 変数を操作したり、システムを操作したり、と、戻り値を使わないものもある
モジュール.関数名(引数)
クラス.関数名(引数)
変数.関数名(引数)
```

```:組み込み関数（後述）は __builtins__ モジュールのメソッドである
$ python -c "print('string')"
string

$ python -c "__builtins__.print('string')"
string
```

**[呼び出し側に値を返す一連の文](https://docs.python.org/ja/3/glossary.html#term-function)** なので、使わないor意識しなくても返り値はある。

```:返り値を使わないor意識しない関数
$ python -c "print(type(print('string')))"
string
<class 'NoneType'>

$ python -c "a=[2,3,4]; print(type(a.append('strings'))); print(a)"
<class 'NoneType'>
[2, 3, 4, 'strings']
```


## 組み込み関数

https://docs.python.org/ja/3/library/functions.html

組み込みメソッド（builtin_function_or_method）は特定のクラスやインスタンスのメソッドではなく、引数をつけて関数を呼び出し、なんらかのデータ型を返す。

### 型変換
データ型を変換するための操作
`int()` / `str()` / `bool()` / `list()` / `dict()` など

だいたい **[前編](https://qiita.com/bashaway/items/4899e231f53c722c7f2c#%E5%9E%8B%E5%A4%89%E6%8F%9B)** で解説したような使い方なので、省略します。

### 入出力
ファイルやキーボードから入出力するための操作
`open()` / `input()` / `print()`  など

だいたい **[入出力の章](#入出力)（後述）** で解説するような使い方なので、省略します。

### 数学
算術的な演算や抽出するための操作
`min()` / `max()` / `sum()` / `pow()` など

リストを与えて算出結果を返したり、引数を与えて算出結果を返します。

```:min()の例
$ python -c "a=[10, 20, 3]; print(min(a))"
3
```

```:max()の例
$ python -c "a=[10, 20, 3]; print(max(a))"
20
```

```:sum()の例
$ python -c "a=[10, 20, 3]; print(sum(a))"
33
```

```:pow()の例
$ python -c "print(pow(3,3))"
27
```

### オブジェクト
データ型に沿った情報を生成する操作
`sorted()` / `reversed()`/ `range()` / `enumerate()` / `slice()` など。
これらは、引数にとったオブジェクトはそのままで、新たにオブジェクトを生成する。
`list()`は、ものすごくおおざっぱに考えて「引数をリストに変換する」と考えてしまってよい。

```:sorted()の例
$ python -c "a=[3, 2, 5, 1, 4]; print(sorted(a))"
[1, 2, 3, 4, 5]
```

```:reversed()の例（リストではなく、iteratorという「繰り返しデータ」のような型が返ってくる。）
$ python -c "a=[3, 2, 5, 1, 4]; print(list(reversed(a)))"
[4, 1, 5, 2, 3]
```

```:range()の例（リストではなく、rangeという「繰り返し回数」のような型が返ってくる。）
$ python -c "print(list(range(0,5)))"
[0, 1, 2, 3, 4]
```

```:enumerate()の例（リストではなく、enumerateという「カウンタとデータのタプルのリスト」のような型が返ってくる）
$ python -c "a=['a', 'b', 'c']; print(list(enumerate(a)))"
[(0, 'a'), (1, 'b'), (2, 'c')]
```

```:slice()の例（リストではなく、スライス動作（a[2:4]）するためのオブジェクトが返ってくる）
$ python -c "a=[1, 2, 3, 4, 5]; print(a[slice(2,4)])"
[3, 4]
```


## 変数に紐づくメソッド
リストの `list_a.append(x)` / `list_a.clear()` など、変数に紐づいて操作するメソッドのこと。

append(x)もclear()も、正しくは **[ミュータブルなシーケンス型の演算](https://docs.python.org/ja/3/library/stdtypes.html#mutable-sequence-types)** となっていますが **[このエントリの目的](https://qiita.com/bashaway/items/4899e231f53c722c7f2c#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)** からどんどん離れていってしまうので、そこまで解説しません。（自身も説明できるほど理解できてません）

一般的に使う変数は標準的なデータ型（＝標準ライブラリの組み込み型）で生成されるので、標準ライブラリのメソッドではあるが、コードを記載する中ではよく利用されるのでセクションとして独立させている。

内容は **[前編のデータ型](https://qiita.com/bashaway/items/4899e231f53c722c7f2c#%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B)** と一部重複してます。

### 文字列

https://docs.python.org/ja/3/library/stdtypes.html#string-methods

`count()`は文字列の文字数を数える
```:count()の例
$ python -c "print('strings'.count('s'))"
2
```

`find()`は何文字目に存在するかを調べる
```:find()の例
$ python -c "print('strings'.find('t'))"
1

# 存在しない場合は -1 が返る
$ python -c "print('strings'.find('q'))"
-1
```

`index()`は何文字目から開始するかを調べる
```:index()の例
$ python -c "print('strings'.index('t'))"
1

# 存在しない場合は ValueError となる
$ python -c "print('strings'.index('q'))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: substring not found
```

`fortmat()`は、プレースホルダを指定して、変数をはめ込むことができる。
また、 **[書式指定ミニ言語仕様](https://docs.python.org/ja/3/library/string.html#formatspec)** という指定で任意の書式を指定できる。
```:format()の例
$ python -c "print('Hello, {}'.format('Python'))"
Hello, Python

$ python -c "print('{a} {b}'.format(a='Good',b='morning'))"
Good morning
```

`join()`は文字列配列を文字列にする。
`split()`は文字列を文字列配列にする。
```:join()とsplit()の例
$ python -c "print(','.join(['one', 'two', 'three']))"
one,two,three

$ python -c "print('one,two,three'.split(','))"
['one', 'two', 'three']
```

`upper()`は文字列をすべて大文字にする。
`lower()`は文字列をすべて小文字にする。
```:upper()とlower()の例
$ python -c "print('Strings'.upper())"
STRINGS

$ python -c "print('Strings'.lower())"
strings
```

`replace()`は文字列を置換する。
```:replace()の例
$ python -c "print('strings'.replace('str','th'))"
things
```

`isXXX()`は文字列が特定の条件を満たすかをチェックして論理値を返す。
```:文字列が大文字のみか小文字のみかをチェックする
$ python -c "print('STRINGS'.isupper())"
True
$ python -c "print('Strings'.isupper())"
False

$ python -c "print('strings'.islower())"
True
$ python -c "print('Strings'.islower())"
False
```

```:文字列が英数字か数値かをチェックする
# アルファベットと数字だけか？
$ python -c "print('strings123'.isalnum())"
True
$ python -c "print('strings\n'.isalnum())"
False

# 数値か？
$ python -c "print('strings123'.isdigit())"
False
$ python -c "print('1234567'.isdigit())"
True
```

数値の取り扱いは難しいので、以下参照

* **[isdigit()](https://docs.python.org/ja/3/library/stdtypes.html#str.isdigit)**: 全ての文字が数字
* **[isdecimal()](https://docs.python.org/ja/3/library/stdtypes.html#str.isdecimal)**: 全ての文字が十進数字
* **[isnumeric()](https://docs.python.org/ja/3/library/stdtypes.html#str.isnumeric)**: 全ての文字が数を表す文字



### リスト

https://docs.python.org/ja/3/library/stdtypes.html#mutable-sequence-types

`index()`は、指定した要素が最初に出現する添え字番号を返す。
`count()`は、指定した要素が出現する回数を返す。
```python:コード（count,indexの例）
list_a = ['1', '2', '4', '2']

# indexはその要素が最初に出現する添え字番号を返す
print(list_a.index('2'))

# countはその要素が出現する回数を返す
print(list_a.count('2'))
```

```shell-session:実行結果
1
2
```

`append()` / `insert()` / `extend()` は、既存の配列に新に要素を追加する。
```python:コード（append,insert,extendの例）
list_a = ['1', '2', '4', '2']

# appendは配列の最後にその要素を加える
list_a.append('5')
print(list_a)

# insertは配列の指定した添え字の箇所にその要素を加える
list_a.insert(0,'10')
print(list_a)

# extendは配列に新たな配列を追加（拡張）する
list_a.extend(['str01','str02'])
print(list_a)
```

```shell-session:実行結果
['1', '2', '4', '2', '5']
['10', '1', '2', '4', '2', '5']
['10', '1', '2', '4', '2', '5', 'str01', 'str02']
```

`pop()` / `remove()` / `clear()` は、既存の配列の要素を削除する。
```python:コード（pop,remove,clearの例）
list_a = ['1', '2', '4', '2']

# 配列のうち、指定した添え字の要素がpopされる
# popした対象が返り値になる
print(list_a.pop(0))
print(list_a)

# -1 は配列の最後を指し示す
print(list_a.pop(-1))
print(list_a)

# 配列のうち、最初の'2'が削除される
# removeした対象は返り値にならない
print(list_a.remove('2'))
print(list_a)

# clearはすべての要素を削除する
list_a.clear()
print(list_a)
```

```shell-session:実行結果
1
['2', '4', '2']
2
['2', '4']
None
['4']
[]
```

配列は、文字列変数のように代入して利用しようとすると、想定した動作とならない場合がある。これは、配列を代入しても、同じオブジェクトを参照してしまうためである。
```python:コード（配列を代入するとうまくいかないの例）
list_a = ['1', '2', '4', '2']

# list_aをlist_bに代入した場合
list_b = list_a

# list_aをpopすると
list_a.pop(0)

# list_aの最初の要素は削除されるが
print(list_a)

# list_bの最初の要素も削除されてしまう
print(list_b)

# 理由はlist_bがlist_aと同一のオブジェクトになっているからである
# idは対象オブジェクトの固有番号（オブジェクトID）を返す関数
print(id(list_a),id(list_b))
```

```shell-session:実行結果
['2', '4', '2']
['2', '4', '2']
1204481676864 1204481676864
```

このように配列を複製して利用したい場合は `copy()` を利用する。
```python:コード（copyの例）
list_a = ['1', '2', '4', '2']

# list_aをlist_bにコピーした場合
list_b = list_a.copy()

# list_aをpopすると
list_a.pop(0)

# list_aの最初の要素は削除される
print(list_a)

# list_bはもとのままになる
print(list_b)

# 理由はcopyした場合はlist_bがlist_aと別のオブジェクトになっているからである
print(id(list_a),id(list_b))
```

```shell-session:実行結果
['2', '4', '2']
['1', '2', '4', '2']
2836583929408 2836584006592
```

`sorted()`と`reversed()`は配列の順序を変える。
似た関数に`sort()`と`reverse()`があるが、これらの違いは「配列そのものを操作する」か「新たに配列を生成する」か、の違いである。
```python:コード（sortの例）
list_a = ['3', '2', '4', '2']

# 整列した配列を生成する
list_b = sorted(list_a)
print(list_b)

# list_aとlist_bは別のオブジェクトである
# 配列そのものを整列する
list_a.sort()
print(list_a)

print(id(list_a),id(list_b))
```

```shell-session:実行結果
['2', '2', '3', '4']
['2', '2', '3', '4']
2501765224000 2501765562176
```

`reversed()`の場合は **[前述のオブジェクト](#オブジェクト)** のとおり、iteratorとして返ってくるので、listすれば配列として取り扱える。

```python:コード（reversedの例）
list_a = ['3', '2', '4', '2']

# 逆列したiteratorを生成する
list_b = reversed(list_a)
print(list_b)
# iteratorなのでlistでキャストすればlistとして取り扱える
print(list(list_b))

# 配列そのものを逆列にする
list_a.reverse()
print(list_a)

# list_aとlist_bは別のオブジェクトである
print(id(list_a),id(list_b))
```

```shell-session:実行結果
<list_reverseiterator object at 0x000001B420F6F820>
['2', '4', '2', '3']
['2', '4', '2', '3']
1873158592064 1873158797344
```


### 辞書
https://docs.python.org/ja/3/library/stdtypes.html#mapping-types-dict

```python:コード（updateの例）
dict_a = {
'key11': 1,
'key12': 'moji',
'key13': False,
}

dict_b = {
'key21': 33,
'key22': 'string',
'key23': True,
'key12': 'new moji'
}

# dict_aのオブジェクトIDを表示
print(id(dict_a))

# updateは既存の辞書に、別の辞書を加える
dict_a.update(dict_b)
print(dict_a)
print(id(dict_a))

# | は新たな辞書を生成する操作である
dict_a = dict_a|dict_b
print(dict_a)
print(id(dict_a))
```

```shell-session:実行結果
2017211977088
{'key11': 1, 'key12': 'new moji', 'key13': False, 'key21': 33, 'key22': 'string', 'key23': True}
2017211977088
{'key11': 1, 'key12': 'new moji', 'key13': False, 'key21': 33, 'key22': 'string', 'key23': True}
2017212041664
```



```python:コード（get,popの例）
dict_a = {
'key11': 1,
'key12': 'moji',
'key13': False,
}

# キーがkey11の値を取得
print(dict_a.get('key11'))
# キーが存在しない場合はNoneが返る
print(dict_a.get('undef'))

# キーがkey11の値を取り出す（もとの辞書からは削除される）
print(dict_a.pop('key12'))
print(dict_a)

# キーが存在しない場合はKeyErrorになる
print(dict_a.pop('undef'))
print(dict_a)
```

```shell-session:実行結果
1
None
moji
{'key11': 1, 'key13': False}
Traceback (most recent call last):
  File "C:\Users\basha\tmp\python\sample.py", line 17, in <module>
    print(dict_a.pop('undef'))
KeyError: 'undef'
```

```python:コード（items,keys,valuesの例）
dict_a = {
'key11': 1,
'key12': 'moji',
'key13': False,
}

# itemsはキーと値のタプルのビュー（リストのようなもの）が返る
print(dict_a.items())

# keysはキーのみのビュー（リストのようなもの）が返る
print(dict_a.keys())

# valuesは値のみのビュー（リストのようなもの）が返る
print(dict_a.values())
```

```shell-session:実行結果
dict_items([('key11', 1), ('key12', 'moji'), ('key13', False)])
dict_keys(['key11', 'key12', 'key13'])
dict_values([1, 'moji', False])
```


## メソッドチェーン
データ型に対してメソッドを呼んだとき、その返り値に対してメソッドを呼ぶことができる。ただし、呼べるメソッドはデータ型に対応したものである必要がある。


```python:コード
str_a='Strings'

# str_bはstr_aがすべて大文字になった文字列である
str_b = str_a.upper()
print(str_b)

# str_cはstr_b(=str_aがすべて大文字になった文字列)を
# Sをsに置き換えた文字列である
str_c = str_b.replace('S','s')
print(str_c)

# str_dはstr_aがすべて大文字になった文字列を、Sをsに置き換えた文字列である
str_d = str_a.upper().replace('S','s')
print(str_d)

# 左側から順番に操作していくので、これでは想定する結果が得られない
str_e = str_a.replace('S','s').upper()
print(str_e)
```

```shell-session:実行結果
STRINGS
sTRINGs
sTRINGs
STRINGS
```

また、チェーンする数が多くなったり、関数名や引数が長い場合は、可読性が下がるので、複数行に分けて記述することができる。

Pythonではカッコで括られた部分は文字の途中でなければ改行できるので、以下のように記述することができる。
```python:コード
str_a='Strings'

# 全体をカッコで括るパターン
str_new = ( str_a
           .upper()
           .replace('S','s')
          )
print(str_new)

# メソッドのカッコで改行するパターン
str_new = str_a.upper(
          ).replace(
            'S',
            's'
          )
print(str_new)
```

```shell-session:実行結果
sTRINGs
sTRINGs
```

適用させるメソッドのデータ型が合致すれば、メソッドチェーン内で型が変わっていっても問題ない。
```python:文字列→リスト→文字列→リストと変わっている
$ python -c "print(','.join(['one', 'two', 'three']).upper().split('T'))"
['ONE,', 'WO,', 'HREE']
```




## ライブラリ/パッケージ/モジュール

https://docs.python.org/ja/3/library/index.html

Pythonには標準ライブラリというさまざまな機能を提供するパッケージが存在している。「**[テキスト処理サービス](https://docs.python.org/ja/3/library/text.html)**」や「**[汎用オペレーティングシステムサービス](https://docs.python.org/ja/3/library/allos.html)**」などに分類される。

ライブラリ、パッケージ、モジュール、クラス、関数、などの用語が出てくるが、包含関係としては以下のように覚えておけばよい。

* パッケージ（複数モジュール（ファイル）の集合＝フォルダ）
* モジュール（クラスや関数をファイルにしたもの）
* クラス・関数

同一のものをライブラリと呼んだり、パッケージと呼んだりするが、あまり明示されていないようなので、意図が通じれば気にしなくてよいと思います。（というか、自身がよく理解できていません）

ライブラリを利用する場合は `import xxx` でどのライブラリを利用するかを指定する必要があります。

以下よく利用される標準ライブラリとその使い方を解説します。


### os

https://docs.python.org/ja/3/library/os.html

```shell-session:環境変数
$ python -c "import os; print(os.environ)"
environ({'ACLOCAL_PATH': '/mingw64/share/aclocal:/usr/share/aclocal', 
（～環境変数が表示される～）
```

```shell-session:ディレクトリ作成(注！カレントディレクトリ配下に sample-dir が作成されます！)
$ python -c "import os; print(os.mkdir('sample-dir'))"
None
```

`os.system()` OSコマンドが実行できる関数ですが `subprocess`モジュール の利用が推奨されています。

### subprocess

https://docs.python.org/ja/3/library/subprocess.html

基本的な使い方は以下のとおり。
* capture_output: 標準出力と標準エラー出力を返り値に含める
* text: 標準出力と標準エラー出力を文字列にする（デフォルトFalseの場合はバイト列になる）
* encoding: 標準出力と標準エラー出力の文字列エンコーディングを指定する

コマンドの実行結果は返り値の `stdout` に含まれている。
```python:コード
import subprocess

# 実行したいコマンドを配列に格納する
cmd = ["ls","-hs","*.py"]
cp = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
print("stdout:", cp.stdout)

# 文字列として指定する場合は shell=True とする
cmd = "ls -hs *.py"
cp = subprocess.run(cmd, shell=True, capture_output=True, text=True, encoding="utf-8")
print("stdout:", cp.stdout)
```

```shell-session:実行結果
stdout: 1.0K sample.py

stdout: 1.0K sample.py
```

コマンドの実行結果で標準エラー出力がある場合は `stderr` に含まれている。
コマンドの実行結果が正常終了でない場合は `check=True` とすることでPython内で例外として取り扱うことができるようになる。
```python:コード
import subprocess

# 標準エラー出力は stderr に格納される
cmd = ["ls", "wrong-filename"]
cp = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
print("stdout:", cp.stdout)
print("stderr:", cp.stderr)

# check=Trueとするとエラー時にPythonの例外を発生させる
cmd = ["ls", "wrong-filename"]
cp = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", check=True)
```

```shell-session:実行結果
stdout:
stderr: ls: cannot access 'wrong-filename': No such file or directory

Traceback (most recent call last):
  File "C:\Users\basha\tmp\python\sample.py", line 11, in <module>
    cp = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", check=True)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\subprocess.py", line
 526, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['ls', 'wrong-filename']' returned non-zero exit status 2.
```


### re

https://docs.python.org/ja/3/library/re.html

`re.match` / `re.search` / `re.fullmatch` はいずれも、文字列を正規表現でチェックする。
３つの違いは **[search() vs. match()](https://docs.python.org/ja/3/library/re.html#search-vs-match)** のとおり。

> re.match() checks for a match only at the beginning of the string
re.search() checks for a match anywhere in the string (this is what Perl does by default)
re.fullmatch() checks for entire string to be a match

* `re.match()` 「対象文字列の先頭」でマッチするかをチェックする
* `re.search()` 「対象文字列のどこか」でマッチするかをチェックする
* `re.fullmatch` 「対象文字列のすべて」でマッチするかをチェックする

```python:コード
import re

# 先頭がマッチするかチェックするのが match
print(re.match("abc", "abcdef"))
print(re.match("bcd", "abcdef"))

# 先頭以外でもマッチするかチェックするのが search
print(re.search("cd", "abcdef"))
print(re.search("^c", "abcdef"))

# 全体がマッチするかチェックするのが fullmatch
print(re.fullmatch("p.*n", "python"))
print(re.fullmatch("r.*n", "python"))
```

マッチしていれば `Matchオブジェクト` というものが返る。
マッチしていなければ `None` が返る。

```shell-session:実行結果
<re.Match object; span=(0, 3), match='abc'>
None
<re.Match object; span=(2, 4), match='cd'>
None
<re.Match object; span=(0, 6), match='python'>
None
```

Matchオブジェクトではなく、文字列として返したい場合は `findall` を利用する。
`findall` はマッチした要素が文字列の配列として返ってくる

`r'[文字列]'` は raw文字列というもので、エスケープシーケンスを含む特殊文字を特殊文字ではなく、そのままの文字列として解釈させる指定ができる。`re`を利用するときによく利用される。

```python:コード
import re

# \b は単語の区切りという意味の正規表現
print(re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest'))

# グループ化すると、グループ化した要素だけがタプルで返ってくる
print(re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10'))

# re.IGNORECASE を指定すると、大文字小文字が無視される
print(re.findall(r'python', 'PYTHON Python python'))
print(re.findall(r'python', 'PYTHON Python python', re.IGNORECASE))
```

```shell-session:実行結果
['foot', 'fell', 'fastest']
[('width', '20'), ('height', '10')]
['python']
['PYTHON', 'Python', 'python']
```

`findall` はマッチした部分を文字列配列として取り出すが、マッチした部分で分割するには `split` を利用する。

```python:コード
import re

# \s はホワイトスペース（空白、タブ、改ページ、改行など）という意味の正規表現
print(re.split(r'\s', 'which foot　or hand fell fastest'))

# 3つめの引数は maxsplit で、最大何回区切るか、を指定することができる
print(re.split(r',', 'Words, words, words.'))
print(re.split(r',', 'Words, words, words.', 1))
```

```shell-session:実行結果
['which', 'foot', 'or', 'hand', 'fell', 'fastest']
['Words', ' words', ' words.']
['Words', ' words, words.']
```

正規表現でマッチした部分を別の文字列で置き換える場合には `sub` を利用する。


```python:コード
import re

# 置換したい正規表現, 置換先の文字列, 置換元の文字列 の順に指定する
print(re.sub(r',\s', ' / ', 'apple, banana, carot'))

# 4つめの引数は count で、最大何回置換するか、を指定することができる
print(re.sub(r',\s', ' / ', 'apple, banana, carot', 1))
```

```shell-session:実行結果
apple / banana / carot
apple / banana, carot
```


# 入出力

これまでのサンプルでは、コードに記載された情報だけを利用して処理をしていた。
（たとえば、コード内の決め打ちの変数を `print` するなど）

ここでは、コードに記載された情報以外の情報をプログラムに引き渡すことで、情報の入出力ができる方法について解説する。


## コマンドライン引数

これまでは `python [ファイル名]` という形式でプログラムを実行していたが、ファイル名の後に引数をつけることで、その引数をプログラム内で利用することができる。



## キーボードからの入力

Pythonスクリプトへはキーボード入力を受け付けてコードの動作に反映させることができる。

```python:コード
moji = input("input strings: ")
print("入力文字： " + moji)
```

```shell-session:コードを実行すると以下で一時停止する
input strings: ← ここで文字を入力してEnterを押す
```

「おはよう」と入力すると、入力された文字をそのまま出力する

```shell-session:実行結果
input strings: おはよう
入力文字： おはよう
```

## ターミナルへの出力

`print`はターミナルへ文字を出力していたが、文字列を出力する方法が何通りかある。

```python:コード
str_morning = "おはよう"
str_noon = "こんにちは"
str_evening = "こんばんは"

# 文字列として連結したものを表示させる
print(str_morning + str_noon + str_evening)

# 複数の文字列をカンマで区切るとスペースで連結される
print(str_morning, str_noon, str_evening)

# 連結する文字をsepで変更できる（デフォルトはスペース）
print(str_morning, str_noon, str_evening, sep='#')

# endで行末文字を指定できる（デフォルトは改行コード）
print(str_morning, str_noon, str_evening, end='#EOL\n')

# 変数名を直接指定することもできる
print(f'Morning: {str_morning}')

# {}を利用したフォーマット
print('Morning: {morning}, Noon: {noon}'.format(morning=str_morning,noon=str_noon))
```

```:実行結果
おはようこんにちはこんばんは
おはよう こんにちは こんばんは
おはよう#こんにちは#こんばんは
おはよう こんにちは こんばんは#EOL
Morning: おはよう
Morning: おはよう, Noon: こんにちは
```


## ファイルからの入力

以下のようなファイルをあらかじめ作成しておく

```:hosts.txt
hostname01.example.com
hostname02.example.com
hostname03.example.com
```

```python:コード
file = 'hosts.txt'

# 読み込みモードのときは mode='r' とするが、省略可である
with open(file) as f:
    # ファイル全体をひとつの変数として取り込む場合はread()を使う
    stream = f.read()
    print(stream)

# いちど読み込んだファイルを再度読み込む場合には、再度openから始める必要がある
# 読み込む場所を移動するseekもありますが、ここでは触れません
with open(file) as f:
    # ファイル全体をリストとして取り込む場合はreadlines()を使う
    stream = f.readlines()
    print(stream)
```

```shell-session:実行結果
server01.example.com
server02.example.com
server03.example.com

['server01.example.com\n', 'server02.example.com\n', 'server03.example.com\n']
```


## ファイルへの出力

```python:コード
file = 'output.txt'

# 書き込みモードのときは mode='w' とする
with open(file, mode='w') as f:
    # ひとつの変数を書き込む場合はwrite()を使う
    host_body = 'hostname01.example.com\n'
    host_body += 'hostname02.example.com\n'
    host_body += 'hostname03.example.com\n'
    f.write(host_body)

# 追記モードのときは mode='a' とする
with open(file, mode='a') as f:
    # リストを書き込む場合はwritelines()を使う
    host_list = ['hostname01.example.com\n',
                 'hostname02.example.com\n',
                 'hostname03.example.com\n',
                ]
    f.writelines(host_list)
```

これを実行してもターミナルには何も表示されない（print文などで標準出力へ出力していないため）が、 `output.txt` というファイルが生成されている。

```shell-session:output.txtを開く
$ cat output.txt
hostname01.example.com
hostname02.example.com
hostname03.example.com
hostname01.example.com
hostname02.example.com
hostname03.example.com
```

自動的に改行を付与するオプションは無いので、リストを改行付きで書き込みたい場合は、以下のように工夫する必要がある。

```python:コード
file = 'output.txt'

with open(file, mode='w') as f:
    host_list = ['hostname01.example.com',
                 'hostname02.example.com',
                 'hostname03.example.com',
                ]

    # リストを改行コードでjoinした文字列をwrite()で書き込む
    f.write('\n'.join(host_list) + '\n')

    # リストの各要素に改行コードを追加したリストをwritelines()で書き込む
    f.writelines(host + '\n' for host in host_list)
```



## 練習問題

<details><summary>"input strings: " というプロンプトでキーボード入力を待ち受けて、入力された文字列をファイル(output.txtとする)に追記して終了する処理</summary>

```python:コード
file = 'output.txt'

prompt = "input strings: "
moji = input(prompt)
buf = prompt + moji + '\n'

with open(file, mode='a') as f:
    f.writelines(buf)
```

</details>

```shell-session:実行結果
$ python ./sample.py
input strings: sample strings
               ^^^^^^^^^^^^^^
               ↑の部分がキーボード入力

$ python ./sample.py
input strings: 30
               ^^
               ↑の部分がキーボード入力

$ cat output.txt
input strings: sample strings
input strings: 30
```


# 例外処理

前編の型変換のセクションで、変換できないものがエラーになると記載した。このエラーが「例外」のことで、そのままでは処理が途中で強制終了ししてしまいます。

```python:コード
moji = 'sample'
print(int(moji))
print('例外発生後の処理')
```

```shell-session:実行結果
Traceback (most recent call last):
  File "C:\Users\basha\tmp\python\sample.py", line 2, in <module>
    print(int(moji))
ValueError: invalid literal for int() with base 10: 'sample'
```

このような場合に、「例外処理」として発生したエラーの内容に応じて後続の処理を実施することができます。

## 例外の種類

実行時に発生しやすい例外として以下のものがある。

|例外|意味|
|:--:|:--:|
|`TypeError`|型が違うことにより処理することができない場合|
|`ValueError`|型は問題ないが処理することができない場合|
|`IndexError`|（リストやタプルなどで）存在しない添え字にアクセスしようとした場合|
|`KeyError`|（辞書型で）存在しないキーにアクセスしようとした場合|
|`ZeroDivisionError`|ゼロ除算が実行された場合|

このほか`OverflowError`もありますが、整数では発生しないため、このエントリでは取り上げません。

**TypeError**
以下の場合、文字列型と整数型を結合しようとしているのでエラーになります。
```:TypeErrorの例
$ python -c "a='str'+10"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
```

**ValueError**
以下の場合 `int` による型変換で`str`という文字列を変換しようとしているのでエラーになります。文字列の`100`を変換する場合にはエラーが発生しません。
```:TypeErrorの例
$ python -c "a=int('str')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'str'
```

**IndexError**
以下の場合 `range(3)` に対する添え字でのアクセスは０～２のみが許可されるためエラーになります。1番目にアクセスするのであればエラーが発生しません。
```:IndexErrorの例
$ python -c "a=range(3)[99]"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
IndexError: range object index out of range
```

**KeyError**
以下の場合はキーが `key1` しか存在しないので、キーとして `key` を参照しているためエラーになります。キーとして `key1` を参照するのであればエラーが発生しません。
```:KeyErrorの例
$ python -c "a={'key1': 'value1'}; v=a['key']"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
KeyError: 'key'
```

**ZeroDivisionError**
以下の場合はゼロで除算しているためエラーになります。
```:ZeroDivisionErrorの例
$ python -c "a=2/0"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: division by zero
```


## 例外のキャッチ

例外が発生しそうな処理の記載箇所はあらかじめ`try`に記載しておき、例外が発生したら`except`の処理を実行することができます。例外の発生を検出することは、例外をキャッチする、と言ったりもします。

```python:コード
moji = 'sample'
try:
    print(int(moji))
except:
    print('例外が発生')
print('例外発生後の処理')
```

```shell-session:実行結果
例外が発生
例外発生後の処理
```

上記の例では `except:` と記載していますが、この記載ではどのような種類の例外もキャッチして処理を実行します。

例外の種類ごとに異なる処理を実行したい場合には、特定の例外だけをキャッチするように記載します。


```python:コード
moji = input("input number: ")
list_a = [10, 20, 30]
try:
    print('入力された数字： '+moji)
    print('リストの' + moji +'番目の値： '+str(list_a[int(moji)]))
except ValueError:
    print('ValueErrorが発生')
except IndexError:
    print('IndexErrorが発生')
except:
    print('不明な例外が発生')
```

```shell-session:実行結果
$ python ./sample.py
input number: 1
入力された数字： 1
リストの1番目の値： 20

$ python ./sample.py
input number: sample
入力された数字： sample
ValueErrorが発生

$ python ./sample.py
input number: 100
入力された数字： 100
IndexErrorが発生
```


## 練習問題

<details><summary>前述の練習問題を改修して、書き出すファイルには数値のみ出力し、キーボード入力された文字を整数として取り扱い、ファイルの最終行の数値と入力された数値を合算してファイルに追記する。ただし、キーボード入力された文字が整数として取り扱えなかった場合はエラーメッセージを出力して処理を終了する。</summary>

```python:コード
file = 'output.txt'

# output.txtの最終行を読み取り整数変換する
# 読み取った数値を prev に代入する
# ファイルがない場合は例外処理として prev に0を代入する
try:
    with open(file) as f:
        stream = f.readlines()
        prev = int(stream[-1])
except:
    prev = 0

# キーボード入力を受け付けて整数変換する
# 整数変換で例外となった場合はメッセージを出力して処理を終了させる
try:
    num = int(input('input strings: '))
    print('OK')
except:
    print('NG: not a number')
    exit()

# 処理が進んだ場合は、最初に読み取った数値とキーボード入力の数値を合算してファイルに出力する
with open(file, mode='a') as f:
    f.writelines(str(prev+num)+'\n')
```

</details>

```shell-session:実行結果
$ rm output.txt

$ python ./sample.py
input strings: sample
NG: not a number

$ python ./sample.py
input strings: 10
OK

$ cat output.txt
10

$ python ./sample.py
input strings: 2
OK

$ cat output.txt
10
12
```


# さいごに

ユーザ定義関数とか、外部ライブラリ＆virtualenvとかもやりたかったのですが、ここまでが限界でした。
前編とあわせてここまで手を動かしてやってみれば、Pythonでの簡単な文字列処理や数値集計など、ある程度やりたいことはできるんじゃないか～、と思います。