見出し画像

【読書感想】リーダブルコード

プログラミングをする人にとっての必読書と謳われている本。
確かに、プログラムを業務で書く前に、全員の共通認識としてあるべき考え方が多く載っていた。
(自分のために)特に重要だと思ったことをまとめてみようと思う。



1.名前に情報を詰め込む

クラス、関数、変数など、プログラムを書いていると、自身で命名しないといけない場面が数多くある。普段(この本を読むまで)は、命名のさい、自身の基準で「こんなもんかなぁ」程度で何気なく命名していた。

例えば、
データフレームで黒データと白データを読み込んだ時には

import pandas as pd
kuro = pd.read_csv('黒データの場所')
shiro = pd.read_Csv('白データの場所')

といったようにである。

また、関数の定義についても

def calculate_A(df):
    A = []
    count = 0
    for i, row in df.iterrows():
        if row['column_name'] == 1:
            count += 1
        else:
            count -= 1
        A.append(count)
    return A

のようにその時一時的に使用するための関数として(適当に)命名していた。

でも、この本では

「データの種類」や「目的」などの大切な情報を命名の際入れることが、第三者が読むときには非常に大切になる

と書かれていた。

確かに、上記の場合、その下にkuro や shiro が出てきても、なんの変数なのか、データなのか といったことがよく分からないし、関数は何をしたいのかわからない。

この本を読んでからは以下のように変更した。

df_kuro = pd.read_csv('黒データの場所')
df_shiro = pd.read_Csv('白データの場所')

def Calculate_'What I count'(df):#関数の頭は大文字に
    list_for_count_'What I count' = []#関数内であっても作成する変数の名前は目的をわかるように
    count = 0
    for index, row in df.iterrows():#indexやrowなどを自分だけが分かるように省略せずに記載
        if row['column_name'] == 1:
            count += 1
        else:
            count -= 1
        list_for_count_'What I count'.append(count)
    return list_for_count_'What I count'

特に、英語を苦手としている人は、日本語で命名しがちであるが、シソーラス(類語辞典)やChatGPTなどを使用して、できるだけ英語での表記を心がけよう。
read_csvやdef、returnなど、プログラム言語は基本的にすべて英語で記載されている。そので中で急に ”Honzitu” のような日本語が出てくるのはナンセンスと考える。

import datetime

Honzitu = datetime.date.today()# 今日の日付を取得

#↓下記に変更すべき------------------------------------------------

today = datetime.date.today()

また、汎用的・抽象的な名前はできるだけ避けよう。
例えば、get や retval (return valuableの略)などの名前は、「何かをゲットするんだな」や「何かを返すんだな」などは分かるものの、結局あいまいな命名となってしまっている。

下記のように命名を変更すべきである。

# No.1
get = df.loc[df['単位を取得したかどうかのダミー変数']==1,:]

# No.2
if row['金額'] >= 100:
    retval = 1 
else:
    retval = 0

#↓下記に変更すべき------------------------------------------------

# No.1
dummy_credit = df.loc[df['単位を取得したかどうかのダミー変数']==1,:]

# No.2
if row['金額'] >= 100:
    dummy_over_100 = 1 
else:
    dummy_over_100 = 0

そのほかにも、

  1. 限界値を定めるときはmin、maxを使う

  2. 範囲を定めるときはfirst、lastを使う

  3. ブール(bool)値の時はA_is_やA_has_などを使う

等に気を付けよう。

【メモ】

bool値はTure、Flaseを返す二値変数である。
数学者booleanから命名されている。
(booleanが素晴らしい人とは思うのだが、個人名から命名しないでほしい。。(リーダブルコードにもそう書いてあるように))


2.コメントの方法について

コメントを書くにはそれだけの価値を持たせる必要がある。
自分のリマインドとしてのよくわからないコメントを書きがちであるが、第三者を常に考えコメントしよう。

考えることとしては、

  1. コードからすぐにわかることはコメントしない

  2. コメントをつけないとわからないプログラムはプログラム自体を変えよう

  3. 優れたコード > ひどいコード + 優れたコメント

  4. 定数にコメントをつける

である。

この4つを常に考えコメントするようにしよう。
たとえは下記に示した。

# No.1
#データを読み込む ← プログラムを読んだらわかるからこのコメントは不要
df = pd.read_csv('')

# No.2
A = pd.get_dummies(df['key'])
df.insert(2, 'dummy', A) #作成したダミー変数をdfの3列目にinsertする ← もしわかりづらい場合はプログラム自体を下記のように変更

#↓下記に変更

df['dummy'] = pd.get_dummies(df['key'])

3.条件式の書き方

条件式を書くときは、左側に「調査対象」を、右側に「比較対象」を書くことを意識しよう。

