Python学習の基礎・覚え書き


# はじめに
この記事は、Pythonの自己学習で学んだこと（基礎的なこと）を書き留めておく覚え書きです。
環境構築手順や、アプリケーション作成のノウハウなどについては触れていませんので、あらかじめご了承ください。
## 実行環境
* MacOS 11.6.2
* Python 3.9.7

# 基礎編
## 変数
### 変数宣言
変数名を書くだけで使用できる。キーワードは不要。
```Py
x = 100 + 123
x.bit_length() # ビット数を取得
```
```:output
8
```
### None
`None`は、何もない状態を示す。Javaで言えば`null`のようなイメージ。

## 演算
### 除算結果の小数点以下を切り捨てる
```Py
20//3
```
```:output
6
```
### べき乗
```Py
3**2
```
```:output
9
```
### 文字列の繰り返し
文字列型の値に対して乗算を行うと、その文字列を繰り返す。
```Py
msg1 = 'hello'
msg2 = '!'
print(msg1, msg2 * 10)
```
```:output
hello !!!!!!!!!!
```

## フォーマット
### 文字列を変数で置き換える
format関数を使うと、文字列の`{}`で囲んだ部分が変数に置き換わって返却される。
置き換える値はformat関数に渡した順で使用されるが、`{1}`のように位置番号を指定すると順番を変更できる。
```Py
prices = [100, 250, 800]
'お買い上げ金額は{1}点で{0}円です。'.format(prices[0] + prices[1] + prices[2], len(prices))
```
```
'お買い上げ金額は3点で1150円です。'
```

### 書式を指定したフォーマット
```Py
# 本来ならば定数としたいが、Pythonには該当する仕組みがない
bmi = 22
height = float(input('身長を入力(cm) > ')) # ユーザーの入力を、float型に変換して代入
weight = bmi * (height / 100) ** 2

# endを指定することで改行を抑止できる
print('身長が{}cmの場合の標準体重は'.format(height), end = '')
# 小数点以下第2位まで表示する
print('{:.2f}kgです。'.format(weight))
```
```:output
身長を入力(cm) > 151
身長が151.0cmの場合の標準体重は50.16kgです。
```

### 書式化演算子
文字列の後の`%`は、書式化演算子を意味する。ただし、format関数で代替可能。
```Py
name = input('汝の名は？> ')
print('%s よ、よく来たな。' % name)
```
```:output
汝の名は？> 勇者
勇者よ、よく来たな。
```

## タプル
### タプル型の定義
タプルは順序づけられた複数の値を格納するために使用し、一度設定した値は変更できない（immutable＝不変）。
タプルは、カンマ区切りの値を丸括弧で囲むことで定義する。
```Py
a = (1,) # 1要素の場合、末尾にカンマが必要
b = (1,2,3)
print(a)
print(b)
```
```:output
(1,)
(1, 2, 3)
```

## リスト
### リストの定義
カンマ区切りの値を角括弧で囲むとリストが定義できる。タプルとは異なり、設定した値は後からでも変更可能。
```Py
a = [1, 2, 3]
print(a)
```
```:output
[1, 2, 3]
```
### リストのコピー
元のリストの変更の影響を受けないようにするためには、単なる代入ではなくリストをコピーする。
下記の例では、リスト`a`の内容を変更しても、`b`, `c`, `d`の内容は変わらないことが確認できる。
```Py
a = [1, 2, 3]
b = a.copy()
c = list(a)
d = a[:]
a[1] = 'hello'

print(a, b, c, d, sep='\n')
```
```:output
[1, 'hello', 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
```

### 初期化
`range`関数の結果など、イテラブルなオブジェクトから、リストの初期値を設定できる。
```Py
numList = list(range(1, 6))
print(numList)
```
```:output
[1, 2, 3, 4, 5]
```
### リスト内包表記
イテラブルなオブジェクトを評価した結果から、リストを生成できる。
```Py
list1 = [num for num in range(1, 6)] # そのままリスト化
list2 = [num -1 for num in range(1, 6)] # 取り出した値を減らす
list3 = [num for num in range(1, 6) if num % 2 == 1] # 条件を満たす値だけを追加
print(list1, list2, list3, sep='\n')
```
```:output
[1, 2, 3, 4, 5]
[0, 1, 2, 3, 4]
[1, 3, 5]
```

### zip関数による複数リストからの要素取り出し
zip関数を使うと、複数のリストから値のペアを作成できる（各要素はタプル型となる）。
```Py
# 2つのリストから値のペアを作る
eto = ['酉', '戌', '亥']
animal = ['とり', 'いぬ', 'いのしし']

list(zip(eto, animal))
```
```:output
[('酉', 'とり'), ('戌', 'いぬ'), ('亥', 'いのしし')]
```

