openpyxlで数式セルが None になるときは、data_only=True を付けるだけで解決するとは限りません。この指定は保存済みの計算結果を読み取るもので、数式を再計算する機能ではないためです。数式の有無と保存された値を別々に調べ、結果がないセルをゼロに置き換えず、集計を止めて確認します。

対象は、Pythonの変数・条件分岐を少し学び、Excelの読み込みから数式結果の取得へ進んでいる社会人です。この記事では通常の .xlsx の単純な数式を扱います。マクロ、外部参照、複雑な配列数式を自動で再現する手順ではありません。まず小さな教材で「読めること」と「計算済みであること」の違いをつかみましょう。

仕様・講座情報の確認日:2026年10月8日。openpyxl公式の数式の説明と公式チュートリアルを参照しました。公式stable文書の表示は3.1.3、本稿の教材の実行環境はPython 3.12.14・openpyxl 3.1.5です。掲載結果は後述する自作教材の実行結果であり、実在の業務帳票を試した記録ではありません。

data_onlyでNoneになる理由を、数式と保存値から切り分ける

Excelで見える数字と、ファイルに保存された結果を区別する

画面に200と見えているセルでも、内部には「120と80を足す」という数式が入っている場合があります。数式の文字列と、その式を計算した結果は、同じ情報ではありません。Excelなどの計算機能を持つアプリが結果を保存していれば、読み取り側はその保存値を使えます。しかし数式を書き込んだだけのファイルには、結果が用意されていないことがあります。

例えばPythonから =SUM(B2:B3) を書き込むと、式自体は記録できます。そこに数値120と80があるとしても、openpyxlが計算して200を保存するわけではありません。公式文書は数式を評価しないことを明記しています。式の書き込みが成功したので、次の読み込みで結果も取れる、と考えるところが初学者には分かりにくい点です。

data_only=False なら数式を読み、data_only=True なら保存された結果を読む、という役割で考えます。Trueという名前から「数字だけを自動で作る機能」と解釈しないでください。今回の問題では両方の読み取りを並べると、どちらの段階に情報が足りないかが見えます。基本の読み書きから整理したい場合は、PythonでExcel自動化を学ぶ順番も参考になります。

ファイルの来歴も調査の材料です。「人がExcelで保存した」「Pythonで生成した」「別のサービスから出力した」「入力値だけを後から変更した」では、保存された結果に期待できる状態が違います。Noneを見た時点でライブラリの不具合と決めず、誰が何を更新し、どのファイルを読み取ったのかを確認します。

とくに、手元の画面で開いているファイルとコードで指定したファイルが別でないかを先に見てください。共有フォルダーの旧版、ダウンロードした複製、別名保存したファイルなどが混在すると、設定を変えても話が合いません。パスとシート名、対象セルの番地を固定し、調査の対象を一つに絞ることが近道です。

Noneを見つけたら、空欄・数式・ゼロを別の状態として残す

Noneが返ったセルは、元々空欄なのか、数式があるのに結果が保存されていないのかで対応が変わります。値の読み取りだけを見ると、この二つが同じNoneに見えることがあります。数式側の読み取りも併用し、元のセルに式があるかを調べてください。通常の単純な数式では、セルの data_type が f かどうかが確認材料になります。

ゼロも注意が必要です。「まだ計算されていない」状態を0へ変えると、本当に0だったセルとの区別がなくなります。集計表の合計が小さくなっても処理はエラーを出さずに進むため、かえって気付きにくくなります。value or 0 のような短い書き方は、未確認の情報を勝手に確定させる用途には使いません。

PythonではFalseも条件判定で偽になります。またboolはintの派生型なので、数値かを調べる条件の書き方によっては、Falseが数値0として扱われます。教材ではboolを先に判定し、数値ゼロと区別します。これはExcelの数式キャッシュに限らず、表の値を処理するときの基礎練習にもなります。

