エラーを読んで直す|Pythonの最初の1行

このページはプロモーション(広告)を含みます。

Pythonの最初の1行(初級・読む25分+演習40分)

この授業のゴール

仕事でどう使うか

プログラムを書く時間の多くは、エラーを読んで直す時間です。経験者が速いのは、間違えないからではなく、エラーから原因を絞り込む手順を持っているからです。エラーは「失敗」ではなく、「どこで・何が・なぜ」を教える手がかりです。

路線 この授業の使いどころ
DS(データサイエンティスト) 前処理の途中で止まったとき、型・欠損・件数0のどれが原因かを絞る。結果の数字がおかしいのに止まらないとき、期待値と照合して気づく
ML(機械学習エンジニア) 学習が途中で止まったとき、ログのエラーから入力データの形・型の不一致を特定する。訓練を再実行する前に、小さなデータで再現して直す
DX(DX推進・AI企画) 自動化したワークフローが止まったとき、原因が「入力の不備」か「プログラムの不具合」かを切り分け、現場へ入力ルールを返す。エラー内容を、開発者に正確に伝える
RS(研究者) 解析が止まった状況を、小さな再現例にして共有する。エラーなしで出た数値が、手計算と合うかを検証して再現性を守る

この授業は、PY01 の最後です。ここまでの4つのエラー(TypeError ValueError NameError SyntaxError)を、読み方の手順として整理します。

前提

次の内容を使います。

この授業では、エラーをわざと起こして読む練習を、画面内で行います。わざとエラーを起こすコードは、文字列に入れて実行し、結果だけを表示する形にしてあります。

身近な状況:赤い文字が出て、何から見ればよいかわからない

税込・割引後の価格を求める短いコードを書きました。実行すると、長い英語の文字が出て止まりました。

Traceback (most recent call last):
  File "sample.py", line 3, in <module>
    discounted = price * (100 - rate_pct) // 100
TypeError: unsupported operand type(s) for -: 'int' and 'str'

初心者がやりがちなのは、次の3つです。(1) 全部が読めないので閉じてしまう、(2) とりあえず書き換えて再実行する、(3) 検索して出てきた直し方をそのまま貼る。どれも、原因がわからないまま「たまたま動いた」状態になりかねません。

この表示は、実は決まった形の伝票です。上から順に、「どこで止まったか」を示し、最後の1行が「何が起きたか(種類: メッセージ)」を示します。上の例を読むと、(1) sample.py の 3行目で止まった、(2) エラーの種類は TypeError(型が合わない)、(3) 内容は「- の左が int、右が str なので引き算できない」です。つまり 100 - rate_pct の rate_pct が文字列のままだとわかります。

直感的な説明:エラーは「伝票」、読む順番は下から

荷物の配送伝票には、「どこで」「何が」「なぜ」止まったかが書かれます。Pythonのエラー表示も同じです。

知りたいこと 見る場所 例
何が起きたか 最後の行(種類: メッセージ) TypeError: unsupported operand ...
どこで止まったか 最後の File ..., line N の行と、その下に出るコードの1行 line 3、discounted = ...
そこへ至る経路 上の方の File ..., line N(呼び出しの順) 小さなコードでは1つだけのことが多い

読む順番は下から上です。最後の1行で「種類とメッセージ」をつかみ、次にその上の「行番号とコード」を見ます。上の方にある長い経路は、最初は読み飛ばして構いません。

もう1つ、大事な点があります。エラーが出た行は、Pythonが止まった行であって、間違いを書いた行とは限りません。上の例では、止まったのは3行目ですが、原因は「rate_pct を文字列のまま代入した」行(たとえば2行目)にあります。この区別が、この授業の中心です。

正確な定義:エラーの種類と、読み取りの手順

エラーには大きく3種類あります。

  1. 構文エラー(SyntaxError など):コードが文法として読めない。実行が始まる前に見つかり、プログラム全体が1行も実行されない。
  2. 実行時エラー(例外):文法は正しいが、実行中に不可能な操作が起きた。止まるまでの行は実行されている。
  3. 論理エラー(バグ):エラーは出ないが、結果が意図と違う。Pythonは教えてくれないので、人が期待値と照合して見つける。

主な例外と、典型的な原因は次のとおりです。

