リーダブルコードチートシートPython版

# 第Ⅰ部　表面上の改善
## 2章　名前に情報を埋め込む
### 明確な単語を選ぶ
- 動作や目的に適した明確な名前を付ける
- 類義語をよく調べる
```py: 明確な名前付け
# Bad
def get_page():  # どこからページを取得するのか曖昧

# Good
def fetch_page():  # ネットからページを取得する場合
def download_page():
```

```py: 明確な名前付け
# Bad
def size():  # 何のサイズか分からない

# Good
def height():  
def num_nodes():
def memory_bytes():
```

```py: 類義語
send   # deliver, dispatch, announce, distribute, route
find   # search, extract, locate, recover
start  # launch, create, begin, open
make   # create, set up, build, generete, compose, add, new
```

### tmpやretvalなどの汎用的な名前を避ける
- エンティティ値や目的を表した名前を選ぶ
- `tmp`は生存期間が短く，一時的な保管が最も大切な変数にだけ使う
- イテレータが複数ある時は，インデックスにもっと明確な名前を付けるとよい
```py: 目的を表した名前
# Bad
retval += v[i] * v[i]  # 2乗した値の合計を保存しておく変数

# Good
sum_squares += v[i] * v[i]
```

```py: 一時的な保管
# Good
tmp = right
right = left
left = tmp
```

```py: 一時的な保管が目的ではない
# Bad
tmp = user.name()
tmp += " " + user.phone_number()

# Good
user_info = user.name()
user_info += " " + user.phone_number()
```

```py: インデックスに名前を付ける
# Bad
for i in range(clubs.size()):
  for j in range(members.size()):

# Good
for club_i in range(clubs.size()):
  for member_i in range(members.size()):
  
for ci in range(clubs.size()):
  for mi in range(members.size()):
```

### 抽象的な名前よりも具体的な名前を使う
```py
# Bad
server_can_start():  # TCP/IPポートをサーバがリッスンできるかどうかを確認するメソッド

# Good
can_listen_on_port():
```

### 変数名に情報を追加する
- 変数名に単位を入れる
- 危険や注意を喚起する情報を追加する
```py: 変数名に単位を入れる
import time
# Bad
start = time.time()
# 計測したい処理
elapsed = time.time() - start
print("処理時間:", elapsed, "秒")

# Good
start_sec = time.time()
# 計測したい処理
elapsed_sec = time.time() - start_sec
print("処理時間:", elapsed_sec, "秒")
```

```py: 危険や注意を喚起する情報を追加する
# Bad
password  # プレインテキストであるため，処理前に暗号化すべき
comment   # ユーザが入力したcommentは表示する前にエスケープする必要あり
html      # htmlの文字コードをUTF-8に変えた
data      # 入力されたdataをURLエンコードした

# Good
plaintext_password
unesvaped_comment
html_utf8
data_ulrenc
```

### 名前の長さを決める
- スコープが小さければ短い名前でもいい
- プロジェクト固有の省略形はダメ
- 不要な単語を排除する
```py: スコープが小さい
# Good
if debug:
  m = {}
  LookUpNamesNumbers(m)
  print(m)
```

```py: プロジェクト固有の省略
# Bad
be_manager  # back_end_manager

# Good
eval  # evaluation
doc  # document
str  # string
```

```py: 不要な単語を削除
# Good
to_string():  # convert_to_string():
```

### 名前のフォーマットで情報を伝える
- クラス，関数，変数，メンバ変数等で異なるフォーマットを使う
- 基本的にはコーディング規約やプロジェクト内の規約に従う
```py
PythonClass   # クラス，例外など
python_class  # メソッド，関数，変数
PYTHON_CONST  # 定数
```

## 3章　誤解されない名前
### 曖昧な表現を避ける
```py
# Bad
filter():  # 選択するのか除外するのか不明

# Good
select():   # 選択する
exclude():  # 除外する
```
```py
# Bad
length  # 何の長さか不明

# Good
max_lenght  # 最大の長さ
max_chars   # 文字の長さ
```