同じ集計列にあっても区別したいセルの状態
読み取った状態そのまま合計に使うか確認すること
数式あり・保存値None保留計算済みの結果を用意する工程が必要か
数式なし・空欄保留または仕様で扱いを決める未入力なのか、該当なしなのか
数値の0数値として区別して扱う業務上もゼロでよいか
False金額列なら保留論理値が混じった理由
Excelエラー・文字列保留式のエラーや列の型の違い

空欄をゼロとする仕様が正当に決まっている業務もあります。その場合でも、数式結果が未取得という状態まで同じルールへ含めないことが大切です。入力仕様が決まっていないなら、担当者へ確認する項目として残します。欠損と重複の扱いを学ぶときも、CSVの欠損・重複を確認する教材で同じように状態を分けています。

8セルの架空教材で、計算結果がない状態を再現する

次の教材は、実在の請求書や受講料一覧を使いません。120、80、合計の式、ゼロ、空欄、False、エラー表示、数字に見える文字列を、B2からB9へ順番に置きます。教材上の120と80を足した期待値は200です。ただし、その数式の結果がファイルへ保存されていないため、値用の読み取りではNoneになります。

BytesIO はメモリ内でファイルのようにデータを扱うための仕組みです。今回は教材をメモリに保存して読み直すので、作業フォルダーの帳票を上書きしません。すでにopenpyxlが利用できる学習環境で実行してください。ライブラリ導入や職場のPCへのインストールは、勤務先のルールに従って別途判断します。

from io import BytesIO
from openpyxl import Workbook, load_workbook

book = Workbook()
sheet = book.active
sheet.title = "練習"
items = [120, 80, "=SUM(B2:B3)", 0,
         None, False, "#DIV/0!", "200"]
for row, value in enumerate(items, start=2):
    sheet.cell(row=row, column=2, value=value)

buffer = BytesIO()
book.save(buffer)
payload = buffer.getvalue()
formulas = load_workbook(BytesIO(payload), data_only=False,
                         read_only=True)
values = load_workbook(BytesIO(payload), data_only=True,
                       read_only=True)
try:
    for row in range(2, 10):
        addr = f"B{row}"
        original = formulas["練習"][addr]
        stored = values["練習"][addr]
        print(addr, original.data_type,
              repr(original.value), repr(stored.value))
finally:
    formulas.close()
    values.close()

実行すると、B4は元の型が f、数式が '=SUM(B2:B3)'、保存値が None になります。B5は数値0、B6は空欄のNone、B7はFalse、B8は '#DIV/0!'、B9は文字列の '200' です。引用符の有無や型を見るために repr() を使っています。数字に見える文字列と、本当の数値を画面だけで判断しないためです。

この結果から言えることは、今回の条件では数式だけを書いて保存しても計算結果が作られなかった、という範囲です。すべてのExcelファイルでNoneになるわけではありませんし、逆にNoneが出ないことがすべての計算の正しさを示すわけでもありません。教材で再現した条件と、手元のファイルの条件を照らし合わせてください。

練習では、B2を130へ変えると合計の期待値が210になることも紙に書いてみます。その後コードを実行しても、数式を計算していないという性質は変わりません。入力値を変えたことと、結果を更新したことが別工程だと説明できれば、この教材の最初の目的は達成できています。

型を確認し、保留すべきセルの理由を表示する

次は、読み取った値の分類です。数字でないものをすべて削除するより、どのセルがどんな理由で保留されたのかを残したほうが、入力側と相談しやすくなります。今回の分類は金額などの数値列を想定した学習用です。論理値を扱う表なら、Falseを保留する必要がない場合もあります。

def classify(original, stored):
    if original.data_type == "f" and stored.value is None:
        return "数式結果なし"
    if stored.data_type == "e":
        return "Excelエラー"
    if stored.value is None:
        return "未入力"
    if isinstance(stored.value, bool):
        return "論理値"
    if isinstance(stored.value, (int, float)):
        return "数値"
    return "文字列など"

formulas = load_workbook(BytesIO(payload), data_only=False,
                         read_only=True)
values = load_workbook(BytesIO(payload), data_only=True,
                       read_only=True)