3つのリストから組を生成する場合の例
```Py
year = ['H29', 'H30', 'H31']
eto = ['酉', '戌', '亥']
animal = ['とり', 'いぬ', 'いのしし']

# zip関数で、複数のシーケンス要素を集めたタプル型のイテレータを作成する
for year, eto, animal in zip(year, eto, animal):
    print(year, ':', eto, '({})'.format(animal))
```
```:output
H29 : 酉 (とり)
H30 : 戌 (いぬ)
H31 : 亥 (いのしし)
```

## 辞書
### 辞書型の定義
カンマ区切りの値を波括弧で囲むと、辞書型の変数(連想配列)が定義できる。
```Py
dic = { 'key1': 'value1', 'key2': 'value2' }
print(dic['key2'])
```
```:output
value2
```

### 関数で辞書を作成する
`dict`関数にリストを渡すと、リストの各要素を項目として持つ辞書を生成できる。
```Py
seq1 = dict([['a', 'b'], ['c', 'd'], ['e', 'f']])
seq2 = dict(['ab', 'cd', 'ef']) # 渡す文字が2文字以外だとエラーになる
seq3 = dict(('ab', 'cd', 'ef')) # タプルを渡しても結果は同じになる

print(seq1, seq2, seq3)
```
```:output
{'a': 'b', 'c': 'd', 'e': 'f'} {'a': 'b', 'c': 'd', 'e': 'f'} {'a': 'b', 'c': 'd', 'e': 'f'}
```

### 辞書型要素のイテレーションアクセス
for inで辞書の各要素に対して繰り返し処理ができる。
```Py
# 辞書の定義
spells = { 'ホイミ': 3, 'ルーラ': 8, 'ギラ': 2 }

# キーのみの取り出し
for key in spells:
    print(key, sep=', ')
# 値のみの取り出し
for value in spells.values():
    print(value, sep=', ')
# 要素の取り出し(各要素はタプルとなる)
for item in spells.items():
    print(item)
```
```:output
ホイミ
ルーラ
ギラ
3
8
2
('ホイミ', 3)
('ルーラ', 8)
('ギラ', 2)
```

### リスト内包表記
辞書でも、リスト内包表記を使って初期化を行うことができる。
```Py
eto = ['酉', '戌', '亥']
animal = ['とり', 'いぬ', 'いのしし']

# リスト内包表記で辞書を作成
result = {i: j for (i, j) in zip(eto, animal)}
result
```
```:output
{'酉': 'とり', '戌': 'いぬ', '亥': 'いのしし'}
```

## 集合
### 集合の定義
重複を許容しない要素の組み合わせを集合という。
`set`関数もしくは、波括弧`{}`で囲むことで作成できる。

```Py
# 集合を作る
month = {'1月', '2月', '3月', '4月', '5月'}
print(month)

print(set('Python'))

print(set(['Python', 'App']))

print(set( ('Python', 'App') ))

print(set( {'酉': 'とり', '戌': 'いぬ'} ))

```
```:output
{'4月', '2月', '5月', '1月', '3月'}
{'t', 'h', 'n', 'o', 'y', 'P'}
{'Python', 'App'}
{'Python', 'App'}
{'酉', '戌'}
```

### リストの重複要素削除
一度集合に変換することで、リストの重複要素を削除できる。
```Py
data = ['スカッシュ', 'バレー', 'ポロ', 'スカッシュ', 'バレー']

# リストから重複を排除した集合を作成
dataSet = set(data)
print(dataSet)
# 集合をリスト化
print(list(dataSet))
```
```:output
{'バレー', 'ポロ', 'スカッシュ'}
['バレー', 'ポロ', 'スカッシュ']
```

### 集合同士の演算
それぞれの集合で重複した要素のみの抽出など、演算を行うこともできる。
```Py
data1 = {'スカッシュ', 'バレー', 'ポロ'}
data2 = {'スカッシュ', 'バレー', 'セパタクロー'}
data3 = {'スカッシュ', 'バレー', 'キックベース'}

# data1の重複していない要素のみを取り出す
print(data1 - data2)
# data1とdata2で重複する要素のみを取り出す
print(data1 & data2)
# 重複要素を取り除いた集合を作成する
print(data1.union(data2, data3))
# 全てに共通する要素で集合を作成する
print(data1.intersection(data2, data3))
```
```:output
{'ポロ'}
{'バレー', 'スカッシュ'}
{'キックベース', 'バレー', 'ポロ', 'セパタクロー', 'スカッシュ'}
{'バレー', 'スカッシュ'}
```