### 限界値を含めるときはminとmaxを使う
```py
# Bad
CART_TOO_BIG_LIMIT = 10  # 未満か以下か不明

# Good
MAX_ITEMS_IN_CART = 10  # 10以下となる
```

### 範囲を指定するときはfirstとlastを使う
```py
# Bad
integer_range(start=2, stop=4)  # [2,3] or [2,3,4] 

# Good
integer_range(start=2, stop=4)  # [2,3,4]　包含していることが明確
```

### 包含/排他的範囲にはbeginとendを使う
- 4月中を表す場合，`begin`の4月1日は含み，`end`の5月1日は含まないとなる
```py
# Bad
print_event_int_range(begin="04-01 00:00:00", end="04-30 23:59:59")

# Good
print_event_int_range(begin="04-01 00:00:00", end="05-01 00:00:00")
```
### ブール値の名前
- 頭にis, has, can, shouldなどを付ける
- 否定形ではなく肯定形にする
```py: 頭にhasをつける
# Bad
space_left():  # 残りスペースを返すの？

# Good
has_space_left():  # 残りスペースがあればTrue
```

```py: 肯定形にする
# Bad
disable_ssl = False

# Good
use_ssl = True
```

### 軽い処理か重い処理かをユーザーに伝える
```py
# Bad
get_mean()  # 一瞬で取得するイメージ
size()

# Good
compute_mean()  # 計算コストがかかるイメージ
count_size()  
```

## 4章　美しさ
### 一貫性のある簡潔な改行位置
```py
# Bad
class PerformanceTester:
    wifi = TcpConnectionsSimulator(
        500,  # kbps
        80,  # millisecs latency
        200,  # jitter
        1  # packet loss
    )

    t3_fiber = \
    TcpConnectionsSimulator(
        4500,  # kbps
        10,  # millisecs latency
        0,  # jitter
        0  # packet loss
    )

# Good
class PerformanceTester:
    # TcpConnectionsSimulator(throughput, latency, jitter, packet_loss)
    #                             [kbps]     [ms]    [ms]    [parcent]
    
    wifi     = TcpConnectionsSimulator(500,  80, 200, 1)
    t3_fiber = TcpConnectionsSimulator(4500, 10,   0, 0)
```

### メソッドを使った整列
```py
# Bad
database_connection = database_connection()
error = ""
full_name = expand_full_name(database_connection, "Doug Adams", error)
assert full_name == "Mr. Douglas Adams"
assert error == ""
full_name = expand_full_name(database_connection, "John", error)
assert full_name == ""
assert error == "more than one result"

# Good
def check_full_name(partial_name, expected_full_name, expected_error):
    database_connection = database_connection()
    error = ""
    full_name = expand_full_name(database_connection, partial_name, error)
    assert full_name == expand_full_name
    assert error == expected_error
    
check_full_name("Doug Adams", "Mr Douglas Adams", "")
check_full_name("John"      , ""                , "more than result")
```

### 縦の線をまっすぐにする
```py
# Bad
details = request.POST.get('details')
location = request.POST.get('location')

# Good
details  = request.POST.get('details')
location = request.POST.get('location')
```

### 一貫性と意味のある並び
- 対応するHTMLフォームの`<input>`フィールドと同じ並び順にする
- 最重要なものから重要度順に並べる
- アルファベット順に並べる

### 宣言をブロックにまとめる・コードを段落に分割する
```py
# Bad
def suggest_new_friends(user, email_password):
    friends = user.friends()
    friends_emails = set(f.email for f inf friends)
    contacts = import_contacts(user.email, email_password)
    contact_emails = set(c.email for c in contacts)
    
# Good
def suggest_new_friends(user, email_password):
    # ユーザーの友達のメールアドレスを取得する
    friends = user.friends()
    friends_emails = set(f.email for f inf friends)
    
    #ユーザのメールアカウントからすべてのメールアドレスをインポートする 
    contacts = import_contacts(user.email, email_password)
    contact_emails = set(c.email for c in contacts)
```

## 5章　コメントすべきことを知る
### コメントするべきではないこと
- コードから分かることをコメントに書かない
- ひどいコードを補う補助的なコメント
```py: コードから分かる
# Bad
# 2番目の'*'以降をすべて削除する
name = '*'.join(line.split('*')[:2])
```