try:
    statuses = []
    for row in range(2, 10):
        addr = f"B{row}"
        status = classify(formulas["練習"][addr],
                          values["練習"][addr])
        statuses.append(status)
        print(addr, status)
    print("全セルが数値:", all(s == "数値" for s in statuses))
finally:
    formulas.close()
    values.close()

このコードは先ほどの教材に続けて実行します。分類結果は「数値」がB2、B3、B5の3セル、「数式結果なし」がB4の1セル、「未入力」がB6の1セル、「論理値」がB7の1セル、「Excelエラー」がB8の1セル、「文字列など」がB9の1セルです。最後の「全セルが数値」はFalseになります。この状態で全8セルの合計を確定させない、という判断を学びます。

ここでは数値型と判定できることだけを検査しています。非常に大きな数、単位が違う数、業務上許さない負数などまで判定していません。数値型の中に浮動小数点の特殊な値が含まれる用途なら、その検査も追加する必要があります。型を通過したら業務として正しい、と一段飛ばしにしないでください。

文字列の200を数値へ直したい場合も、理由を調べてから変換します。金額列へ単位や桁区切りが混じっているなら、pandasでカンマ・円付き金額を検証する方法のように、書式と変換前の値を残す工程が必要です。今回の分類へ文字列の自動変換まで一度に足すと、何を直したのかが見えにくくなります。

Noneが消えても、更新済みとは限らないと理解する

保存値が200として読めれば、少なくとも数値が保存されていることは分かります。しかし、その値が今の入力値に対応するかまでは分かりません。入力が変わったあとに計算・保存の工程が適切に行われていなければ、以前の結果を読む可能性もあります。数値が返ったという条件だけを完了基準にしないでください。

調査メモには、「入力値の更新日時」「計算と保存を行った工程」「読み取ったファイル」「対象シート・セル」「独立して計算した期待値」を残します。システムの日時だけで最新と決めるより、どの操作で何を変えたかを追えることが重要です。古い計算結果を意図的に作るために、ファイル内部を書き換える必要はありません。

合計式がB2とB3を参照しているなら、二つの数値を別に足して期待値を照合できます。式を見て別の範囲を参照していたら、計算エンジンの問題とは別に、そもそもの式の誤りを疑います。隠れている行を含めるか、フィルター後の行だけか、丸める時点はいつか、といった業務の条件も確認対象です。

  1. 同じファイルから数式と保存値を読む。
  2. 数式あり・保存値なしなら集計を保留する。
  3. 保存値があれば、入力の更新と計算工程を確認する。
  4. 小さな期待値と照合してから後続処理へ進む。
数式の存在、保存値の存在、結果の正しさを三段階で調べる、編集部作成の確認図。

例えばB2が120、B3が80の教材で200を期待する確認は、入力を130、80に変えた教材では210を期待する確認へ変わります。出力を見てから期待値を書き直すと、誤りも正解に見えてしまいます。先に期待値を決め、入力の条件が変わったらその理由と一緒に検査を更新してください。

元ファイルを守りながら結果を確認し、学習の次の一歩を選ぶ

再計算が必要なら、計算を担当するアプリと工程を決める

数式結果が未保存なら、計算できるアプリで結果を用意するか、必要な計算をPython側で別に実装するかを決めます。Excelで開く方法を選ぶとしても、ただ開いて閉じるだけで目的を満たすとは考えません。計算方式、入力の更新、式の対象、保存先を確認し、計算後の複製ファイルを改めて読み取る工程を作ります。

既存のExcelが利用できる職場なら、担当者に再計算と保存の確認を依頼し、更新済みの複製を受け取るという方法もあります。手元で使用できる計算アプリやファイルの互換性は環境ごとに異なります。本稿ではExcelやLibreOfficeでの実行を検証していないため、いずれの環境でも同じ結果になるとは案内しません。

Python側で計算するなら、今回のような単純な二つの数値の合計から始めます。Excelの式の文字列を eval() へ渡して実行する方法は使いません。関数、参照、型、日付、丸めの規則などは、ExcelとPythonでそのまま一致するとは限らないからです。明確に決めた計算だけを通常のPythonコードとして書き、入力と出力を照合します。

