pythonエンジニア認定試験受験記③

### 本記事について
以下のオデッセイ開催のpython基礎認定試験を今度受験するにあたり
勉強して上で学んだことを記事でアウトプットしていく。
前回記載の記事が長くなっていたのでパート3で分けていく

https://cbt.odyssey-com.co.jp/pythonic-exam.html

### 自作モジュールのインポートについて

まず以下のおなじみフィボナッチ級数を書き出すプログラムを書く

```python:fibo.py
def fib(n):
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()

def fib_l(n):
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result
```

この自作のモジュールを読み込むにはインタープリター上で次のようにする

```python
from fibo import fib, fib_l
fib(1000)
fib_l(1000)
```

```python:実行結果
>>> fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fib_l(1000)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
```

ポイントは次の部分
`from fibo import fib, fib_l`
fiboというモジュールからfibとfib_lという関数をインポートしますという指定である
他にも
`from fibo import *`
という指定で全て読み込めるが余計なものまで読み込んでしまうため非推奨である

`from <モジュール> import <関数名>`
と覚えておくと良い

さらにディレクトリにこの自作のプログラムを入れることでパッケージとして利用できる
※別途＿＿init__.pyというからファイルを作成することでパッケージとして認識される

```:ディレクトリ構成
$ ls -l fib_package
-rw-r--r--  1 xxx-pc  staff  234  6 13 19:36 fib_package/fibo.py
-rw-r--r--  1 xxx-pc  staff  0  6 13 20:54 fib_package/__init__.py

from fib_package.fibo import fib, fib_l
fib(1000)
fib_l(1000)
```
結果は同じなので割愛する

因みに `__init__.py` に以下のように記載することで`import *`と実行した時に読み込まれるモジュールを指定できる
※今回はfibo.pyしかないので次の通り

```python
__all__ = ["fibo"]
```

### 構文の実行

pythonで複数の変数を無理やり1行に入れたいと言った時に使えるテクニック

```python
a = 10
b = 20
c = 30
print(a + b + c)
```

これをセミコロンで区切って

```python
a = 10; b = 20; c = 30; print(a + b + c) 
```

のように書ける
・・・がまずこんな風にはpythonのお作法として書かない
やるのであればタプルのアンパッキングを使うのが良いと思う

```python
a, b, c = 10,20,30
print(a,b,c)
```

用途としてインタープリター上で確認したいとかそういう用途かと思う

### 正規表現

正規表現を使うにはまずreモジュールを読み込みcompileで正規表現にマッチさせるパターンを指定する

```python
import re
p = re.compile('[a-z]+')
```

マッチさせたい文字列をmatchで指定する
今回は'makeyourchoice'というキーワードを指定

```python
m = p.match("makeyourchoice")
```

結果をgroup(),start(),end(),span()でそれぞれ確認する

```python:実行結果
m.group()
'makeyourchoice'

m.start(), m.end()
(0, 14)

m.span()
(0, 14)
```

- group()はマッチした文字列を返す
 - 今回は小文字のa-zなので全てマッチする
- start(),end()はマッチした開始位置と終了位置を返す
 - 今回は0番目から最後の14文字目まで表示
- span()は開始位置と終了位置をタプルで返す

いずれもmatch()の場合は先頭から走査していくので今回の場合だと
'mAkeyourchoice'と指定した場合はmしかマッチしない
参考までに以下の通り

```python:実行結果
m = p.match("mAeyourchoice")
m.group()
'm'

m.start(), m.end()
(0, 1)

m.span()
(0, 1)
```

search()をしてすると全体を走査していく
この場合開始はmatchのように先頭から見ていかないのでstart()やspan()は0以外になる可能性がある


```python:実行結果
m = p.search("MAkeyourChoice")

m.group()
'keyour'

m.start(),m.end()
(2, 8)
m.span()
(2, 8)
```

条件にマッチした部分を全て抜き出したい場合は、findall()を使うと良い
マッチした箇所だけリストで抜き出してくれる

```python
p = re.compile("[a-z]+")
m = p.findall("MAkeyourChoice")
print(m)
```

```python:実行結果
['keyour', 'hoice']
```

finditer()を使うと条件にマッチした部分を抜き出した上でイテレータとして格納してくれる

```python
import re
p = re.compile("[a-z]+")
m = p.finditer("MAkeyourChoice")

for match_word in m:
    print(match_word.group())
    print(match_word.span())
```

```python:実行結果
keyour
(2, 8)
hoice
(9, 14)
```

###ファイルの読み書き

ファイルの書き込みはopen()を使う
2種類やり方があるが最初のやり方は推奨されていないため、記憶の片隅に留めておくレベルで忘れて良い

