pythonチュートリアル #2 -無知に気づく-

※以下の企画です

https://qiita.com/tatataichi/private/56936a028e625e62af8e


Pythonチュートリアルの二回目投稿です。
一回目の段階でもそこそこ学びがあったので、今回も期待値が高まっています。
今回は4章をやっていきます。

# 4章 学習内容まとめ
4章は「**制御構造**」についての章。for文とか、関数の定義とか。
自分はバックグラウンドとして、C言語に触れている期間が長かった（と言っても、趣味でマイコンとか触ってただけ…）ので、ここらへんの細かい文法は泥臭くなりがち。
しっかり学んでパイソニック（というらしい）なコードを書けるようにしたい！

## for文
他の言語（特にC言語とか）からPythonに移住して最初に戸惑うのがfor文の文化の違い。
C言語などでは等差級数のループ（iが10になるまで++的な）で繰り返すが、Pythonはリストの要素に対して反復実行を行う。

``` python:for文
>> words = ['cat','dog','food','enjoy']
>> print(words)
>> for w in words:
>>   print(w,len(w))

['cat', 'dog', 'food', 'enjoy']
cat 3
dog 3
food 4
enjoy 5
```

僕は上記の書き方だと慣れなさすぎて、以下の用にrange関数を用いて結局いつもの書き方をしてしまっている。
良いか悪いかはおいておいて、個人的にパイソニックなのは上記な気がする。

``` python:range関数
>> for i in range(len(words)):
>>   print(words[i],len(words[i]))

cat 3
dog 3
food 4
enjoy 5
```


## 反復可能体（iterable）
**イテラブル**という言葉はよく聞いたことがあり、雰囲気で理解していた（理解していない）つもりだった。
本書で改めて説明もあったので、ちゃんと理解できるようにChatGPTにも聞いてみた。
### 反復可能体（iterable）とは by GPT4o
>「反復可能体」は、**何度も繰り返して同じ結果が得られる仕組みやプロセスのこと**です。
たとえば、実験やプログラムの結果が一貫して同じになることを指し、科学や技術で成果の信頼性を確認するために重要です。再現性があることで、その結果が偶然でなく、正しい手順に基づいたものであると証明できます。

うーん。微妙にわかるようなわからないような…

と、悩んでいるところに出会ったこの記事がとにかくわかりやすかった…！ありがとうございます。
https://qiita.com/bkh4149/items/fa7c80e4d7077aa609c1

イテラブルが弾丸で、イテレータが詰め込み済みの銃。イテレータは弾丸（イテラブル）を放つたびに無くなっていく。
Pythonチュートリアルの文中に**メモリ効率が良い**とかって書いてたのが腑に落ちた。

## ループにおけるelse文
とりあえず以下のコードをみてもらいたくて…

```python:ループのelse
>>for n in range(2,10):
>>  for x in range(2,n):
>>    if n % x == 0:
>>      print(n,"equals",x,'*',n//x)
>>      break
>>  else:
>>    #ループで約数を見つけられなかったとき
>>    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
```
※書籍内のコードをそのまま写経

2から10までの数字の中で素数を見つけるコードになっているが、変なところにelseがある。
インデントを見ると`if - else`の組み合わせではなく`for - else`になっている。
これで良いらしい。
pythonの場合は、forに対してelseをつけると**try文**的な動作を実現できるらしい。
だったら`try文`で良くねって思ってしまうが、人のコードを呼んだときに出てくる可能性もあるので一応覚えておく。

## 関数のデフォルト値
関数の引数にはあらかじめ値を代入しておける。
これは関数の呼び出し時に引数指定を柔軟にできるという強力な機能だと思う。

```python:関数のデフォルト値
>> def ask_ok(prompt, retries=4, reminder="Please try again!"):
>>   while True:
>>     ok = input(prompt)
>>     if ok in ('y','ye','yes'):
>>       return True
>>     if ok in ('n','no','nop','nope'):
>>       return False
>>     retries = retries - 1
>>     if retries < 0:
>>       raise ValueError('Invalid user response')
>>     print(reminder)
```

``` python:作った関数の実行
>> ask_ok("do you really want to quit? ")

do you really want to quit? yes
True

>> ask_ok("do you really want to quit? ")

do you really want to quit? no
False

>> ask_ok("do you really want to quit? ")

do you really want to quit? 1
Please try again!
do you really want to quit? 2
Please try again!
do you really want to quit? 3
Please try again!
do you really want to quit? 4
Please try again!
do you really want to quit? 5
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-53a9d314ec75> in <cell line: 1>()
----> 1 ask_ok("do you really want to quit? ")

<ipython-input-1-b676fcc9b0c9> in ask_ok(prompt, retries, reminder)
      8     retries = retries - 1
      9     if retries < 0:
---> 10       raise ValueError('Invalid user response')
     11     print(reminder)

ValueError: Invalid user response

```

ここには結果を記載しないが、retriesに値を入れたり、reminderの値を変えたり出来る。
これは本当に汎用的な関数の作成に使えそう。
あとは地味に`in`も便利だと思った。文字列の探索は正規表現でどうちゃらこうちゃらとかして毎回違う処理をしてしまっている気がするので、`in`も使いこなしていきたい。

## キーワード引数
関数の引数指定には`*名前`と`**名前`という指定の方法もある。
これはdjangoでviewsを書いているときに、**何も考えずにおまじない的に記述していた**（本当にやばい奴で申し訳ない）。

``` python:例えばこれ
class DownloadSelectedImagesView(View):
    def post(self, request, *args, **kwargs):
```

正直どんな役割なのかわかっていなかったが、本書で学び直して少し理解できた気がする。

### `*名前`：可変位置引数
複数の引数をタプル（一次元配列的な）として受け取れる。

```python:**名前
>> def func(*args):
>>    print(args)

>>func(1, 2, 3)

(1, 2, 3)

```
### `**名前`：可変キーワード引数
複数のキーワード引数を1つの辞書として受け取れる。

```python:**名前
>> def func(**kwargs):
>>    print(kwargs)

>> func(a=1, b=2)

{'a': 1, 'b': 2}
```
ルール的には、`*名前`を先に指定して、`**名前`は後に指定するとのこと。
djangoのpostメソッドなどで`*args`とか`*kwargs*`が指定されがちなのは、いろいろ追加で送られてきても対応できるようにしているから…らしい（gpt談）

## ドキュメンテーション文字列
関数の中によく以下のような記述を見かける(gpt君もよく出力してくる)。
これ、ただの親切なコメントアウトなのかと思っていたが、想像よりももっと親切だった。

``` python:よくみるやつ
>> def nothing_to_do():
>>   """
>>   何もしない関数
>>   """
>>   pass

>> nothig_to_do()

```
上記だと、もちろん実行しても何も出ない。
しかし、以下のよに呼び出すと…

``` python:よくみるやつ。すごい親切

>> nothing_to_do.__doc__

  何もしない関数
  
```

出力される！
これめちゃくちゃ親切！世のため人のためにちゃんと設計しよう…

# まとめ
今回はとにかく学びがあった。
というか何も知らなすぎた。パイソニックな書き方はもちろん、`__doc__`とかイテレータとか、キーワード引数など…
ちゃんとこうやって勉強しなければ、一生ふわふわしたままだったと思う。
自分の無知に気づけただけでも本当に収穫だと思った。…ちゃんとこの書籍をやり切ろう！