数式結果を用意する方法を選ぶための比較
状況進め方事前に調べる条件
既存の複雑なExcel帳票担当アプリで計算・保存後に複製を読む互換性、外部参照、更新手順
小さな固定の足し算Pythonで別に計算して照合する参照範囲、単位、丸めの規則
計算環境が不明処理を止めて作成者へ確認ファイル生成元、計算できる担当者
大量の定期処理運用設計を先に相談する実行権限、失敗時の停止、結果の記録

今回はどの有料サービスも契約せず、外部の計算サービスへの帳票送信も行っていません。読者も、小さな教材を動かすためだけに新しい有料ツールを導入する必要はありません。既存環境でどこまで確認できるかを把握してから、実務に必要な手段を検討すると、学習目的と導入判断を分けやすくなります。

読み取り調査と編集処理を分け、保存し直さない

調査用に data_only=True で読んだブックを、そのまま元の名前で保存することは避けます。数式そのものを読み込む用途ではないため、式を維持したい編集処理と混ぜないほうがよいからです。また、公式チュートリアルは保存時に既存ファイルが警告なしで上書きされることや、すべての要素が保存されるとは限らないことも注意しています。

学習では「入力」「調査結果」「出力予定」を別にします。入力は変更しないコピーを使い、調査結果にはセルの番地・元の式・保存値・状態を残します。編集が必要なら、数式を読める設定で別工程を作り、異なる出力名を使って、元のファイルと比較します。保存に成功しただけで帳票全体が維持されたとは判断しません。

読み取りの範囲も限定します。今回の教材はB2からB9までと決めています。実務で全シート・全セルを無条件に走査して、不要な情報をログへ出す必要はありません。自分に必要な列と件数を先に定めれば、処理時間だけでなく、出力された記録を人が確認する負担も小さくできます。

調査が終わった読み取り用ブックは閉じます。ファイルを閉じる処理と保存する処理は別です。閉じたから計算結果が更新された、という考え方にはなりません。掲載コードの finally は途中で問題が起きても読み取りを終了するためのもので、数式を計算する工程ではありません。

職場の実データを生成AIへ見せてコードを相談する場合は、ファイルの中身やパス、氏名、取引先名などをそのまま貼らず、承認された範囲の情報を使います。今回の教材だけでもNoneの問題は再現できます。原因の切り分けに不要な実データを増やすより、同じ現象が起こる最小の架空例を用意してください。

集計を通す条件と、止める条件を先に書く

自動化の学習では、正常な表を処理できたことだけを成果にしがちです。しかし今回の題材なら、数式結果がない表を止められることも大切な成果です。金額の集計なら、「必要なセルが数値」「未確認の数式結果がない」「期待する件数がそろう」「単位が一致する」など、通すための条件を言葉で書きます。

逆に止める条件は、数式あり・保存値なし、Excelのエラー、想定外の文字列、論理値の混入、シート名や列の不一致などです。止まったときに単にエラー番号だけを出すより、対象セルと理由を表示したほうが担当者は次の確認へ進めます。機密情報が含まれる業務では、表示する値の範囲も決めます。

教材の8セルでは全セルが数値ではないので、合計を確定させません。「使えるセルだけの小計」を別の目的で出すなら、それが何セルの小計か、保留が何セルあるかを明記します。200という合計式と、その参照元120・80を同時に合計すると二重計上にもなるため、単に数字のセルを全部足す方法は使いません。

この二重計上は、欠損の問題とは別です。Noneを直したあとにB4が200になっても、B2・B3・B4をまとめて足してよいとは限りません。表の中で明細と合計がどの行にあるかを理解し、集計対象を指定する必要があります。データ同士を結合するときの件数増加は、pandasのmergeで行数が変わる検証でも扱っています。

最終的な調査メモの例は、「入力8セル、数値3、保留5。B4に数式結果なし。全体の集計は未実施。作成担当へ計算・保存工程を確認」となります。未実施を失敗として隠すより、どの条件が満たされていないかを具体的に伝えるほうが、誤った数値を次の工程へ渡すことを防ぎやすくなります。