```py: 補助的なコメントを記述するのではなく適切な変数名をつける
# Bad
# レジストリキーのハンドルを開放する．実際のレジストリは変更しない．
delete_registry()

# Good
release_registry_handle()
```

### 自分の考えを記録する
- 考えを記録する
- コードの欠陥にコメントをつける
- 定数にコメントを付ける
```py: 考えを記録
# Good
# このデータだとハッシュテーブルよりもバイナリツリーの方が40%速かった
# 左右の比較よりもハッシュの計算コストの方が高いようだ
```

```py: コードの欠陥コメント
# Good
# TODO: あとで追加，修正する
# FIXME: 既知の不具合があるコード
# HAXK: あまりきれいじゃない解決策
# XXX: 危険！大きな問題がある
```

```py: 定数にコメント
# Good
NUM_TUREADS = 8  # 値は「>= 2 * num_processors」で十分
MAX_RSS_SUBSCRIPTIONS = 100  # 合理的な限界値．人間はこんなに読めない
IMAGE_QUALITY = 0.72  # 0.72ならユーザはファイルサイズと品質の面で妥協できる
```

### 読み手の立場になって考える
- コードを読んだ人が疑問に思うところを予想してコメント
- 読み手が驚くような動作は文書化
- ファイルやクラスには「全体像」のコメント
- 読み手が細部にとらわれないよう，コードブロックにコメントを付け要約

## 6章　コメントは正確で簡潔に
- 複数のものを指す可能性がある「それ」や「これ」などの代名詞は避ける
- 関数の動作はできるだけ明確に説明する
- コメントに含める入出力の実例慎重に選ぶ
- コードの意図は，詳細レベルでなく，高レベルで記述する
- よくわからない引数にはインラインコメントを使う
- 多くの意味が詰め込まれた言葉や表現を使って，コメントを簡潔に保つ

# 第Ⅱ部　ループとロジックの単純化
## 7章　制御フローをよみやすくする
### 条件式の引数の並び方
```py
# Bad:
if 10 == length:
while bytes_received < bytes_expected:

# Good: 左に期待値や比較対象の式を置く
if length == 10:
while bytes_expected > bytes_received:
```

### if/elseブロックの並び順
- 条件は否定形よりも肯定形を使う
- 単純な条件を先に書く
- 関心を引く条件や目立つ条件を先に書く
```py: 肯定形を使う
# Bad
if not debug:

# Good:
if debug
```

```py: 関心を引く条件を先に処理
# Bad
if not url.has_query_parameter("expand_all"):
    response.render(items)
else:
    for item in items:
        item.expand()

# Good: "expand_all"が関心を引く条件なため，これを先に処理する
if url.has_query_parameter("expand_all"):
    for item in items:
        item.expand()
else:
    response.render(items)
```

### 三項演算子
- 基本的には誰もが分かりやすいif/elseを使い，三項演算子は簡潔になるときのみ使う
```py
# Bad
return mantissa * (1 << exponent) if exponent >= 0 else mantissa / (1 << -exponent)

# Good
time_str += "pm" if hour >= 12 else "am"
```

### 関数から早く返す
```py
# Good: 早期returnを使用する
def contains(str, substr):
    if str is None or substr is None: return False
    if substr == "": return True
```

### ネストを浅くする
- 関数内で早期`return`を使う
- ループ内で`continue`を使う
```py: continueを使う
# Bad
for result in results:
    if result is not None:
        non_null_count += 1

        if result != "":
            print("considering")

# Good
for result in results:
    if result is None:
        continue
    non_null_count += 1
    
    if result == "":
        continue
    print("considering")
```

### 実行の流れを追えるか？
- スレッド：どのコードがいつ実行されるかよくわからない
- シグナル/割り込みハンドラ：他のコードが実行される可能性がある
- 例外：いろんな関数呼び出しが終了しようとする
- 関数ポインタと無名関数：コンパイル時に判別できないので，どのコードが実行されるかわからない
- 仮想メソッド：object.virtulMethod()は未知のサブクラスのコードを呼び出す可能性がある