- 一つ目の方法(openをそのまま使う)

```python
f = open('sample.txt', 'w')
f.write('インド人を右へ')
f.close()

f = open('sample.txt')
f.read()
f.close
```

- 二つ目の方法(with句を使う)

```python
with open('sample2.txt', 'w+') as f:
    f.write('今夜はとってもヒヤシンス\n明日は雪がフリージア！')
    f.seek(0)
    print(f.read())
```

2点目が良いこととしてまずいちいちclose()でファイルを閉じなくても良いところ
あと書き込みの後読み込む際はseek(0)で書き込み位置を最初に戻してやらないと読み込めないので注意
要は入力カーソルが書き込まれた時点では最終行のあると思うと分かりやすい

合わせて読み込むときの読み込み方にも言及してみる
read以外にもreadline,readlinesがある
それぞれの違いは次の通り

```python
with open('sample2.txt') as f:
    print("###read - 引数なし###")
    print(f.read())
    f.seek(0)
    print("###read - 文字数指定###")
    print(f.read(3))
    f.seek(0)
    print("###readline - 1行実行###")    
    print(f.readline())
    f.seek(0)
    print("###readlines - 全行実行してリスト化###")    
    print(f.readlines())
```

```python:実行結果
###read - 引数なし###
今夜はとってもヒヤシンス
明日は雪がフリージア！
###read - 文字数指定###
今夜は
###readline - 1行実行###
今夜はとってもヒヤシンス

###readlines - 全行実行してリスト化###
['今夜はとってもヒヤシンス\n', '明日は雪がフリージア！']
```

まとめとしてreadは文字を出力しているので
1行ずつ出力してなんらかの処理をさせたい場合はreadlinesを利用する必要があることがわかる。


### jsonの取り扱い

jsonの取り扱いについてはまずjsonモジュールをインポートする

`import json`

その後出力や書き込みを行うには次の通り
まずは辞書を用意する

```python
import json
x = {
   'name':'Suzuki',
   'age':15,
   'gender':'male',
   'score':[70,60,55,68,90]
}
```

dumpsを使うことでjson形式として出力できる

```python
print('raw: ', x)
print('json: ', json.dumps(x))
```

```python:実行結果
raw:  {'name': 'Suzuki', 'age': 15, 'gender': 'male', 'score': [70, 60, 55, 68, 90]}
json:  {"name": "Suzuki", "age": 15, "gender": "male", "score": [70, 60, 55, 68, 90]}
```

rawがそのまま辞書を出力したもの、
jsonがjson形式にダンプして出力したものである
keyvalueがダブルクウォーテーションで囲まれていることがわかる

同様にこれをファイルに書き出しそれを読み込む場合を考えてみる
その場合はdumpとloadを使う
末尾のsがつかないので間違いに注意する

```python
with open('profile.json', 'w') as f:
    json.dump(x, f)

with open('profile.json') as f:
    print(json.load(f))
```

```python:実行結果
{'name': 'Suzuki', 'age': 15, 'gender': 'male', 'score': [70, 60, 55, 68, 90]}
```

実行結果の通りjsonからpythonで読み込める辞書型に変換してくれたことを確認できた

### 実行環境について
pythonのコードを書くにあたり大体の人はvscodeかPyCharmを使っているかと思う。
個人的にはどちらも入れていて若干拡張機能の多さと無料であることとからvscodeの方が良いのかなと思って使っている(PyCharmはCommunity版であれば一応無料だが)
ただ最近はもっぱらjupyter Notebookにコードを書いて清書をvscodeにしていた。

理由は実行しながらコードが書けるのがお手軽なのと、
補完機能を一通り網羅しているからである

ただ一つだけ不満があって引数つきのpythonスクリプトを実行するときはいちいちvscodeやPyCharm側のターミナルを立ち上げていたのだが、その後調べたらそもそもそんなことをしなくて良いことが判明した。

やり方は以下の通り

適当な引数が必要なコードを用意する

```
%%file error_test.py

import sys

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()
```

上記をShift+EnterとかでJupyterNotebookで実行すると
なんとerror_text.pyというファイルが実行したスクリプトの配下にできるのである
`%%file　<ファイル名>`　で<ファイル名>のファイル作りますよという意味

あとは次の通り適当に引数を渡してやれば良い
`%%!` でシェルコマンド実行しますよという意味になる

```
%%!
error_test.py 'profile.json'
```

```python:実行結果
['profile.json has 1 lines']
```


これで完全にターミナルを起動する必要がなくなった。
因みに他にも色々あるようなので `マジックコマンド jupyterNotebook` とかで検索をかけて貰えば捗ると思う。

ありがとう Jupyter Notebook
これからもよろしく Jupyter Notebook