独学と個別指導を、質問できる内容で比較する

今回の教材で、式を読む設定と値を読む設定の違いを説明でき、Falseと0を分ける理由も分かるなら、独学で小さな例を増やしてみる段階です。ファイルを変更する前に、自分で期待値と停止条件を作れるかを確認します。基本の表処理を一通り学びたい人は、社会人向けPython・CSV分析の始め方へ進めます。

一方、関数や例外処理がまだ分からず、動いたコードを説明できない状態なら、基礎へ戻るか個別指導を比較する選択もあります。「このエラーを代わりに直してもらう」だけでなく、「なぜ計算されないのか」「どの値を保留するのか」を質問できる学習先かを見てください。Excelの自動化だけが目的なら、広いAI開発講座が必要かも考えます。

2026年10月8日確認のPython Winner公式のPythonマスターは、変数・関数・デバッグ・例外処理・ライブラリ・ファイル入出力などを扱い、受講料297,000円税込、受講期間3ヶ月、30分のマンツーマン18回を掲載しています。検討しやすいのは、Pythonの基礎からコードを説明する力を学びたい人です。今回のopenpyxlの個別課題がそのまま添削対象になるかは、公開内容だけでは確定できません。

別の学習先も比較したいのは、数式キャッシュの一つの問題だけを解決したい人、Excelの操作だけを学びたい人、すでにPythonの基礎が身に付いている人です。短い公式資料の読解や単発の質問で目的を満たす場合もあります。受講前には、追加費用を含む支払総額、質問できる範囲、予約・振替、手元の環境への対応を確認します。

この数式結果の問題を学ぶ方法の比較
方法向いている状況限界・確認点
公式資料と自作教材小さなコードを読んで試せる実務帳票の仕様は自分で確認
Python基礎の個別指導型・条件分岐・ファイル処理から整理したい個別Excel演習への対応範囲を確認
帳票担当者との確認入力や計算工程が不明プログラミング学習とは別に必要

無料の受講相談を利用する場合は、「通常のxlsxで式があるのに保存値がNoneになる教材を説明したい」と目的を伝えると、講座との適合性を確認しやすくなります。実際の帳票を送る前に、架空例だけで相談できるかを尋ねます。相談のために受講を決める必要はなく、説明を受けてから目的・予算・時間に合うか判断してください。

広告:【PythonWinner】の公式案内で学習範囲と無料受講相談を確認できます。

よくある質問と、次に行う小さな練習

まずは教材をそのまま実行し、B4のNoneとB6のNoneが違う理由を説明してください。次にB5の0をFalseと区別できるかを確かめ、最後にB2の入力を変えて期待値を書き直します。数式結果がない状態を残したまま、誤って集計を確定させないことまでが練習です。

よくある質問

Q. data_only=Trueなら数式を計算してくれますか?
A.

計算しません。保存済みの数式結果を読む指定です。結果が保存されていなければNoneになることがあるため、式用と値用の読み取りを比べます。

Q. Noneはゼロに置き換えてもよいですか?
A.

数式結果が未取得なのか未入力なのかを調べる前に、ゼロへ置き換えないでください。業務仕様で空欄をゼロとする場合も、数式結果なしとは区別します。

Q. Excelでは数字が見えるのにPythonではNoneなのはなぜですか?
A.

読み取っているファイルが同じか、計算結果を保存したか、生成・更新工程がどうなっているかを確認します。画面の表示だけでファイルに結果が保存されたとは判断しません。

Q. 数値が取得できたら最新の結果ですか?
A.

数値が保存されていることと、最新の入力に対応することは別です。入力の更新、計算・保存工程、小さな期待値との照合を確認します。

Q. 調査用ブックを保存して元ファイルへ戻してよいですか?
A.

調査中は元ファイルを保存し直さない運用にします。data_onlyで読む調査と、数式を維持する編集を分け、編集が必要なら複製へ別名で出力して比較します。