Python関数（備忘録）

Pythonの関数周りの備忘録です。
随時追加・修正を行っていきます。

## 引数
```python
# 引数なし
def say():
    print('hello')

say() # hello

# 引数あり
def sub(a, b):
    return a - b

result = add(3, 2) # 1

# 引数と返り値の型宣言
def add(a: int, b: int) -> int:
    return a + b

result = add(2, 3)
print(result) # 5
result = add('AAA', 'BBB')
print(result) # AAABBB（型宣言をしても実行は出来るのでこうなってしまう）

# 引数の指定の仕方
def dinner(main, side, dessert='pudding'):
    print('main =', main)
    print('side =', side)
    print('dessert =', dessert)

# 位置引数
dinner('beef', 'salad', 'ice') # dinner関数が受け取る引数の順番の通り渡す

# キーワード引数
dinner(dessert='cake', main='fish', side='soup') # dinner関数が受け取る引数の名前を指定して渡す

# 位置引数とキーワード引数を混ぜて使う
dinner('fish', dessert='cake', side='soup') # 順序に注意

# デフォルト引数
dinner('beef', 'salad') # dessertを指定していないのでデフォルト値のpudding

# リストや辞書は参照渡しなのでデフォルト引数に空のリストは指定しない
def append(x, list=None)
    if list is None:
        list = []
    list.append(x)
    return list

# 位置引数のタプル化
def cooking(taste, **args):
    print('taste = ', taste)
    print(args) # ('beef', 'pork')

cooking('salt', 'beef', 'pork')

# キーワード引数の辞書化
def cooking(taste, **args, **kwargs):
    print('taste = ', taste)
    print(args) # ('beef', 'pork')
    print(kwargs) # {drink='juice', dessert='ice'}

cooking('soy sauce', 'beef', 'pork', drink='juice', dessert='ice'):
```

## Docstrings
```python
def sub(a, b):
    """関数subに関する説明

    Args:
        param1 (int): XXXXXXXXXX
        param2 (int): XXXXXXXXXX

    Returns:
        int: XXXXXXXX
    
    """
    return a - b

# 以下の方法でDocstringsに記述した関数のhelpを参照出来る
help(sub)
sub.__doc__
```

## 関数内関数
```python
def outer_func(a, b):
    # 他の関数から呼ばれない関数としてouter_func内で関数を定義
    def inner_func(c, d):
        return 2 * c + d

    result1 = inner_func(a, b) # 2*2+3=7
    result2 = inner_func(b, a) # 2*3+2=8

outer_func(2, 3) # 7+8=15
```

# クロージャー
```python
def circle_area(pi):
    def calc(radius):
        return pi * radius * radius

    return calc

circle_area1 = circle_area(3.14)
circle_area2 = circle_area(3)

print('計算結果')
# 引数を渡した関数を定義しておいて後から実行できる
print(circle_area1(10)) # 314.0
print(circle_area2(10)) # 300
```

# デコレーター
```python
# メインの処理の前に====BEFORE====を出力する機能を持つデコレーター
def print_before(func):
    def wrapper(*args, **kwargs):
        print('====BEFORE====')
        result = func(*args, **kwargs)
        return result
    return wrapper

# メインの処理の後に====AFTER====を出力する機能を持つデコレーター
def print_after(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print('====AFTER====')
        return result
    return wrapper

@print_before # デコーレーターの指定
@print_after  # 上から順番に実行される
def say():
    print('hello')　# ====BEFORE==== hello ====AFTER====
```

# ラムダ
```python
weeks = ['Mon', 'TUE', 'wed', 'Thu', 'fri', 'SAT', 'SUN']

def conversion(words, func):
    for word in words:
        print(func(word))

# weeks内のwordを1つずつcapitalizeした文字列を順に出力する
conversion(weeks, lambda word: word.capitalize())

# weeks内のwordを1つずつlowerした文字列を順に出力する
conversion(weeks, lambda word: word.lower())

# weeks内のwordを1つずつupperした文字列を順に出力する
conversion(weeks, lambda word: word.upper())
```

# ジェネレーター
```python
def meal():
    # 朝食の前に必要な処理を書ける
    yield 'Breakfast'
    # 昼食の前に必要な処理を書ける
    yield 'Lunch'
    # 夕食の前に必要な処理を書ける
    yield 'Dinner'

m = meal()

# next(m)が実行される度にyieldが順番に返却される
print('Wake UP')
print(next(m)) # Breakfast
print('Working')
print(next(m)) # Lunch
print('Working')
print('Go home')
print(next(m)) # Dinner
print('Good night')
```