### 使わない方が良い構文
- 基本的にdo/whileは避け，whileループを使う
- 基本的にgotoは使わない

## 8章　巨大な式を分割する
### 説明変数
```py
# Bad
if line.split(':')[0].strip() == "root":

# Good: 式を表す変数を使う
user_name = line.split(':')[0].strip()
if user_name == "root":
```

### 要約変数
```py
# Bad
if request.user.id == document.owner_id:
    # 編集可能
if request.user.id != document.owner_id:
    # 読取専用

# Good: 式を変数に代入しておく
user_own_document = request.user.id == document.owner_id

if user_owns_document:
    # 編集可能
if not user_owns_document:
    # 読取専用
```

### ド・モルガンの法則を使う
```py
# Bad
if not (file_exists and not is_protected):

# Good
if (not file_exists) or (is_protected):
```

## 9章　変数と読みやすさ
### 変数を削除する
- 役に立たない一時変数
- 中間結果を保持する変数
- 制御フロー変数
```py: 役に立たない一時変数
# Bad
now = datetime.datetime.today()
root_message.last_view_time = now

# Good
root_message.last_view_time = datetime.datetime.today()
```

```py: 制御フロー変数
# Bad
done = False

while 条件 and not done:
    ...
    if 条件:
        done = Ture
	continue

# Good
while 条件:
    ...
    if 条件:
        break
```

### 変数のスコープ縮める
- グローバル変数をできるだけ使わない
- 大きなクラスを小さなクラスに分割する
- 定義の位置を下げる

### 変数は一度だけ書き込む
- 変数に一度だけ値を設定する
- イミュータブルにする

# 第Ⅲ部　コードの再構成
## 10章　無関係の下位問題を抽出する
### 抽出すべき機能
- 高レベルの目標と関係のない下位の処理
- 純粋なユーティリティコード（文字列操作，ファイルの読み書きなど）
- その他汎用的な機能
- やりすぎには注意
```py
# Bad
def find_closest_location(lat, lng, array):
    closest_dist = MAX_VALUE
    for i in array:
        ...
	# 高レベルの目標と関係ない複雑な処理
	...
	if dist < closest_dist:
	    ...
    return closest
    
# Good
def spherical_distance(lat1, lng1, lat2, lng2):
    ...
    # 高レベルの目標と関係ない複雑な処理
    ...

def find_closest_location(lat, lng, array):
    closest_dist = MAX_VALUE
    for i in array:
        dist = spherical_distance(...)
	if dist < closest_dist:
	    ...
    return closest
```

```py
# Bad
user_info = {"username": "...", "password": "..."}
user_str = json.dumps(user_info)
cipher = Chiper("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE)
encrypted_bytes = chpher.update(user_str)
encrypted_bytes += chiper.final()
url = "http://exmaple.com/?user_info=" + base64.urlsafe_b64encode(encrypted_bytes)

# Good
def url_safe_encrypt(obj):
    obj_str = json.dumps(obj)
    cipher = Chiper("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE)
    encrypted_bytes = chpher.update(obj_str)
    encrypted_bytes += chiper.final()
    return base64.urlsafe_b64encode(encrypted_bytes)
    
user_info = {"username": "...", "password": "..."}
url = "http://exmaple.com/?user_info=" + url_safe_encrypt(user_info)
```

## 11章　一度に1つのことを
- 一度に1つのタスクを行う
- タスクをできるだけ異なる関数に分割，もしくは異なる領域に分割する

## 12章　コードに思いを込める
- ロジックを明確に説明する
- 既存のライブラリが何を提供してくれるかを理解し活用する
```py: ロジックを明確に説明する
# Bad
if document:
　　if not is_admin_request() and (document["username"] != SESSION["username"]):
        return not_authorized()
else:
    if not $is_admin:
        return not_authorized()

# Good
if is_admin_request():  # 管理者
    pass
elif document and (document["username"] != SESSION["username"]):  # 文書の所有者
    pass
else:  # その他
    return not_authorized()
```
## 13章　短いコードを書く
- 汎用的なユーティリティコードを作って，重複コードを削除する
- 未使用のコードや機能を削除する
- プロジェクトをサブプロジェクトに分割する
- 定期的にすべてのAPIを読んで，標準ライブラリに慣れ親しんでおく
```py: 標準ライブラリに慣れ親しんでおく
# Bad: リストから重複を取り除くため関数を実装
def unique(elements):
    temp = {}
    for element in elements:
        temp[element] = None
    return list(temp.keys())
    
print(unique([2,1,2]))  # [2,1]

# Good: set型を使えば解決
print(list(set([2,1,2])))  # [1,2]
```

