Pythonの制御構文、関数（Python学習メモ②）

今回は制御構文と関数についての学習メモです。


## if文
ポイント

* swhichはない(変わりにif, elif, elseを使用)

```python
# こんな感じで入力待ちにできる
x = int(input("整数を入れてください: "))

if x < 0:
    x = 0
    print ("負数はゼロとする")
elif x == 0:
    print("ゼロ")
elif x == 1:
    print("ひとつ")
else:
    print("もっと")
```

## for文
ポイント

* あらゆるシーケンス(リストや文字列)のアイテムに対し、そのシーケンス内の順序で反復を×ことができる
* 反復中のシーケンスを改変する必要があるときは、コピーを取って反復をかけることが推奨される。これにはスライス表記を使うのが便利。

```python

# 文字列の長さを測る
words = ['cat', 'window', 'defenstrate']
for w in words:
    print(w, len(w))

# 出力
# cat 3
# window 6
# defenstrate 11

# リスト全体のスライスコピーにループをかける
words = ['cat', 'window', 'defenstrate']
# リストのシャローコピーを取る
for w in words[:]:
    if len(w) > 6:
        words.insert(0, w)
print(words)

# 出力
# ['defenstrate', 'cat', 'window', 'defenstrate']
```

## range()関数
ポイント

* 数字の連なりに反復をかけるときは便利
* 与えられた終端地は入らない
* `range(10)`が生成する10個の値は、ちょうど長さ10のシーケンスの各アイテムへのインデックスとなる(シーケンスのインデックス反復をかけることが可能)
* とはいえ多くの場合は`enumerate()`関数を使うのが便利
* rangeが返却するオブジェクトはリストのように振る舞うが、リストではない(`iterable`なオブジェクト)
* 本当にはリストを使わないことでリソースを節約する

```python

# rangeによる反復
for i in range(5):
    print(i)

# 出力
# 0
# 1
# 2
# 3
# 4

# シーケンスのインデックスを用いた反復
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
    print(i, a[i])

# 出力
# 0 Mary
# 1 had
# 2 a
# 3 little
# 4 lamb
```

## break文とcontinue文、ループにおけるelse節
ポイント

* break文はC言語同様、forまたはwhileのループを抜けるもの
* continue文もC言語同様、ループの残りを飛ばして、次回の反復に行く
* forまたはwhileにも、else節を指定することができる
* forの場合はリストを使い果たしたときにelseが呼ばれる
* whileの場合は条件式に合致しないときにelseが呼ばれる


```python

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
        # このelseはforにかかっている
        # ループで約数を見つけられなかった場合
        print(n, 'is a prime number')

# 出力
# 2 is a prime number
# 3 is a prime number
# 4 equals 2 * 2
# 5 is a prime number
# 6 equals 2 * 3
# 7 is a prime number
# 8 equals 2 * 4
# 9 equals 3 * 3
```

## pass文
ポイント

* pass文は何もしない
* 構文的には文が必要なのに、プログラム的には何もする必要がないときに使う
* 最小のクラスを生成するのにもよく使われる
* 新しくコードを書いているとき、関数や条件の本体にプレースホルダとして置いておき、もっと抽象的なレベルについて考えることを助ける、という使い方もある


```python
while True:
    pass #何もしない Ctrl+Cで終了されるのを待つ

# 最小のクラスを生成
class MyEmptyClass
    pass

# 関数の定義
def initLog(*args):
    pass # 実装を行ったら消す


```

## 関数の定義
ポイント

* 関数本体の最初の文には文字列リテラルを使うことができる(これが関数のドキュメンテーション文字列になる)。これは善い行いなので習慣にすること。
* docstringを書いておくとドキュメントを自動生成できたりして便利
* 関数内ではグローバル変数は基本的に参照しかできない
* 値を直接代入することは(`global`文で指定した場合は別として)できない
* 関数をコールするときに渡される実引数は、コールの時点でその関数のローカルなシンボル表に加えられる
* オブジェクトの場合は、オブジェクトの参照が渡される
* return文を持たない関数は`None`を返す

```python
def fib(n):
    """nまでのフィボナッチ級数を表示する"""
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

fib(2000)

# 出力
# 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
```

## さらに関数定義について
### 引数のデフォルト値

ポイント
* 引数にはデフォルト値を定義可能
* デフォルト値の評価は、関数を定義した時点で、定義を書いたスコープで行われる（例①）
* デフォルト値の評価は一度しか起きないことに注意する
* デフォルト値がリストやディクショナリなどの可変オブジェクトの場合、これが影響する（例②）


```python
# 例①
i = 5

def f(arg=i):
    print(arg)

i = 6
f()

# 出力
# 5

# 例②
def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

# 出力
# [1]
# [1, 2]
# [1, 2, 3]

```

### キーワード引数
ポイント

* 関数はキーワード引数もとれる
* 関数をコールするときは必ず位置引数が先でキーワード引数をあとにしなければならない
* キーワード引数はすべて関数定義の仮引数に書いたものと一致している必要があるが、その順序は問われない
* 引数は値を1度しか取れない(引数指定の重複は無効)

