【Python】超個人的Pythonことはじめ

実務経験あったものの、基礎がゆるゆるだったため、Python学習始めました。
学習のメモをつらつらと。

# 文法系

## 型ヒント

### 引数、戻り値の型を指定する場合
```ruby
# 引数と戻り値にstr型を指定
def func(title: str) -> str:
    return title

# 戻り値がない場合
def func(title: str) -> None:
    print(f"このタイトルは、{title}です")

```

### 特定の「値」のみを許可する場合
```ruby
from typing import Literal
TYPE = Literal['txt', 'html']
```

なお、型ヒントはあくまでヒントのため、異なる型を代入してもエラーにはならない。


## リスト内包表記

基本構文： `変数を使った処理 for 変数 in イテラブルオブジェクト`

if文を含む構文： `変数を使った処理 for 変数 in イテラブルオブジェクト if 条件式`

```ruby: test.py
# 基本形
num = [i for i in range(10)]

num2 = [i for i in range(10) if i % 2 == 0]

```

`{k, v for k, v in dic.items()}` の先頭の「k, v」のように、「変数を使った処理」の箇所で変数をカンマで並べると、SyntaxError になる。
「変数を使った処理」の箇所では、数値と数値を四則演算したり、文字列と文字列を結合したり、コロンを使って新しい辞書を作成したりできる。
また、`{(k, v) for k, v in dic.items()}` のように、タプルを要素とした集合を生成することもできる。

```ruby: test.py

dic = {1: 2, 3: 4}

# Success
# {3, 7}
num = {k + v for k, v in dic.items()}

# Success
# {(1, 2), (3, 4)}
num2 = {(k, v) for k, v in dic.items()}

# Error
num3 = {k, v for k, v in dic.items()}

```


## with 文

with 文でファイルの処理を書くと、処理中に例外があっても最後にファイル閉じてくれる。
try-finally 構文と同じ。