種類 意味 典型的な原因 例
SyntaxError 文法が間違っている 括弧・引用符の閉じ忘れ、全角記号 print('a'
IndentationError 字下げ(行頭の空白)が不正 if の下で字下げしていない if True: の次が字下げなし
NameError 知らない名前を使った 代入前に使用、タイプミス、大文字小文字 print(prce)
TypeError その型にその操作はできない 文字と数の演算、関数に渡す値の型 '1000' * 0.9
ValueError 型は合っているが、値が不適切 変換できない文字列 int('1,980')
ZeroDivisionError 0で割った 件数0の平均 total / 0

読み取りの手順(デバッグの基本):

  1. 最後の行から、種類とメッセージを読む。
  2. 行番号とその行のコードを見る。
  3. その行で使っている変数の値と型を print(値, type(値)) で確かめる(数値・文字列・真偽値)。
  4. 「なぜその値になったか」を、上の行へ遡って原因を探す。
  5. 1か所だけ直して、再実行する。別のエラーが出たら、1に戻る。
  6. エラーが消えたら、結果を期待値と照合する(エラーなし ≠ 正しい)。

構文エラーの行番号は、Pythonが「おかしい」と気づいた位置です。括弧の閉じ忘れでは、開いた行を指すこともあれば、次の行を指すこともあり、バージョンでも違います。「その行と、その1つ前の行」を見るのが安全です。

メッセージの文面は、Pythonのバージョンで変わることがあります。この教材の実行環境はPython 3.12系です。他の環境で文面が違っても、「種類・行番号・値」を読む手順は同じです。

段階付き例題:3つの誤りを、1つずつ直す

問題:価格 '1,980' 円、割引 '15' %の注文で、割引後の価格を整数(切り捨て)で求める。次のコードにはエラーが重なっている。1つずつ直して、1683 を出す。

この授業では、わざとエラーを起こすコードを run_sample(コード) に渡して、エラーの種類・メッセージ・行番号を表示します。def は「処理に名前を付ける」書き方で、「関数と引数」(有料版) で学びます。いまは、run_sample を「文字列のコードを実行して、結果を表示する道具」として使ってください。

ステップ1:最初の実行。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

code1 = '''price = '1,980'
rate_pct = '15'
discounted = price * (100 - rate_pct) // 100
print(discounted)
'''
run_sample(code1)

出力は TypeError: unsupported operand type(s) for -: 'int' and 'str' (3 行目: discounted = price * (100 - rate_pct) // 100) です。最後の行から、種類は TypeError、100 - rate_pct の引き算で、右側が str だとわかります。原因は2行目で、rate_pct を整数にしていないことです。

ステップ2:1か所だけ直す。rate_pct を int('15') にして再実行します。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

code2 = '''price = '1,980'
rate_pct = int('15')
discounted = price * (100 - rate_pct) // 100
print(discounted)
'''
run_sample(code2)

出力は TypeError: unsupported operand type(s) for //: 'str' and 'int' (3 行目: ...) です。エラーの種類は同じでも、メッセージが変わりました。今度は // の左側が str、つまり price * 85 の結果('1,980' を85回繰り返した文字列)に対して切り捨て除算をしようとして止まっています。前のエラーを直したからこそ見えた、次の問題です。

ステップ3:もう1か所直す。price も整数にします。ただし、カンマを除かずに int('1,980') と書くとどうなるか。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

code3 = '''price = int('1,980')
rate_pct = int('15')
discounted = price * (100 - rate_pct) // 100
print(discounted)
'''
run_sample(code3)

出力は ValueError: invalid literal for int() with base 10: '1,980' (1 行目: price = int('1,980')) です。今度は ValueError で、止まったのは1行目です。メッセージの末尾に、変換できなかった値 '1,980' が示されています。

ステップ4:カンマを除いて直す。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

code4 = '''price = int('1,980'.replace(',', ''))
rate_pct = int('15')
discounted = price * (100 - rate_pct) // 100
print(discounted)
'''
run_sample(code4)

出力は 1683 と エラーなし です。

ステップ5:結果を検算する。$1980 \times 0.85 = 1683$ で、手計算と一致します。エラーが消えても安心せず、期待値と照合します。ここまでの流れは、「読む → 1か所直す → 再実行」の繰り返しです。

操作して理解する:エラーの種類を予想する

次の5つの小さなコードは、それぞれ別の種類のエラーになります。実行前に、種類と行番号を予想してください。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

samples = {
  'A': "price = 1000\nprint(prce)\n",
  'B': "total = 300\ncount = 0\naverage = total / count\n",
  'C': "value = int('abc')\n",
  'D': "if True:\nprint(1)\n",
  'E': "print('合計: ' + 1000)\n",
}
for key in samples:
    print(key, end=' ')
    run_sample(samples[key])

答えは次のとおりです。

記号 種類 行 読み方
A NameError 2 prce という名前が未定義。price のタイプミス
B ZeroDivisionError 3 count が 0。件数が0の平均は計算できない
C ValueError 1 'abc' は整数に変換できない
D IndentationError 2 if の下の print(1) が字下げされていない
E TypeError 1 文字列と整数の連結

for key in samples: は、辞書(キーと値の組を持つ型)の項目を順に取り出して繰り返す書き方です(「辞書と集合」(有料版))。

自分で A のコードの prce を price に直すと、エラーなしで 1000 が出ます。エラーの文面にある名前(prce)と、自分が代入した名前(price)を見比べるのが、NameError の直し方の基本です。

もう1つ、エラーが出ない誤りを検出する道具として assert(アサート)を使います。assert 条件, 'メッセージ' は、条件が False のときだけ AssertionError で止まります。

discounted = 1980 * (100 - 15) // 100
assert discounted == 1683, '割引後価格が 1683 になっていません'
print('検算OK', discounted)

出力は 検算OK 1683 です。期待値を先に決めてコードに書いておけば、後で式を直したときに、誤りに気づけます。この教材の演習の自動採点も、同じ assert で書かれています。

発展:呼び出しの経路を読む/再現例を作る/警告との違い

経路(トレースバック)の読み方:関数(処理のまとまり。「関数と引数」(有料版))の中でエラーが起きると、表示には複数の File ..., line N が並びます。上が最初の呼び出し、下が実際に止まった場所です。

import traceback

def tax_included(price, rate_pct):
    return price * (100 + rate_pct) // 100

def total_of(raw):
    return tax_included(raw, 10)

try:
    total_of('1,980')
except Exception:
    print(traceback.format_exc().splitlines()[-1])

出力は TypeError: unsupported operand type(s) for //: 'str' and 'int' です。'1,980' * 110 は文字列の繰り返しとして成立してしまい、続く // 100 で初めて止まります。止まったのは tax_included の中ですが、間違った値を渡したのは呼び出し側です。経路の上へ遡って、値がどこで文字列になったかを探します。

最小の再現例:他の人に質問するときは、(1) エラーの全文、(2) エラーを起こす最小のコード、(3) 期待した結果と実際の結果、(4) Pythonのバージョン、を示すと、早く解決します。社外の掲示板などでは、社内のデータ・個人情報・認証情報を含めません。

警告(Warning)との違い:Warning は「このまま動くが、将来問題になり得る、または意図と違うかもしれない」という通知で、処理は止まりません。無視せず、メッセージを読んで判断します。

エラーの握りつぶしの危険:except Exception: pass のように、すべてのエラーを何もせず無視すると、本当の原因が見えなくなります。想定する例外の種類を限定し(except ValueError:)、記録・通知・代替処理のどれかを必ず行います。

よくある失敗

失敗1:エラーが出た行だけを直そうとして、原因の行を見落とす

誤り:止まった行(3行目)を何度書き換えても直らない。原因は1行目で price を文字列にしたことにある。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

bad = '''price = '1000'
rate = 0.1
discounted = price * (1 - rate)
'''
run_sample(bad)

good = '''price = int('1000')
rate = 0.1
discounted = price * (1 - rate)
print(discounted)
'''
run_sample(good)

出力は、TypeError: can't multiply sequence by non-int of type 'float' (3 行目: discounted = price * (1 - rate)) と、900.0、エラーなし です。止まったのは3行目ですが、直したのは1行目です。

直し方:止まった行の変数について print(price, type(price)) で値と型を確かめ、その値が作られた行まで遡る。「型が違う」エラーでは、値の出どころが原因です。

失敗2:エラーを消すためだけの修正をして、結果を壊す

誤り:ZeroDivisionError を避けるために、分母に 1 を足してしまう。

total = 300
count = 3
average_wrong = total / (count + 1)
average_right = total / count
print(average_wrong, average_right)

count = 0
if count == 0:
    average = None
else:
    average = total / count
print(average)

出力は 75.0 100.0、None です。count + 1 と書けばエラーは出なくなりますが、平均が間違った値になります。しかも、エラーが消えたので誰も気づきません。件数0の平均は、数学的に定義できないので、0 ではなく None(値がない)で表し、集計の仕様で「平均なし」として扱うのが誠実です(数値・文字列・真偽値 の「0と欠損」)。

直し方:エラーの意味(0で割れない)を理解し、例外的な場合の扱いを仕様として決めて書く。エラーを消すことと、正しい結果を出すことは別です。

失敗3:エラーなしで動いたので、正しいと思い込む

誤り:計算が最後まで動いたので、結果の確認を省略する。

price = 1980
discounted = price * 85 // 100
with_tax = discounted * 1.1
print(with_tax)
print(with_tax == 1851)

try:
    assert with_tax == 1851, f'税込価格が 1851 になっていません(実際: {with_tax})'
except AssertionError as e:
    print('AssertionError:', e)

出力は 1851.3000000000002、False、AssertionError: 税込価格が 1851 になっていません(実際: 1851.3000000000002) です。* 1.1 を使うと小数が残ります。エラーは出ないので、assert を書かない限り、小数の端数に気づきません。切り捨てた整数 1851 が期待値なら、discounted * 110 // 100 と書く必要があります(演算子と型変換)。

直し方:期待値(手計算の結果)を決めて、assert で自動的に照合する。型も assert isinstance(with_tax, int) で確かめます。

失敗4:字下げ(インデント)と、全角スペースの混入

誤り:字下げの数がそろっていない。または、見た目では区別できない全角スペースが混ざる。

import traceback

def run_sample(code):
    # 学習用の道具:文字列のコードを実行し、エラーの種類・メッセージ・行番号を1行で表示する
    try:
        exec(compile(code, 'sample.py', 'exec'), {})
    except SyntaxError as e:
        print(f'{type(e).__name__}: {e.msg}  ({e.lineno} 行目)')
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)[-1]
        line = code.splitlines()[tb.lineno - 1].strip()
        print(f'{type(e).__name__}: {e}  ({tb.lineno} 行目: {line})')
    else:
        print('エラーなし')

run_sample("if True:\nprint(1)\n")
run_sample("x = 1\n  y = 2\n")
run_sample("x = 1\n y = 2\n")

出力は、IndentationError: expected an indented block after 'if' statement on line 1 (2 行目)、IndentationError: unexpected indent (2 行目)、SyntaxError: invalid non-printable character U+3000 (2 行目) です。最初は字下げが足りない、2番目は余計な字下げ、3番目は全角スペース(U+3000)を含むため、見た目は同じでも別の種類のエラーになります。

直し方:字下げは半角スペース4つにそろえる。エディタの設定で、タブと全角スペースを可視化する。エラーメッセージの行番号とともに、その行の行頭の文字を確かめます。

まとめ

用語

トレースバック(traceback)

エラー発生時に表示される、どの行でどの種類のエラーが起きたかを示す記録。最後の行に種類とメッセージが出る。

構文エラー(syntax error)

コードが文法として読めないエラー。実行が始まる前に見つかる。

論理エラー(バグ)(logic error / bug)

エラーは出ないが、結果が意図と違う誤り。人が期待値と照合して見つける。

アサーション(assertion)

assert 条件, メッセージ で、条件が偽のときに AssertionError で止める確認の仕組み。

よくある質問

エラーメッセージが英語で、読めません。

全文を訳す必要はありません。最初は、TypeError、ValueError、NameError のような種類名と、メッセージに出てくる変数名・値('1,980' など)だけを拾います。種類名ごとの典型原因はこの授業の表にまとめました。翻訳ツールを使うときも、社内データ・個人情報を貼らない点に注意します。

エラーが複数出たら、どこから直しますか?

一番最初に出たエラー、つまり、実行が止まった1つだけを直します。1か所を直して再実行すると、次のエラーが見えます。まとめて直すと、どの変更が効いたのかがわからなくなります。

検索して見つけた直し方を、そのまま使ってよいですか?

メッセージの意味を自分で説明できる範囲で使います。説明できないまま貼ると、たまたま動いても、別の入力で壊れます。使うときは、期待値の照合(assert や手計算)を必ず行います。

`try` / `except` で全部のエラーを捕まえれば、止まらなくなりますか?

止まらなくなりますが、原因も見えなくなります。想定できる例外(ValueError など)だけを指定して捕まえ、記録・通知・代替処理を行います。想定外のエラーは止めて、人が確認する方が安全な場面が多くあります。

`assert` は仕事のコードでも使いますか?

使います。ただし、利用者の入力の検証には向きません(assert は無効にする設定があるため)。入力の検証には if と例外を使い、assert は「プログラムの内部で成り立つはずの前提」や、テストの期待値の確認に使います。

エラーを読めるようになる近道はありますか?

わざとエラーを起こして、種類と行番号を予想し、実行して確かめる練習です。この授業の操作の節のように、5種類のエラーを自分で起こしてみると、実務で出会ったときに落ち着いて読めます。

確認問題

問1 エラー表示を読むとき、最初に見るべき場所はどこか。

  1. ① エラーが出たら読まず、書き換えて再実行する
  2. ② 最後の行(エラーの種類とメッセージ)
  3. ③ 一番上の行
  4. ④ どこでもよいので、とにかく長い行
解答と解説

正解:②

最後の行に、何が起きたかを示す「種類: メッセージ」があります。次に、その上の行番号と該当行のコードを見ます。

問2 NameError: name 'prce' is not defined の原因として、最も可能性が高いのはどれか。

  1. ① price のタイプミス、または代入前に使った
  2. ② prce が大きすぎる数だった
  3. ③ print が使えない環境だった
  4. ④ 文字列を数に変換できなかった
解答と解説

正解:①

NameError は、その名前をまだ知らない(代入されていない)ときに起きます。代入した名前と、エラーの名前を1文字ずつ比べます。

問3 price を1行目で文字列として作り、3行目 price * (1 - rate) で TypeError が出た。直すべき場所として最も適切なのはどれか。

  1. ① エラーが出た3行目を削除する
  2. ② どの行を直しても同じ
  3. ③ price を文字列として作った1行目(数に変換する)
  4. ④ エラーが出た3行目の * を + に変える
解答と解説

正解:③

止まった行と、原因の行は違うことがあります。型のエラーでは、値が作られた行(出どころ)まで遡って直します。

問4 int('1,980') を実行したときのエラーの種類として正しいものはどれか。

  1. ① TypeError
  2. ② NameError
  3. ③ SyntaxError
  4. ④ ValueError
解答と解説

正解:④

引数は文字列で型は合っていますが、中身(カンマ付き)が整数として読めないので ValueError です。replace(',', '') で除いてから変換します。

問5 エラーなしで最後まで実行でき、with_tax に 1851.3000000000002 が入った。期待値は整数 1851 だった。この状況の説明として正しいものはどれか。

  1. ① 実行環境の不具合である
  2. ② 論理エラー(意図と違う計算)で、Pythonは教えてくれない。期待値と照合して見つける
  3. ③ エラーが出ていないので、問題はない
  4. ④ Pythonが自動的に整数へ直してくれる
解答と解説

正解:②

論理エラーは、プログラムが文法的に正しく動いても結果が意図と違う状態です。期待値を決め、assert や手計算で確かめます。

問6 件数0の平均を求めるときの ZeroDivisionError を避けるため、平均を 0 とすることにした。この方法の最も大きな問題はどれか。

  1. ① 0 では割り算の結果が整数にならない
  2. ② 問題はなく、むしろ最も安全な方法である
  3. ③ 「平均が0円」という意味の値になり、「平均を計算できない」状態と区別できなくなる
  4. ④ 0 を代入すると、必ず文法エラーになる
解答と解説

正解:③

エラーを消すための値でも、意味が変わると、集計やレポートの判断を誤らせます。None(値なし)で表し、仕様として「算出不可」を明記します。

次に学ぶこと

PY01 の6授業は、これで終わりです。次は、条件によって処理を分ける 「ifと条件式」(有料版)(ifと条件式)と、繰り返し 「forとrange」(有料版)(forとrange)へ進みます。この授業の ZeroDivisionError の場合分けも、if で書きました。

この授業の確認問題だけでモジュールの習得とはしません。PY01 の総合テストと、別の条件の自力課題(税込価格と割引の計算、誤った型の修正)で判定します。例外処理の詳細は 「例外と後処理」(有料版) で扱います。

キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。

アプリを無料で開く有料版と無料版の違い