return を書き忘れる
print("=== おこづかい帳 ===") とだけ書くと、画面には出ますが関数の戻り値は None になり、テストは失敗します。必ず return で返します。
レッスンの本文で注意している点を、つまずきの側から195件ぶん並べ直しました。書いた覚えのある形が見つかったら、 そのままレッスンに戻って前後を読めます。
return を書き忘れるprint("=== おこづかい帳 ===") とだけ書くと、画面には出ますが関数の戻り値は None になり、テストは失敗します。必ず return で返します。
return "=== おこづかい帳 == のように閉じるクオートがないと SyntaxError になります。開いたら必ず閉じます。
print() のように全角のかっこを書くとエラーになります。記号はすべて半角で書きます。日本語を打つ設定のまま記号を入力すると起こりがちです。
name + price_text と書くと りんご120 とくっつきます。+ " " + を挟むのを忘れないでください。半角スペースと全角スペースは別の文字なので、必ず半角にします。
return "name" + " " + "price_text" と書くと、引数の中身ではなく name price_text という文字がそのまま返ります。引数を使うときはクオートで囲みません。
円 を付け忘れる最後の + "円" が抜けると りんご 120 になります。テストは 1 文字違うだけで落ちるので、末尾まで見直します。
return "budget - price1 - price2" と書くと、計算されずにその文字がそのまま返ります。計算式はクオートで囲みません。
x を使うbudget x 2 は Python では文法エラーです。掛け算はアスタリスク * と決まっています。
budget - (price1 - price2) と書くと 3000 - 40 で 2960 になってしまいます。今回はかっこは不要です。
\n を付ける+ "\n" で締めくくると、目に見えない差でテストが失敗します。改行は行と行の 間 にだけ入れます。
\n を全角で書く¥n や全角の n では改行になりません。半角のバックスラッシュと半角の n を使います。日本語キーボードでは ¥ キーで入力できます。
def より上で呼び出すと NameError になります。定義が先、呼び出しがあと、という順番を守ります。
# を使う見た目は似ていますが、Python はコメントとして扱いません。SyntaxError になります。記号は半角で入力します。
# をコメントだと思うprint("#1 りんご") の # はただの文字で、コメントにはなりません。クオートの中は文字列として扱われます。
return price1 + # price2 のように式の途中で # を書くと、右辺が消えて SyntaxError になります。コメントアウトは行ごとに行います。
上のほうは Python の内部の呼び出し履歴で、原因は いちばん下 に書いてあります。下から読むのが鉄則です。
SyntaxError は原因の 1 行あとを指すことがあります。指摘された行に問題が無ければ、上の行を疑います。
動かない行をまるごと削ればエラーは消えますが、必要な処理まで消えます。直すのであって、消すのではありません。
エラーを読んでみるで詳しく読む\n を付けてしまう"合計 200円" のうしろに改行を足すと、見た目には分からない差でテストが落ちます。改行は行と行の間だけです。
120円 の 1 2 0 は半角、円 は全角です。数字を全角で打つと別の文字になり、一致しません。
見出しはイコール = が半角 3 つ、内側は半角スペース 1 つずつです。== や ==== では通りません。
__name__ のアンダースコアの数を間違える前後とも 2 本ずつで合計 4 本です。1 本だと AttributeError になります。
type の結果をそのまま比べようとするtype(120) は "int" という文字列ではありません。文字列がほしいときは必ず .__name__ を付けます。
type を変数名に使ってしまうtype = "食費" と書くと、以降 type が命令として使えなくなります。組み込みの名前は変数名に使いません。
int の結果を受け取らないint(price_text) と書くだけでは何も起きません。price = int(price_text) のように変数に入れるか、そのまま return する必要があります。
int("1200円") は ValueError です。円 を含めずに数字だけを渡します。
int(price_text) + "80" は TypeError です。片方だけ変換しても、もう片方が文字列なら混ぜられません。
str を付け忘れるname + " " + price + "円" は TypeError です。エラーメッセージは can only concatenate str と出ます。「文字列にしか足せない」という意味なので、数値のほうを str で包みます。
str で包んでしまうstr(name + " " + price + "円") と書いても直りません。かっこの中で先に連結が起きて、そこで落ちるからです。包むのは price だけです。
str と int を取り違える名前が短いので逆に書きがちです。str は文字列にする、int は整数にする、と結果の型で覚えます。
true や none は NameError です。True False None はすべて先頭が大文字です。
"True" は str です。bool がほしいときはクオートを付けません。
None を 0 や空文字と同じに扱う型が違うので、type の結果も str にしたときの見た目も変わります。未入力を表したいときだけ None を使います。
f を忘れる"{name} {price}円" は波かっこごと文字として残ります。実行してもエラーが出ないので気づきにくく、表示を見て初めて分かります。
f"{'name'} {price}円" と書くと、変数ではなく name という文字がそのまま入ります。かっこの中の変数名にクオートは付けません。
f"{name}{price}円" は りんご120円 になります。空白が必要なら {} と {} の間に自分で書きます。
input の結果をそのまま計算に使う戻り値は必ず str です。int を通さないと TypeError になります。
split の結果を 1 つの変数で受けるname = text.split(" ") と書くと、切り分けた 2 つがまとめて name に入ります。左に 2 つ名前を並べます。
split(" ") は半角スペースで切ります。全角スペースで打たれた文字列は切れません。
3 行目の 合計 ...円 が必要です。品物 1 件なので金額は同じですが、行としては別に用意します。
f-string を使わずに + で書く場合は str(price) が必要です。忘れると TypeError になります。
合計 120円 のうしろに \n を足すと、見た目では分からない差でテストが落ちます。改行は行と行の間だけです。
= と == の書き間違いtotal = budget は代入なので、比較のつもりで書くとエラーか、意図しない上書きになります。くらべるときは必ずイコール 2 つです。
> と >= の取り違え「予算を超えたら」は >、「予算に達したら」は >= です。ちょうどの値の扱いが変わります。
True の大文字を忘れるPython の真偽値は True と False で、先頭が大文字です。true と書くと NameError になります。
他の言語の && や || は Python では使えません。SyntaxError になります。英単語の and or not を使います。
category == "食費" and "日用品" は意図した意味になりません。and の右側にも category == "日用品" のように完全な条件を書きます。
and と or を取り違える「食費かつ 1000 円以上」を or で書くと、100 円の食費もすべて当たってしまいます。日本語の「かつ」「または」と対応させて確認します。
"交通" や "交通 費" のように 1 文字でも違うと一致しません。全角と半角、余分な空白に注意します。
"食" in "食費" は True ですが、"食" in ["食費"] は False です。右側が何なのかで判定の意味が変わります。
in の左右を逆にするcategories in category と書くと意図が反転します。左が探す値、右が探される場所です。
/ と // の取り違え1000 / 3 は 333.3333333333333 という小数です。f"{1000 / 3}円" と書くと、とんでもない見た目になります。整数がほしいときは // です。
10 // 0 も 10 % 0 も ZeroDivisionError になります。人数のように「0 になりうる値」で割るときは注意が必要です。
% を百分率と思い込む50 % 100 は「50 パーセント」ではなく「50 を 100 で割った余りの 50」です。
0 <= price >= budget と書くと、意味の分からない条件になります。範囲を表すときは向きをそろえます。
and と or の強さを逆に覚えるand のほうが強く、先にまとまります。混ぜるときはかっこを付けます。
0 <= price <= budget を 0 <= price and <= budget と書くand の右側にも完全な条件が必要です。この形は SyntaxError になります。
if 条件 then A else B のような書き方は Python にはありません。値が先、if、条件、else、値の順です。
else を省略する三項演算子の else は必須です。省くと SyntaxError になります。式は必ず何らかの値にならなければいけないからです。
f-string の中で外と同じクオートを使うと文字列が途中で切れます。外が " なら内は ' です。
\n を付けてしまう3 行目のうしろに改行を足すと、見た目には分からない差でテストが落ちます。改行は行と行の間の 2 か所だけです。
つくる:予算チェックで詳しく読むマイナス付きの数字が出たら、この間違いです。どちらの枝にいるかを確認してください。
つくる:予算チェックで詳しく読むf を書き忘れる"合計 {total}円" と書くと、{total} が中身に置き換わらず、そのまま文字として出ます。
if price >= 1000 と書いて末尾の : を落とすと SyntaxError になります。if の行は必ずコロンで終わります。
半角スペース 4 つが基本です。タブと混ぜたり、2 つと 4 つが混在したりすると IndentationError になります。
= と == を取り違える条件で使うのは比較の == です。代入の = を書くと SyntaxError になります。
elif の順番が逆if price < 2000 を先に書くと、300 も 2000 未満なので「普通」で確定してしまい、「節約」に永遠に届きません。狭い条件から先に 書きます。
else に条件を書くelse price >= 2000: は SyntaxError です。条件を付けたいなら elif を使います。
elseif や else if と書くPython の綴りは elif です。他の言語の書き方をそのまま持ち込むと SyntaxError になります。
day == "土" or "日" と書くPython はこれを (day == "土") or ("日") と読みます。"日" は空でない文字列なので常に True 扱いになり、どの曜日でも週末になってしまいます。== は省略できません。
内側の if の中身は 12 個分下げます。8 個のままだと外側の if 直下の処理になり、意味が変わります。
and と or を取り違えるand は両方、or はどちらか一方です。「週末かつ高額」は and です。
case _ を書き忘れるどの case にも一致しないと match 全体が素通りされます。関数なら return に届かず None が返り、テストに落ちます。
case の値をクオートで囲まないcase 食費: と書くと、Python は 食費 という変数名だと解釈します。文字列と比べたいなら case "食費": です。
match の中の case で 1 段、case の中身でさらに 1 段下げます。関数の中なら case の中身は 12 個分になります。
上で見たとおりです。"FizzBuzz" が一度も出ない、という症状が出たらまず順番を疑います。
最後の枝で return n と書くと、7 という数値が返ります。期待されているのは文字列の "7" なので str(n) が必要です。
== を書き忘れるif n % 3: と書くと「余りが 0 でない」という逆の意味になります。余りが 0 かどうかを見るので n % 3 == 0 です。
or が True を返すと思い込む返るのは元の値です。"食費" or "その他" は True ではなく "食費" です。
or を使うprice or 100 と書くと 0 円が 100 になります。数値では if price is None: と明示的に書きます。
and と取り違える既定値の穴埋めは or です。and を使うと逆に、値があるときだけ右側が返ります。
name in "りんご" と左右を逆に書くin は左が探すもの、右が探される場所です。逆にすると意味が反転します。
or のたびに in を書き忘れるif "りんご" or "パン" in name: は "りんご" が常に True 扱いになるため、すべて食費になります。or でつなぐのは条件そのものです。
elif だけで終えると、どれにも当てはまらない品名で None が返ります。
total = 0 の書き忘れ用意していない名前にいきなり足そうとすると NameError になります。ループの前に必ず初期値を置きます。
return をループの中に書いてしまうreturn total がインデントの内側にあると、1 周目で関数が終わってしまい、最初の 1 件だけが返ります。return はループの外、for と同じ深さより手前に置きます。
total = total + prices と書くと、数値とリストを足すことになり TypeError です。足すのは取り出した 1 件の price です。
i = i + 1 を書き忘れると、同じ 1 件を永久に読み続けます。手元で試すときは、止まらなくなったら Ctrl + C で強制終了できます。
i = i + 1 を break の前に置いてしまう位置そのものは動きますが、終了 を items に入れてしまう順番になっていないか、必ず読み返します。取り込むのは 終了 ではない行だけです。
while の条件を i <= len(inputs) にしてしまう最後の 1 件を読んだあと、存在しない位置を読もうとして IndexError になります。件数が 3 なら読める位置は 0 と 1 と 2 までです。
range(1, last_day, step) と書くと最終日が出ません。last_day まで含めたいなら last_day + 1 です。
day + "日目" は TypeError になります。str(day) で文字列に変えてからつなぎます。
step に 0 を渡してしまうrange の刻み幅に 0 は使えず、ValueError になります。刻み幅は必ず 0 以外です。
break と continue を取り違える0 円を飛ばすつもりで break を書くと、最初の 0 円で集計が止まってしまいます。飛ばしたいのか終わりたいのか、書く前に決めます。
continue の下に処理を書いて実行されると思い込むcontinue より下の行は、その周では絶対に動きません。「書いたのに動かない」の原因になりがちです。
while で continue を使ってカウンタを飛ばすwhile の中で continue すると、カウンタを進める行を飛ばしてしまい無限ループになります。while と continue の組み合わせは特に慎重に扱います。
enumerate(names) のままだと 0. りんご から始まります。人に見せる一覧なら enumerate(names, 1) です。
i + ". " は数値と文字列の足し算になり TypeError です。str(i) を挟みます。
result = result + str(i) + ". " + name + "\n" と書くと、最後の行のうしろにも改行が残ります。改行は行の 手前 に足すと、この問題が起きません。
zip に渡し忘れるfor name, price in names: と書くと、品名 1 文字ずつを 2 つに分けようとして ValueError になります。組にするには zip が要ります。
name + " " + price は TypeError です。str(price) を挟みます。
zip は短いほうで止まり、余った分を黙って捨てます。件数が合うことを前提にするなら、len を比べて確かめる処理を別途書きます。
line = "" を外側のループより前に書くと、すべての行がつながってしまいます。行ごとにリセットする必要があります。
内側でも i を使うと、外側の i が上書きされて表が崩れます。外側は i、内側は j のように分けます。
break が外側まで効くと思い込むbreak と continue は、いちばん内側のループ 1 つにしか効きません。内側で break しても、外側は次の周に進みます。
一覧の最後と 合計 がつながって 1 行になります。行を足すときは必ず "\n" を先に足します。
record["price"] は数値なので、str を挟まないと TypeError です。record["category"] は最初から文字列なのでそのままつなげます。
記録が 0 件でもループは静かに 0 周で終わるため、エラーは出ません。記録がありません の行が抜けたまま気づかない、というのがありがちです。
def greet_user(name) とコロンを書かないと SyntaxError です。字下げがないと IndentationError になります。字下げは半角スペース 4 つに揃えます。
greet_user とだけ書くと、関数そのものを指すだけで実行されません。greet_user("たろう") とかっこを付けて初めて動きます。
return を書かずに print で済ませる画面には出ますが、戻り値は None になります。採点は戻り値を見るので、必ず return で返します。
位置引数は前から順に入ります。split(people, total) のように順を取り違えると、エラーは出ないのに答えだけが狂います。いちばん見つけにくい種類のバグです。
add_tax() のように渡し忘れると TypeError になり、missing 1 required positional argument と教えてくれます。多すぎても同じく TypeError です。
/ と // を取り違えるprice * 110 / 100 は小数の 132.0 になります。132 とは別の値なので、テストは落ちます。整数が欲しいときは // です。
def f(budget=3000, total) は SyntaxError です。既定値のある引数は必ず後ろにまとめます。
呼び出しをまたいで同じものが使い回されます。None を既定値にして関数の中で作り直します。
>= にすると 3000 円ちょうどでもオーバー扱いになります。> と >= のどちらが仕様かを確かめてから書きます。
format_record("りんご", 120, categoly="食費") のように綴りを間違えると TypeError になり、unexpected keyword argument と出ます。定義側の綴りを確認します。
format_record("りんご", 120, "食費", category="日用品") は、category に位置とキーワードの両方から値が来るので TypeError です。どちらか片方にします。
str を忘れて数値を連結するname + " " + price は TypeError です。金額は数値なので、str(price) で文字列にしてからつなぎます。第 2 章でやった変換がここでも要ります。
return を 2 行書くreturn total の次の行に return average と書いても、2 行目には決して届きません。return は関数を終わらせるので、1 つの return にカンマで並べます。
total, average, count = total_and_average(prices) は ValueError です。not enough values to unpack と出ます。
0 件で ZeroDivisionError になります。テストには必ず空の場合を入れて確かめます。
divmod(people, total) と書くと divmod(3, 1000) になり、商が 0 で余りが 3 という意味の分からない答えが返ります。エラーが出ないぶん厄介です。
この環境の判定はリスト形式なので、return divmod(total, people) では通りません。q, r = divmod(...) で受けてから [q, r] にします。
0 除算を考えていない人数 0 で ZeroDivisionError です。入口で弾きます。
UnboundLocalError になります。global で押し切らず、引数と戻り値の形に直します。
add_record(records, "りんご", 120, "食費") とだけ書いて records = を付けないと、新しいリストは捨てられ、何も増えません。エラーが出ないぶん気づきにくい間違いです。
キーは name と price と category の 3 つです。namae などと書くと、あとで取り出すときに落ちます。
lambda に return を書くlambda r: return r["price"] は SyntaxError です。lambda は式なので、return は書きません。
key に呼び出した結果を渡すkey=lambda r: r["price"]() のようにかっこを付けると、金額を関数として呼ぼうとして TypeError になります。渡すのは関数そのものです。
sort と sorted を取り違えるrecords.sort() は元のリストを並べ替えて None を返します。新しいリストが欲しいときは sorted です。戻り値が None になっていたら、まずここを疑います。
キーは count total remaining over の 4 つです。1 つでも綴りが違うと、受け取る側が取り出せません。
total の初期化を忘れるfor の前に total = 0 を書かないと NameError です。合計を作るときの定番の手順です。
over を文字列にする"オーバー" のような文字列ではなく、True か False の真偽値を入れます。判定結果は真偽値で返し、言葉にするのは表示側の仕事です。
remove する["りんご"].remove("みかん") は ValueError になり、プログラムがそこで止まります。メッセージは list.remove(x): x not in list です。消す前に if "みかん" in items で存在を確かめるのが基本の対策です。
append の戻り値を受け取るitems = items.append("ノート") と書くと items が None になり、次の行で TypeError が出ます。append は呼ぶだけにします。
append に複数渡すitems.append("りんご", "みかん") はエラーです。append が受け取れるのは 1 つだけで、2 件足したいなら 2 回呼びます。
items[0:3] は 4 件ではなく 3 件です。「終了の手前まで」と覚えます。取れた件数が 1 つ足りない、多いと感じたらここを疑います。
items[-3] と items[-3:] を混同するコロンが無いと 1 件だけ、あるとリストが返ります。前者の戻り値は辞書や文字列、後者はリストです。
スライスで詳しく読むitems[0] を読む[] に対する items[0] は IndexError です。len(items) で件数を確かめるか、スライスを使えば避けられます。
record[name] と書くと、name という変数を探しに行ってしまいます。変数が無ければ NameError です。キーを直接書くときは record["name"] とクオートで囲みます。
record["memo"] は KeyError になります。in で確認するか、キーが無くても落ちない record.get("memo") を使います。get は無いとき None を返します。
[] はリスト、{} は辞書です。{"name", "price"} のようにコロンを書き忘れると、辞書ではなく別のものになります。
for key, value in record と書くitems() を付けずに 2 つ受け取ろうとすると ValueError になります。辞書をそのまま回すと 1 周ごとにキーが 1 つ返るだけなので、2 つに分けられません。
+ でつなぐときに int を混ぜるkey + " = " + value は value が 120 だと TypeError です。f-string を使うか str(value) で変換します。
ループの中で + "\n" を付けると最後の行にも改行が残ります。join を使えばこの問題は起きません。
key に呼び出し結果を渡すkey=lambda r: r["price"]() のようにかっこを足すと関数ではなくなります。渡すのは関数そのもので、呼ぶのは sorted の仕事です。
r["prise"] のようなつづり違いは KeyError になります。エラーメッセージに無いキー名がそのまま出るので、見比べれば分かります。
-1 などで表そうとする降順は reverse=True です。sorted(...)[::-1] でも見た目は逆順になりますが、同じ値どうしの並びが変わってしまうことがあるため reverse を使います。
return set(...) と書くと、順番が定まらないうえに型もリストではありません。テストの比較も安定しません。必ず sorted でリストにしてから返します。
{} と書く{} は空の 辞書 です。空の集合は set() です。type({}) を試すと dict と出ます。
集合には順番が無いので categories[0] は TypeError になります。取り出したいなら先にリストへ変換します。
append を書いてしまう[result.append(r) for r in records] と書くと、append の戻り値である None が並んだリストができます。内包表記の中では append は要りません。
if の位置を間違える絞り込みの if は for の うしろ です。前に書く [a if 条件 else b for ...] は第 3 章の三項演算子で、絞り込みではなく値の切り替えになります。
= を 1 つしか書かないif record["category"] = "食費" は SyntaxError です。比較は == で、代入の = とは別ものです。
3 つ入った組を name, price = pair で受けると ValueError になります。メッセージは too many values to unpack です。数を合わせるか、pair[0] のように番号で取ります。
("りんご") と書くこれはただの文字列です。要素が 1 つのタプルには ("りんご",) とカンマが必要です。
pair[1] = 150 は TypeError です。変えたいなら新しいタプルを作り直すか、最初からリストを使います。
KeyError存在確認をせずに足そうとすると、そのカテゴリが初めて出てきた時点で止まります。in で確認するか、無いとき 0 を返す totals.get(category, 0) を使います。
0 のキーを入れてしまう5 カテゴリ分を先に 0 で埋めると、使っていないカテゴリまで結果に残ります。この仕様では入れません。
値は int です。str にすると "120180" のような連結になり、金額として使えなくなります。
close を書き忘れる開いたまま放置するとファイルが解放されず、書き込んだ内容が反映されないことがあります。次のレッスンで学ぶ with を使えばこの問題は消えます。
読むつもりで "w" を渡すと、ファイルの中身が空になってしまいます。"r" が読み、"w" が書きです。
read の結果をそのまま return すると、行のリストではなく 1 本の文字列が返ります。splitlines を通してからにします。
write に改行を期待するwrite("りんご") を 2 回呼んでも 2 行にはならず、りんごりんご と横につながります。改行は自分で書きます。
write にリストを渡すf.write(["a", "b"]) は TypeError になります。write が受け取れるのは文字列だけなので、先に join で 1 本にします。
f.write(120) も TypeError です。金額を書くときは str(120) で文字列にしてからにします。
with open(path, "w") as f の行末にはコロンが必要で、次の行はインデントします。忘れると SyntaxError になります。
f を使うブロックを抜けたあとに f.read() を呼ぶと ValueError になります。閉じたファイルは読めません。
with を使ったのに close も書く動きはしますが二重に閉じる意味のない行です。with を使ったら close は書きません。
str を付け忘れる",".join([name, price, category]) のように int を混ぜると TypeError になります。エラーメッセージは sequence item 1 と、何番目が悪いかを教えてくれます。
項目の区切りは ","、行の区切りは "\n" です。両方 join で書くので取り違えやすい部分です。
"りんご, みかんセット" のようにカンマを含む品名を保存すると、読み戻したときに項目数が合わなくなります。このコースでは品名にカンマを使わない前提にしますが、実務では標準ライブラリの csv を使ってこの問題を避けます。
except の範囲を広げすぎるexcept: とだけ書くと、あらゆる例外を飲み込んでしまいます。本当のバグまで隠れて原因が追えなくなるので、受け止める種類は必ず書きます。
try の範囲を広げすぎるtry のブロックに関係ない処理まで入れると、どこで失敗したのか分からなくなります。危ない 1 行だけを囲むのが基本です。
except で何もしないexcept ValueError: pass と書くと、失敗を無視して先に進みます。今回は 0 を返すという後始末があるので、pass は使いません。
except の種類を間違えるexcept ValueError では FileNotFoundError を受け止められません。関係のない種類を書くと、例外はそのまま素通りして止まります。
try に with を入れ忘れるwith の外側だけ try で囲んでも意味がありません。open が呼ばれる場所が try の中に入っている必要があります。
None の判定に == を使うinitial_text is not None と書きます。空文字も「渡された」として扱いたいので、if initial_text: では空文字が省略扱いになってしまいます。
int に戻し忘れる金額が "120" のまま返ると、見た目は似ていてもテストは落ちます。辞書に入れる前に int(price) を通します。
保存の形式は name、price、category の順です。読み戻すときも同じ順で受け取ります。キー名は第 7 章から一貫して英語です。
records を返すテストは通ってしまうかもしれませんが、保存できたことの確認になりません。必ずファイル経由で戻します。
つくる:保存機能で詳しく読むcommand[0] 以外を先に取り出してしまう["summary"] は要素が 1 つしかないので、いきなり command[1] を読むと IndexError になります。操作名で分岐したあと、その枝の中で取り出します。
同じ品名が 2 件あるとき、remove を繰り返すと両方消えます。1 件だけ消すなら break でループを抜けます。
add のたびに合計を作り直すと、delete のあとにずれます。最後にまとめて足すほうが確実です。
remaining を合計の途中で計算するループの中で引くと、最後の記録を足す前の値が残ります。引き算はループを抜けたあとです。
月間レポート機能で詳しく読むtop に金額を入れてしまうtop は品名の文字列です。top_price は探すための作業用の変数で、戻り値には入れません。
使いすぎたときの remaining は負の数のままにします。-500 と出るからこそ、いくら超えたのかが分かります。
in の向きを逆にするrecord["name"] in keyword と書くと、品名がキーワードに含まれるかという逆の判定になります。ほとんどの場合ずっと偽になります。
None を返す呼び出し側が for で回した瞬間に TypeError になります。空のリストを返します。
remove で絞り込もうとすると、元の記録が壊れます。新しいリストに集めるのが安全です。