```ruby: test.py
# with ver.
with open('test.txt') as f:
    print(f.read)

# try-finally ver.
f = None
try:
    # 明示的にファイルを 'r' = 読み込みモード で開く
    # モードの引数がなかった場合は、'r' であると解釈される
    f = open('test.txt', 'r')
    print(f.read)
finally:
    if f:
        f.close()
```
なお、ファイルの扱いは[こちら](https://utokyo-ipp.github.io/4/4-1.html)参照で。


## デコレーター

### @property
クラスで定義したメソッドを実行する場合、通常は `obj.culc()` のように「( )」が必要。
しかし、「@property」を付けることで、`obj.culc` のように「()」不要で実行できるようになるデコレーター。
具体例としては、`setter`メソッド や `getter`メソッド、`deleter`メソッドを明示的に実装できる。

### @patch
ユニットテストで特定のメソッドをモックオブジェクトに置き換えるデコレーター。

### @classmethod
メソッド内でクラス自身を参照できるようにするデコレーター。

### @staticmethod
インスタンス化せずにメソッドを使うためのデコレーター。


# メソッド系

## split()
文字列を分割してリストを返す。
`split()` のように引数がない場合は空白文字で分割する。
`split(',')` のように引数でセパレータを指定した場合は、指定のセパレータ（この場合は「,」）で分割する。
```ruby: test.py
sample = 'No Music No Life'
result = sanple.split()
print(result)
```

```ruby: console
['No','Music','No','Life']
```

## join()
構文：`'[char]'.join(iterable)`
｢char」で指定した文字で「iterable」を結合し、文字列を返す。
```ruby: test.py
sample = 'No Music No Life'
result = sanple.split()
result2 = ','.join(result[0:2])
print(result2)
```

```ruby: console
No,Music
```


## items()

辞書型のキーと値を一度に取り出す場合は、items()メソッドを使う
```ruby: test.py
def func(**kwargs):
    for k, v in kwargs.items():
        print(f'{k}: {v}')

dic = {'a': 'Lion', 'b': 'Tiger'}

func(**dic)
```

```ruby:console
a: Lion
b: Tiger
```

辞書のキーと値を一度に取り出す場合は、items()メソッドを使います。

## keys()
辞書の中のすべてのキーの一覧を取得するには keys()メソッドを使います。

```ruby: test.py
mydict = {"P":"Python", "J":"Java", "R":"React"}

print(mydict.keys())
```

```ruby: console
dict_keys(['P', 'J', 'R'])
```

## values()
辞書の中のすべての値の一覧を取得するには values()メソッドを使います。

```ruby: test.py
mydict = {"P":"Python", "J":"Java", "R":"React"}

print(mydict.values())
```

```ruby: console
dict_keys(['Python', 'Java', 'React'])
```


## isalnum()
isalnum()メソッドは、数字と文字だけの場合はTrueを返し、記号が含まれている場合はFalseを返す。


## isalpha()
isalpha()メソッドは、文字だけの場合はTrueを返す。
アルファベットではない日本語などの非ASCII文字でも、数字や記号を含まない場合はTrueを返す。


## swapcase()
大文字を小文字に、小文字を大文字に変換する。
```ruby: test.py
'MUSIC book'.swapcase()

# 結果
'music BOOK'
```


## title()
単語ごとに「大文字1文字＋小文字」の形式に変換する。
```ruby: test.py
'music book'.title()

# 結果
'Music Book'
```


## capitalize()
先頭1文字を大文字に、それ以外を小文字に変換する。
```ruby: test.py
'music book'.capitalize()

# 結果
'Music book'
```

## isinstance()
オブジェクトの型チェックを行うメソッド。
第一引数にチェック対象オブジェクト、第二引数にチェックしたいデータ型を渡す。
戻り値はbool型。
```ruby: test.py
isinstance(1, int)
isinstance(1, str)

# 結果
True
False
```
複数も可能。
```ruby: test.py
isinstance([], (list, tuple))
isinstance("1.0", (int, float))

# 結果
True
False
```
また、チェックは継承元に遡って行われる。
```ruby: test.py
isinstance(True, bool)
isinstance(True, int)
isinstance(True, float)

# 結果
True
True # bool型は、int型を継承している
False
```

# ライブラリ系

## json
- jsonモジュールで**ファイル**を扱う場合、読み込みは **load()** 関数、書き込みは **dump()** 関数
- jsonモジュールで**文字列**を扱う場合、読み込みは **loads()** 関数、書き込みは **dumps()** 関数

### json.loads と json.dumps の挙動の違い
`loads()` メソッドの場合
```ruby: test.py
import json

# JSONオブジェクトをPythonオブジェクトへ変換
data = json.loads("[1,2,3,4]")

print(data[0])
```

```ruby:console
# Pythonオブジェクトになるので、インデックスで対応する値が取れる
1
```

`dumps()` メソッドの場合
```ruby: test.py
import json

# PythonオブジェクトをJSONオブジェクトへ変換
data = json.dumps([1,2,3,4])
print(data[0])
```

```ruby:console
# JSONオブジェクトになるので、インデックスで文字列の値が取れる
[
```


## unicodedata

unicodedataモジュールの `normalize()` メソッドは、文字列の正規化で利用する。

```ruby: test.py
import unicodedata
unicodedata.normalize('NFKC', 'ｱアAＡ')

# 結果
'アアAA' 
```

「NFKC」で正規化の条件を指定している。
上記例では、「半角カナを全角カナに変換し、全角英数字を半角英数字に変換」している。


## enum

enumモジュールは、列挙型での定数定義で利用する。

```ruby: test.py
import enum
class Animals(enum.Enum):
    DOG = 1
    CAT = 2
    BEAR = 3


# enum.auto() を使うと、自動で連番になる
import enum
class Animals(enum.Enum):
    DOG = enum.auto() # 1
    CAT = enum.auto() # 2
    BEAR = enum.auto() # 3


# @enum.unique を使うと、重複した値を設定できなくなる
import enum
@enum.unique
class Animals(enum.Enum):
    DOG = 1
    CAT = 1 # ValueErrorが発生
```

別の列挙型に定義された定数や列挙型以外の型との比較の場合、同じ値であっても`False`と判定される

```ruby: test.py
import enum

class Ani(enum.Enum):
  CAT = 1
  FOX = 2

class Ani2(enum.Enum):
  CAT = 1
  FOX = 2


Ani.CAT == Ani.CAT # True
Ani.CAT == Ani2.CAT # False
Ani.CAT == 1 # False
```
列挙型で定義した定数名を`name`で、値を`value`で取得できる。

```ruby: test.py
Ani.CAT.name # CAT
Ani.CAT.value # 1
```


## itertools

itertoolsモジュールの `chain()` 関数は、引数に反復可能なオブジェクトを期待している。
- リスト・タプル・文字列は、反復可能なオブジェクト → 引数として妥当
- 整数は、反復可能なオブジェクトではない → 引数にするとエラー発生

```ruby: test.py
mport itertools

mylist = list(itertools.chain([1, 2, 3], (4, 5, 6), '789'))
print(mylist)
```

```ruby: console
[1, 2, 3, 4, 5, 6, '7', '8', '9']
```


# その他

## 正規表現

```ruby: 正規表現の特殊文字
\d 数字
\D 数字以外
\s 空白文字
\S 空白文字以外
\w 任意の英数字
\W 英数字以外
. 任意の1文字
^ 先頭
$ 末尾
* 0文字以上の繰り返し
+ 1文字以上の繰り返し
? 0回か1回の繰り返し
{m} m回の繰り返し
{m, n} m回以上、n回以下の繰り返し
[...] 指定したいずれかの文字
[^...] 指定したいずれかの文字以外
(x|y) xかyの選択
```