# 関数編
## 関数の定義
`def`キーワードで関数を定義する。
Javaのような括弧は無いため、インデントされた行が関数の本体部分となる。
```Py
def calcTax(price):
    return price * 1.08

result = calcTax(12)
print('うまい棒は税込み{}円です。'.format(result))
```
```:output
うまい棒は税込み12.96円です。
```
## 引数
### 引数の指定方法
位置引数とキーワード引数の2通りがある。
「仮引数名=値」の形式で引数のデフォルト値を設定することもできる。
通常の引数は省略すると実行時にエラーとなるが、デフォルト値のある引数は省略してもエラーとならない。
```Py
def calcTax2(price, rate = 10):
    return price + int(price * rate / 100)

# 位置指定の引数で関数呼び出し
print(calcTax2(100, 8))
# パラメータ名指定で関数呼び出し
print(calcTax2(rate = 5, price = 200))
# 一部パラメータ名指定で関数呼び出し
print(calcTax2(500, rate = 8))
# デフォルトパラメータの適用
print(calcTax2(2500))
```
```:output
108
210
540
2750
```
### リストをデフォルト値として使う
関数のデフォルトパラメータは再使用されるため、`def appendTest(arg=[])`としてはならない。
（複数回呼び出された時に予期せぬ結果となる可能性がある）
下記の例のように、関数内部でリストを初期化するようにすれば問題は発生しない。
```Py
def appendTest(arg=None):
    msgList = []
    if arg and isinstance(arg, list):
        # 引数がリスト型ならば、msgListにコピーしてセット
        msgList = arg.copy()
    msgList.append('Hello!')
    return msgList

print(appendTest())
print(appendTest(['hogehoge']))
print(appendTest())
print(appendTest(3)) # 数値型なので無視される
```
```:output
['Hello!']
['hogehoge', 'Hello!']
['Hello!']
['Hello!']
```

### 可変長パラメータ
仮引数名の前に`*`を付けると、その引数は可変長となる。
```Py
def funcTest(level, *messages):
    print(messages)
    for msg in(messages):
        print(level, msg)

funcTest('INFO', 'あいうえお', 'かきくけこ', 'さしすせそ')
funcTest('DEBUG', 'たちつてと')
```
```:output
('あいうえお', 'かきくけこ', 'さしすせそ')
INFO あいうえお
INFO かきくけこ
INFO さしすせそ
('たちつてと',)
DEBUG たちつてと
```

### 辞書型パラメータ
仮引数名の前に`**`を付けると、その引数は辞書型となる。
可変長パラメータと同時に使用する場合は、可変長パラメータの後に定義する。
```Py
def funcTest2(date, level, *messages, **keywords):
    print(date, level, list(messages), keywords)

funcTest2('2021/03/01', 'INFO', 'あいうえお', 'かきくけこ', key1='値1', key2='値2')
```
```:output
2021/03/01 INFO ['あいうえお', 'かきくけこ'] {'key1': '値1', 'key2': '値2'}
```

## 様々な関数
### 高階関数
JavaScriptなどと同様、引数で関数を受け取り、実行することができる。
```Py
def printText(text1, text2):
    print(text1, '-->', text2)

def execFunction(func, arg1, arg2):
    func(arg1, arg2)

execFunction(printText, 'hogehoge', 'piyopiyo')
```
```:output
hogehoge --> piyopiyo
```

### 関数内関数
関数の中で関数を定義し、呼び出すこともできる。
```Py
def outerFunc(a, b):
    def innerFunc(c, d):
        return c + d
    return innerFunc(a, b)

outerFunc(1, 5)
```
```:output
6
```

### ラムダ式
処理内容が少ない関数は、`lambda`キーワードを使うことで、記述をシンプルにできる。
```Py
def printNumbers(numbers, func):
    for num in numbers:
        print(func(num))

prices = [100, 210, 317]

printNumbers(prices, lambda val: '{}円'.format(val))
```
```:output
100円
210円
317円
```

### ジェネレーター関数
`return`ではなく`yield`で値を返す関数は、`next`や反復処理で値を順番に取り出せる。

```Py
def generate(str):
    for s in str:
        yield '「{}」'.format(s)

gen = generate('カールおじさん')
gen2 = generate('！？')
# 反復処理での取り出し
for s in gen:
    print(s)
# next関数での取り出し
print(next(gen2))
```
```:output
「カ」
「ー」
「ル」
「お」
「じ」
「さ」
「ん」
「！」
```

### デコレーター
デコレーターを使うと、関数に追加処理を付加できる。
```Py
# 引数が必要な場合は、関数を返す関数が入れ子構造となる
def deco(mark):
    # 関数をラップするデコレーター
    def _deco(func):
        def newFunc():
            print(mark + 'function calles:' + func.__name__)
            return func()
        return newFunc
    return _deco

# デコレーターを使用
@deco('★')
def hello():
    return 'こんにちは！'

print(hello())
```
```:output
★function calles:hello
こんにちは！
```

# おわりに(所感)
使い慣れたJavaScriptなどとの違い（下記）に、はじめは少々戸惑いましたが、比較的シンプルで使いやすい言語であると思いました。
* インデントの深さで関数のブロックが決まる
* キーワード無しで変数宣言が出来てしまう
* ダブルアンダーバーに囲まれた特殊変数（`__name__`など）がたくさんある