機能のある場所の見つけ方
コードベースとは、1つのプログラムのコードすべてです。仕事の初日に渡されるのは、他の人が書いた数千行で、地図はありません。
全部を読む人はいません。小さな質問を1つ立て、答えを検索して、そこから呼び出しをたどります。
このレッスンのゴール
メモ
自分が書いていないコードで機能のある場所を見つけ、呼び出しをファイルからファイルへたどれる。
重要な言葉
| ことば | いみ |
|---|---|
| コードベース | 1つのプログラムを作るコードファイルすべて |
| 入口 | プログラムが動き始めるファイルや関数 |
| 検索 | すべてのファイルから、grep などで言葉を探すこと |
| 呼び出し元 | 関数を使っているコードの行 |
| 呼び出しの連鎖 | 1つの呼び出しが次々と導く関数の並び |
| 説明代わりのテスト | 関数が約束することを、コードで示すテスト |
学生が入力するセルは、このプレビューでは空白です。
ファイルではなく質問から始める
最初のファイルを開いて、上から読んではいけません。小さな質問を立てます。クーポンの割引は、どこで計算されているのでしょうか。
まずフォルダーの形を見ます。プログラムを動かして、何をするか見ます。そのあと、画面に見える言葉を検索します。
プロジェクトを作ります。隠れたキャッシュフォルダーは検索の邪魔なので、最初の Run で止めます。
Python のキャッシュを止める
export PYTHONDONTWRITEBYTECODE=1ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
2つのフォルダーを作る
mkdir -p ~/readcode/shop ~/readcode/testsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
プロジェクトに入る
cd ~/readcodeここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
価格表を書く
printf 'PRICES = {"tea": 250, "mug": 899, "book": 1200}\n\n\ndef unit_price(code):\n return PRICES[code]\n' > shop/prices.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
クーポンを書く
printf 'COUPONS = {"SAVE10": 10, "HALF": 50}\n\n\ndef discount_percent(code):\n if code in COUPONS:\n return COUPONS[code]\n return 0\n' > shop/discounts.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
注文の合計を書く
printf 'from shop.prices import unit_price\nfrom shop.discounts import discount_percent\n\n\ndef order_total(items, coupon=None):\n total = 0\n for item in items:\n total = total + unit_price(item[0]) * item[1]\n percent = discount_percent(coupon)\n return total - total * percent // 100\n' > shop/orders.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
コマンドを書く
printf 'import sys\nfrom shop.orders import order_total\n\n\ndef main(args):\n items = [("tea", 2), ("mug", 1)]\n coupon = args[0] if args else None\n print(order_total(items, coupon))\n\n\nmain(sys.argv[1:])\n' > shop/cli.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
テストを書く
printf 'import unittest\nfrom shop.orders import order_total\n\n\nclass OrderTest(unittest.TestCase):\n def test_no_coupon(self):\n self.assertEqual(order_total([("tea", 2)]), 500)\n\n def test_save10(self):\n self.assertEqual(order_total([("tea", 2)], "SAVE10"), 450)\n' > tests/test_orders.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
プロジェクトの形を見る
ls -Rここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
クーポンを付けて動かす
python3 -m shop.cli SAVE10ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
見える言葉を検索する
プログラムは 1260 と表示しました。クーポンの名前は SAVE10 でした。その言葉は、どこかに書いてあるはずです。grep -rn は、指定したフォルダーの下のすべてのファイルを検索して、ファイル名、行番号、その行を表示します。
画面に見える名前は、知らないコードに入る、いちばんよい入口です。
クーポンの名前を検索する
grep -rn "SAVE10" shop testsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
呼び出しを次の場所へたどる
検索すると、discounts.py にクーポンの表がありました。表は、それだけでは何もしません。次の質問は、だれが使っているか、です。
表を読む関数を検索します。1行が定義で、1行が取り込み、1行が呼び出しです。割引が注文に届くのは、その呼び出しです。
関数の使われ方をすべて探す
grep -rn "discount_percent" shopここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
例: 1つのコマンドの呼び出しの連鎖
| 手順 | ファイルと場所 | 起きること |
|---|---|---|
| 1 | shop/cli.py の main | 入口が、コマンドラインからクーポンを読む |
| 2 | shop/orders.py の order_total | 価格を足し、次に割引を頼む |
| 3 | shop/prices.py の unit_price | 1つの品の値段を返す |
| 4 | shop/discounts.py の discount_percent | クーポンを引く。なければ0 |
学生が入力するセルは、このプレビューでは空白です。
テストを読むとコードの約束がわかる
よいテストは、本物の入力と答えで、関数が何をするはずかを言います。説明が古くなることがない、ただ1つの資料です。古くなると、テストが失敗するからです。
テストの名前を並べて、実行します。通れば、読んだ内容は今日も本当です。
テストを一覧にする
grep -n "def test" tests/test_orders.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
テストを実行する
python3 -m unittest discover -s tests 2>&1 | grep -E "^(OK|FAILED)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
先にすべてを理解しようとして、全ファイルを上から読む人がいます。何日もかかり、ほとんどは質問に関係がありません。質問を1つ立て、検索し、呼び出しをたどり、答えられたら止めます。
さらに: もっと大きなプロジェクト
自分が使っているプロジェクトで、ソースが手元にあるものを選びます。それがすることを1つ選びます。たとえば、設定ファイルをどう読むか、です。
質問、検索した言葉、たどったファイルの連鎖を書きましょう。何手順だったでしょうか。