## 14章　テストと読みやすさ
### テストを読みやすく保守しやすいものにする
- テストのトップレベルはできるだけ簡潔にする
- 入出力のテストはコード1行で記述できるとよい
```py: 入出力のテストを1行で記述
# テスト対象の関数
class ScoredDocument:
    def __init__(self):
        self.url = ""
        self.score = 0.0

def sort_and_filter_docs(docs):
    docs.sort(key=lambda x: x.score, reverse=True)  # スコアに基づいて昇順にソート
    docs[:] = [doc for doc in docs if doc.score >= 0]  # 負の値を削除

# Bad  
def Test1():
    docs = [ScoredDocument() for _ in range(5)]
    docs[0].url = "http://example.com"
    docs[0].score = -5
    docs[1].url = "http://example.com"
    docs[1].score = 1
    docs[2].url = "http://example.com"
    docs[2].score = 4
    docs[3].url = "http://example.com"
    docs[3].score = -5999
    docs[4].url = "http://example.com"
    docs[4].score = 3.0

    sort_and_filter_docs(docs)
    assert len(docs) == 3
    assert docs[0].score == 4
    assert docs[1].score == 3.0
    assert docs[2].score == 1

Test1()

# Good
def check_score_before_after(scores, expected):
    docs = [ScoredDocument() for _ in scores]
    for doc, score in zip(docs, scores):
        doc.score = score
    
    sort_and_filter_docs(docs)
    actual = [doc.score for doc in docs]
    return actual == expected

check_score_before_after([-5, 1, 4, -5999, 3.0], [4,3,1])
```

### エラーメッセージを読みやすくする
```py: unittestモジュールを使ってより詳細なエラーメッセージを表示する
# Bad
a = 1
b = 2
assert a == b

# エラーメッセージ：aとbの中身が分からない
Traceback (most recent call last):
  File "Main.py", line 5, in <module>
    assert a == b
AssertionError

# Good
import unittest

class NormalTest(unittest.TestCase):
    def test_normal_test(self):
        a = 1
        b = 2
        self.assertEqual(a, b)

if __name__ == '__main__':
unittest.main()

# エラーメッセージ：詳細が表示される
F
======================================================================
FAIL: test_normal_test (__main__.NormalTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "Main.py", line 17, in test_normal_test
    self.assertEqual(a, b)
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
```
### テストの適切な入力値を選択する
- 入力値を単純化
- 1つの機能に複数のテスト
```py: 入力値を単純化
# Bad
check_score_before_after([-5, 1, 4, -5999, 3.0], [4, 3, 1])

# Good
check_score_before_after([1, 2, -1, 3], [3, 2, 1])
```

```py: 1つの機能に複数のテスト
# Good: 小さなテストを複数作る方が良い
check_score_before_after([2, 1, 3], [3, 2, 1])    # ソート
check_score_before_after([-10, -1, 3], [3])       # マイナスは削除
check_score_before_after([1, -2, 1, -2], [1, 1])  # 重複は許可
check_score_before_after([], [])                  # 空の入力は許可
```

### テストの機能に名前を付ける
- クラス名や関数名，状況やバグなどを名前に含める（テストの機能名は長くて良い）
```py
# Bad
Test1()
Test2()

# Good
test_sort_and_filter_docs()
test_sort_and_filter_docs_basic()
```

### テスト容易性の低いコード
- グローバル変数を使っている
- 多くの外部コンポーネントに依存
- コードが非決定的な動作をする

### テスト容易性の高いコード
- クラスが小さい．あるいは内部状態を持たない
- クラスや関数が1つのことをしている
- クラスは他のクラスにあまり依存していない
- 関数は単純でインターフェースが明確