```python
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ');

# 次のいずれの形でもコールできる
parrot(1000)                                         # 位置引数1個
parrot(voltage=1000)                                 # キーワード引数1個
parrot(voltage=100000000, action='VOOOOOM')          # キーワード引数2個
parrot(action='VOOOOOOOM', voltage=1000000)          # キーワード引数2個
parrot('a million', 'bereft of life', 'jump')        # 位置引数3個
parrot('a tousand', state='pushing up the daisies')  # 位置引数1個キーワード引数1個

# 次のコールは無効
# parrot()                     # 必要な引数がない
# parrot(voltage=5.0, 'dead')  # キーワード引数の後に非キーワード引数
# parrot(110, voltage=220)     # 同じ引数に値を2度与えた
# parrot(actor='John Cleese')  # 未知のキーワード引数
```

* 仮引数の最後が`**名前`の形になっていると、この引数はディクショナリを受け取る
* このディクショナリには、仮引数に対応するキーワードを除いたすべてのキーワード引数が入っている(つまり、仮引数にないキーワードも使える)
* また、`*名前`の形式と組み合わせることもできる
* 下記の例では、keywordsディク書なりの中身を表示する際にkey()メソッドの結果をそーとしている。これによって引数の表示順序を固定している

```python
def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
        print("-" * 40)
        keys = sorted(keywords.keys())
        for kw in keys:
            print(kw, ":", keywords[kw])
            
cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

# 出力
# -- Do you have any Limburger ?
# -- I'm sorry, we're all out of Limburger
# It's very runny, sir.
# ----------------------------------------
# client : John Cleese
# shopkeeper : Michael Palin
# sketch : Cheese Shop Sketch
# It's really very, VERY runny, sir.
# ----------------------------------------
# client : John Cleese
# shopkeeper : Michael Palin
# sketch : Cheese Shop Sketch
```

### 任意引数のリスト
ポイント

* 可変個数の引数を受け取るようにできる
* 引数はタプルにまとめられる
* 可変個数の引数より前の部分には通常の引数を置くことができる
* 可変個数の引数は仮引数リストの最後に置く(関数に渡される引数の残りすべてを吸い込んでしまうため)
* `*args`形式の場合、これより後ろの仮引数はすべてキーワード引数としてしか使用できず、位置引数としては利用できない

```python
def concat(*args, sep="/"):
    return sep.join(args)

print(concat("earth", "mars", "venus"))

# 出力
# earth/mars/venus

print(concat("earth", "mars", "venus", sep="."))

# 出力
# earth.mars.venus

```

### 引数リストのアンパック
ポイント

* 引数にしたいものがすでにリストやタプルになっていて、位置指定型引数を要求する関数のためにアンパックしなくてはいけないという状況で便利な方法がある
* range関数と`*args`形式を使うと簡単にリストやタプルをアンパックできる

```python
>>> list(range(3, 6))
[3, 4, 5]

>>> args = [3, 6]
>>> list(range(*args))
[3, 4, 5]
```

* 同じように`**`演算子を使う事でディクショナリもキーワード引数として渡すことができる

```python
>>> def parrot(voltage, state='a stiff', action='voom'):
...     print("-- This parrot wouldn't", action, end=' ')
...     print("if you put", voltage, "volts through it.", end=' ')
...     print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

```

### lambda式
ポイント

* キーワード`lambda`を使うと小さな無名関数が書ける
* この形式では単一の式しか持てないという構文上の制限がある

```python
def make_incrementor(n):
    return lambda x: x + n # 無名関数を返す

f = make_incrementor(42)

f(0) # 42
f(1) # 43

```

### ドキュメンテーション文字列(docstring)
ポイント

* 1行目はいつでも常にオブジェクトの目的の短く簡潔な要約とすべき
* この行は大文字で始まり、ピリオドで終わること
* 続きがある場合は、ドキュメンテーション文字列の2行目を空行とし、要約と他の記述を視覚的に分離すべき
* 移行の行では段落を使い、そのおぶじゅえくとのコールの仕方や副作用などを記述するべき
* Pythonのパーサは複数行からなる文字列リテラルのインデントを除去しないので、除去したい場合はドキュメントのツールで行う

```python
def my_function():
    """Do nothing, but document it.
    
    No, really, it doesn't do anything.
    """
    pass

print(my_function.__doc__)

# 出力
# Do nothing, but document it.
# 
#     No, really, it doesn't do anything.
```

### 関数注釈(関数アノテーション)
ポイント

* 型のメタデータ情報を付与することができる
* 関数の`__annotations__`属性にディクショナリとして格納され、関数のほかの部分にはいかなる影響も与えない
* 引数の注釈は引数名のあとにコロン、式と続けることで定義できる
* 返り値の注釈はdef文最後のコロンの手前、仮引数リストとの間にリテラル`->`を挟むことで定義する

```python
def f(ham: str, eggs: str = 'eggs') -> str:
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return ham + ' and ' + eggs

f('spam')

# 出力
# Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
# Arguments: spam eggs
# 'spam and eggs'
```

### コーディングスタイル
ポイント

* インデントはスペース4つ、タブは使わない
* 79文字以下で行を折り返す
* 関数やクラス、関数内の大きめのブロックを分離するのに空白行を使う
* 可能であればコメント行は独立させる
* docstringを使う
* 演算子の周囲やカンマの後ろにはスペースを入れるが、かっこのすぐ内側には入れない  
例: a = f(1, 2) + g(3, 4)
* クラスにはCamelCase、関数やメソッドにはlower_case_with_underscoresを使用する
* メソッドの第一引数としては常にselfを使うようにする
* エンコードはデフォルトのUTF-8かASCIIを使用する