if 10 < x よりも if x > 10 のほうが分かりやすい。
この用法は英語と一緒とのことである。日本語を使う私たちにはあまり直観的に理解しずらいが、数学でも同様の用法を取ることが多いので、数学の式を考えながら条件式を書けばよいと思う。

x = 10
if x > 10:
    print("xは10より大きい")
elif x == 10:
    print("xは10と等しい")
else:
    print("xは10より小さい")

そのほかにも、

  1. 否定形よりも肯定形を使う

  2. 関心を引く条件を先に書く

  3. do/whileループを避ける

  4. ネストを浅くする

を意識しよう。

特に、do/whileループについては、コードブロックを再実行する条件が下にあることで、エラーや混乱を招くことが多くなってしまうため、できるだけ避けよう。


4.グローバル変数とローカル変数を使い分ける

グローバル変数とは、コードブロックを実行した後に残る変数である。そのあとにも繰り返し使用する際に使う。
ローカル変数とは、コードブロックを実行した後に消える一時的な変数である。

グローバル変数をたくさん作ってしまうと、本当に大切な変数が分からなくなってしまうし、命名の複雑さから、エラーを起こしてしまいやすくなる。

なので、一時的に使用する変数についてはローカル変数を指定してあげる癖をつけよう。

グローバル変数は少なければそれだけプログラムが綺麗に見える。
(グローバル変数を作る必要があるときは躊躇なく作ろう)

Pythonでは _ (アンダーバー)を先頭につけると、ローカル変数として定義される。

#例えば、60分以上テレビゲームをプレイした日の数をカウントする関数を実装しようとすると

df = pd.DataFrame(data)

def CalculateDays_TVGamesPlayed_over_60min():
    # ローカル変数
    count = 0
    
    # グローバル変数 df を関数内で使用
    global df
    
    for index, row in df.iterrows():
        if row['key'] >= 60:
            count += 1
    
    df['counts_days'] = count
    return df

# 関数の呼び出し
df = CalculateDays_TVGamesPlayed_over_60min()

5.テストコードについて

テストコードは、コードや関数の定義前に、それらがうまく動くか確認するために書くコードのことである。

基本的には、実際に使用するコードブロックとは別に、テストコードをまとめることがあるが、その際の注意事項のうち、特に大切なことを下記にまとめる。

  1. 最小のテストを作成する

  2. エラーメッセージを読みやすくする

  3. テストの機能に名前をつける

特に、エラーメッセージの読みやすさについて、本書ではunittestモジュールのassertEqual()関数が紹介されていた。

assertEqual()関数についてchatGPTに聞いてみたので、それを下記にまとめる。

unittestモジュールは、Pythonの標準ライブラリの一部であり、ユニットテストを作成するためのツールを提供します。assertEqual()関数は、unittestモジュールの一部であり、テストケースで使用されるアサーションメソッドの一つです。このメソッドは、二つの値が等しいことを確認するために使用されます。

以下に、assertEqual()関数の基本的な使い方と、unittestモジュールを使ったテストの作成方法について説明します。

基本的な使い方
assertEqual(first, second, msg=None) は、firstとsecondが等しいことを確認します。等しくない場合にはテストが失敗し、オプションでエラーメッセージ msg を表示します。


以下に、unittestモジュールを使った簡単なユニットテストの例を示します。この例では、ある関数 add(a, b) をテストします。

テスト対象の関数

def add(a, b):
    return a + b

ユニットテストの作成

import unittest

# テストケースの定義
class TestAddFunction(unittest.TestCase):
    
    def test_add_positive_numbers(self):
        self.assertEqual(add(1, 2), 3)  # 1 + 2 は 3 であるべき
        
    def test_add_negative_numbers(self):
        self.assertEqual(add(-1, -1), -2)  # -1 + -1 は -2 であるべき
        
    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)  # 0 + 0 は 0 であるべき

# テストの実行
if __name__ == '__main__':
    unittest.main()

説明

  1. 関数の定義

    • add(a, b) 関数は、二つの引数を受け取り、それらの和を返します。

  2. テストケースの定義

    • unittest.TestCase を継承した TestAddFunction クラスを定義します。

    • 各テストメソッドは test_ で始まる名前を持ちます。これにより、unittest モジュールはこれらのメソッドを自動的にテストとして認識します。

  3. assertEqualの使用

    • assertEqual() メソッドは、期待される結果と実際の結果が等しいかどうかを確認します。

    • テストが失敗すると、どのアサーションが失敗したかが報告されます。

  4. テストの実行

    • if __name__ == '__main__': ブロック内で unittest.main() を呼び出すことで、テストスクリプトを直接実行できるようにします。

これにより、add 関数が正しく動作するかどうかを確認するためのユニットテストが作成されます。unittest モジュールは多くの便利なアサーションメソッドを提供しており、複雑なテストケースにも対応できます。


いいなと思ったら応援しよう!