初学者向け Python 事始め

## 【本記事の趣旨】
[Python](https://ja.wikipedia.org/wiki/Python) をこれから始める人が、この記事を読んだらとりあえず何か役に立つかも。
Python を既に知っている人でも、何か得られるものがあるかも。
そんな記事です。

何番煎じなのかわからないくらいに、ベタベタに手あかがついたトピックの記事ではあります。
こういうの散々読んだし知ってるわい。という人も多数いるかと思います。 
<font color="yellow">そういう方にはこの記事は役に立ちません、これ以上読まないほうが良い</font> です。


## 【プログラム言語未経験の方への注意】
### 私のお気持ち表明
完全に私の私見、かつ身も蓋も無い話ですが <font color="yellow"> Python はやめたほうが良い </font> です。
ですので、 <font color="yellow">これ以上この記事は読まないほうが良い</font> です。

プログラム言語未経験の方は Python というか [スクリプト言語](https://ja.wikipedia.org/wiki/スクリプト言語) 全般を避けたほうが良いです。
まずは [コンパイル](https://ja.wikipedia.org/wiki/コンパイラ) する言語から使い始めてみるのを勧めます。

ただ「機械学習で使いたいからどうしても Python 」という人もいるかとは思います。
そういう目的がある人は、 Python から始めるでも良いとは思います。
### Python を避けたほうが良い理由
「プログラム書いて、コンパイルして、文法エラー直して、動かして、デバッグして」の一連の作業を繰り返す。
というのが、速成のコツだからです。
**「コンパイルして、文法エラー直して」** の部分がとても大事なんですが、Python だとこれが無いんですね。
最初はコンパイラ様に怒られながら学んだほうが良いかと思いますよ。
### [蛇足] 初学者が始める言語は何が良い？
[Windows](https://ja.wikipedia.org/wiki/Microsoft_Windows) を使っているのなら [C#](https://ja.wikipedia.org/wiki/C_Sharp) が良きかと思います。 
[MAC](https://ja.wikipedia.org/wiki/MacOS) の人は、、、どうしよう。 [Objective-C](https://ja.wikipedia.org/wiki/Objective-C) なのかな。いきなり [Swift](https://ja.wikipedia.org/wiki/Swift_(プログラミング言語)) から初めていいものか。。。
[Linux](https://ja.wikipedia.org/wiki/Linux) の人は好きにしてください。好きにできるでしょう。
スマートフォンとかタブレット端末しか持ってない人は、、、パソコン買ったほうが良いですよ。（真剣なアドバイスとしてそう思います。ほんとに。）

## ファーストステップ
### Python の実行環境は自分でなんとかしろ
これは冗談では無いです。
この程度を自力でどうにかできない人はどうにもならないので、さっさと諦めたほうが良いかと思います。

ですので、 <font color="yellow">そういう人はこれ以上この記事は読まないほうが良い</font> です。
いちおうフォローすると Windows の人は [WSL](https://ja.wikipedia.org/wiki/Windows_Subsystem_for_Linux) を使うのが楽です。
### 実行環境ができたら書いて動かしてみろ
これも詳細は割愛します。
まずは「プログラム書いて、 ~~コンパイルして、~~ 文法エラー直して、動かして、デバッグして」を、やってみることです。
プログラムをファイルに書くより、[インタラクティブシェル](https://ja.wikipedia.org/wiki/インタラクティブシェル) を使うのが手っ取り早くてお勧めです。

やってみるだけならすぐです。まずは触ってみるのがいいです。
この時点で「できなかった・わからなかった」の人は、向いてないので諦めたほうが良いです。

ですので、 <font color="yellow">そういう人はこれ以上この記事は読まないほうが良い</font> です。

### ドキュメント読め
とりあえず [Python チュートリアル](https://docs.python.org/ja/3/tutorial/) 見ろ。
とりあえず [PEP8](https://pep8-ja.readthedocs.io/ja/latest/) 見ろ。
[wtfpython](https://github.com/satwikkansal/wtfpython) の README には細かい話が書いてあるので、迷ったら見るといい。

※
最初に一から十まで読む必要は無いかと思います。必要に応じて必要な箇所をつまみ食いするだけでも十分かと。
困ったら信用に足る公式のドキュメントを検索する。そういうクセを付けておくといいでしょう。

### 資格試験を受験してみるのもいい（そんなに難しくない）
* [Python 3 エンジニア認定基礎試験](https://cbt.odyssey-com.co.jp/pythonic-exam.html)
* [Python 3 エンジニア認定実践試験](https://cbt.odyssey-com.co.jp/pythonic-exam/python3cpe.html)
試験自体は難しくないです。
[模擬試験](https://python-basic.com/) を何周かすれば取得できると思います、実際できました。
ただ、いきなり資格試験を受けるよりも「書いて動かしてみて」を繰り返したほうが良いとは思います。

## Python よもやま話
Python にまつわるあれこれが書いてあります。
Python に限らず、プログラム言語を扱う上での細かな話も混じってます。
私の心にうつりゆくよしなしごとを並べてみただけの内容なので、網羅性とか無謬性は無いのでご注意ください。

あと、Python バージョンは 3 を想定して書いてあります。
（メインバージョン3であってもサブバージョンごとの細かな動作差異はあります。ですがそれを気にした記事になってないのでその点はご注意。）
### [docstring](https://ja.wikipedia.org/wiki/ドキュメンテーション文字列) を知っておく
コメントにも作法があることを知って実践するのが良いです。
調べれば色々見つかりますが、一応 Qiita の記事へのリンクを書いておきます。
https://qiita.com/flcn-x/items/393c6f1f1e1e5abec906
### [flake8](https://flake8.pycqa.org/en/latest/) はやっておく
完全なチェックにはならないけれども、やらないよりは大分マシ。
### import * はやるな
理由は割愛。ならぬことはならぬものです。
PEP8 にもそう書いてあります。
### import の処理時間に気を付ける
import は、それなりに処理時間がかかります。
特に組み込み機器上で [CLI](https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9) での走り切りプログラムを Python で書くような人は注意です。
頭の片隅に気に留めておくといいでしょう。
### 未定義変数の参照は静的解析で１００％チェックできない（はず）
スクリプトあるある。
実際に動かしてみて問題がある行にたどり着くまで、問題があることがわからない。
特に以下のような形はとてもやりがちだが、静的解析では完全にチェックできない（はず）
```python
    if a:
        b = "TEST"
    print(b) # 上記の if 内に入らなかったらここで例外になる（未定義変数の参照）。
```
　
### function 作ったら引数・戻り値の型アノテーション書いたほうが良いらしいですよ
[参考リンク] 型アノテーションについての解説
https://zenn.dev/kazu1/articles/662816f4acaede
https://qiita.com/icoxfog417/items/c17eb042f4735b7924a3

以下は関数の引数・戻り値の型アノテーションサンプル方と実行例。
```python
$ python
>>> def test_func(string_for_p: str) -> None:
...     print("Arg1: ", string_for_p)
...
>>> ret = test_func("TestString") #　★呼び出してみる
Arg1:  TestString
>>> type(ret) # ★戻り値の型を見てみる
<class 'NoneType'>
>>> ret = test_func(1)
Arg1:  1 # ★ int 型の値を引数でもらってもエラーにはならないよ
```
正直書くのめんどくせ。「読めばわかるでしょ」と言いたくなる気持ちはある。
でも公開メソッドには書いておいたほうがいいかもね。

また、以下のようなコードは書きがちではある（ Python に限らず）
```python
$ ### 関数の引数の型をチェックしてみるサンプル
$ python
>>> def test_func2(arg1: list) -> bool:
...     if type(arg1) is not list: # ★ arg1 の型をチェックする実装
...         return False
...     print (arg1.pop(0)) # ★ list でないデータに対して pop すると例外になる
...     return True
...
>>> print("ret=", test_func2(["Hello", "World"]))
Hello
ret= True
>>> print("ret=", test_func2("Hello World"))
ret= False
```
こういう「引数・戻り値」のバリデーションをやるべき・やらないべきは宗旨論なのかなぁ？(私は答えを持ってないので何も言えません）
　
### ビルドオプションでログ出力行をサプレスできない
「リリースビルドではログ出力行を無視してビルド」という当たり前ができない。
Python はスクリプト（コンパイルしない）。なので当たり前と言えば当たり前。

もちろんフレームワークによっては、できる場合があるのかも。
フレームワーク使ってない場合は、もちろんできない。

仮に logging 使うとして、logging を呼び出す行が処理負荷になる。
ログレベルが ERROR でも DEBUG でも呼び出し行は実行するので負荷になる。
これを回避したいけれど、、、

以下は ChatGPT 君がくれた回答（カスタムログラッパーの作成）
```python
import logging

class LazyLogger:
    def __init__(self, logger):
        self.logger = logger

    def debug(self, message_func):
        if self.logger.isEnabledFor(logging.DEBUG): # ★これがミソだと言ってる
            self.logger.debug(message_func()) # ★ログレベルによっては実行されない行

# ログ設定
logging.basicConfig(level=logging.ERROR) # ★ ログレベルは ERROR に設定
logger = logging.getLogger(__name__)
lazy_logger = LazyLogger(logger)

# 負荷が高いデバッグ処理
def expensive_computation():
    return sum(i * i for i in range(10**6))

# ログ出力
lazy_logger.debug(lambda: f"Expensive result: {expensive_computation()}")
logger.error("This is an error message")
```
うーーん、
これログレベルが DEBUG だろうが ERROR だろうが  lazy_logger.debug() と logger.error() の呼び出し行は消えないから実行されるんですよね。
これ負荷低減になってるのかなぁ？

### 変数を if xxx: の記述でチェックすると楽
None, 空文字,  0,  False を判定する場合、同じ if の書き方で実装できる。（以下参照）
```python
>>> s = "String"; i = 1; b = True # ★この値
>>> if s and i and b: # ★こうすると
...   print("All OK")
... else:
...   print("Any NG")
...
All OK # ★こうなる
>>> 
>>> s = None; i = 1; b = True # ★ None を混じらせてみる
>>> if s and i and b:
...   print("All OK")
... else:
...   print("Any NG")
...
Any NG # ★こうなる
```
同じ話で if xxx == 0: みたいに値明示の if は避けられるなら避けたほうが良いと思うよ。
### if xxx is XXX の使い方に気を付ける
is は型を見るときにだけ使うといいです。値判定に使うと怒られます。
というか、バグります（以下参照）
```python
>>> n = 1000
>>> if n is 1000:
...   print("n is 1000!")
... else:
...   print("Error")
...
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
Error
```
たぶんこれくらいは静的解析でチェックできると思いますが、 is の使い方は知っておいたほうが安全です。
### 辞書型変数を dict["k"] の形で値参照すると例外が起きる可能性がある
dict.get("k") の形で値参照すれば例外は起きない（以下参照）
```python
>>> dict = {"k": "v"}
>>> dict["k"]
'v'
>>> dict["l"] # ★存在しない key を指定すると、、、
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'l'
>>> dict.get("l") # ★ get すれば例外にならない
>>>
```

### 例外設計における大罪
他サイトになりますが読んでおくといいです。
https://www.slideshare.net/slideshow/exception-design-by-contract/13477925

例外をどう処理するか？は考えどころさんだと思います。
チームで話し合っておいたほうが良い気はします。
統一方針を持つ・持たないを決めるところからになるのかな？
（私も他人に偉そうなことを言えるだけの答えを持ってないです。）

### 例外のハンドリングに困ったら except Exception を使わざるを得ない
ただし、 [例外の種類](https://docs.python.org/ja/3/library/exceptions.html#Exception) を気にせず except 区を書くなとは言われているみたい。
でも、とにかくキャッチしたいときはあるから、、、
以下サンプル。
```python
>>> try:
...   array = [True, False]
...   print(array[2]) # IndexError
... except Exception as e: # これでもキャッチできるはできる
...   print("Error: ", e, type(e))
...
Error:  list index out of range <class 'IndexError'>
```
### 呼び出しネストの下底部で起きた例外は最上部まで上げるべき？
これも宗旨論争か？
私は答えを持たないので何も言えません。

### function で return を省略したら、戻り値 None
Bash のように直前の実行行の戻り値が function の戻り値にはならない。
以下サンプル。
```python
>>> def ret_test():
...   pass
...
>>> type(ret_test())
<class 'NoneType'>
```
### 自分への戒め
書く必要のないことは書くな。
たくさん書くからたくさんバグが出る。
実装すべき要求機能に対して必要十分な適切なコード量というものが必ずある。
DRY KISS YAGNI