はじめに
プログラミング
プログラミングは、どんなに複雑に見えるソフトウェアやアプリも、元をたどれば、基本的なパーツの組み合わせでできています。
①変数(The Central Component): 値を入れておく「箱」のパーツ
②変数の操作(Modifying the Blocks): 「箱」の中身を加工するパーツ
③条件分岐(Conditional Logic): 「もしも~なら」を判断する賢いパーツ
④繰り返し処理(Repetitive Actions): 同じ作業を何度も行うパワフルなパーツ
⑤処理をまとめる(Creating Composite Blocks): いくつかの積み木をセットにする便利なパーツ
⑥外部とのデータの受け渡し(Connecting to the World) 作ったものを外の世界と繋ぐパーツ
変数(プログラムの中心となる「値を入れる箱」)
すべてのプログラミングの土台となる、最も重要で中心的なパーツが「変数」です。これは、一言でいえば「値が入っている箱」のようなものです。プログラムは、この主役である「箱」に入っている値を使ったり、加工したり、どこかに保存したり、画面に表示したりすることで動いているのです。変数には、数値を入れる箱、文字を入れる箱、複数の箱をまとめた箱など、様々な種類があります。すべては「データをしまう」という役割で共通しています。
プログラミングとは突き詰めれば「『変数』という箱に入っている値を、どこかに保存したり画面に表示させたりしているだけ」とも言えるのです。すべては、この「箱」をどう扱うかという点から始まります。具体例として、「100」という数値を入れたり、「サイトウ」という文字列を入れたりできます。なぜこれが重要かというと、この「箱」があるからこそ、プログラムはデータを記憶し、後から利用できるからです。
変数の操作(箱の中身を「加工」する)
「箱」(変数)が準備できたら、次はその中身にアクションを起こし、具体的な「動き」を作り出すパーツの出番です。これは、「箱」の中身をそのまま使うのではなく、計算したり、文字を付け加えたりして、目的に応じて「加工」する必要があります。
・100という値が入った箱と、200という値が入った箱を足し算する。
・"和田"という文字が入った箱に、"さん"という文字を付け加えて"和田さん"にする。
このように、データに「アクション」を起こす。なぜこれが重要かというと、データは加工されて初めて意味を持つからです。このパーツを使いこなすことで、単なる情報の羅列を、意味のある結果へと変えることができます。
条件分岐(「もしも〜なら」の判断)
このパーツは、プログラムに判断力を持たせる「進路を決める分岐レール」のような役割を担います。「もしも〇〇だったら△△する」というように、特定の条件に応じて処理の流れを変えるのです。これは、先ほど紹介した「変数」の中身をチェックし、その結果に応じて「変数の操作」を実行する、といった形で他のパーツと組み合わせて使われることがほとんどです。
例を挙げると、「入力されたパスワードが保存されているものと違ったら、『パスワードが違います』というエラーメッセージを表示する」といった処理です。プログラムに知性を与えるのが、この「条件分岐」のパーツです。これは、「もしも〇〇だったら××する」というように、ある条件によって、次に行う処理の道筋を変える役割を持ちます。プログラミングの世界では、これをifと呼びます。なぜこれが重要かというと、このパーツがあるおかげで、プログラムは状況に応じた賢い振る舞いができるようになるからです。
繰り返し処理(同じ作業を「自動化」する)
面倒な作業をコンピュータに任せるための、非常にパワフルなパーツが「繰り返し処理」です。その名の通り、ある処理を何度も何度も自動で繰り返すことができます。プログラミングの世界では、これをforと呼びます。「変数の操作」をこの「繰り返し」のパーツで囲むことで、退屈な作業が一瞬で終わるのです。
・クラス全員分の繰り返し処理を行うという積み木の中に、「名前に『さん』を付ける」という変数の操作のパーツを入れる。
たったこれだけで、コンピュータが一瞬でクラス全員分の作業を終わらせてくれます。
これは、面倒な作業を自動化してくれる「組立ライン」のようなパーツです。
処理をまとめる(作業を「パッケージ化」する)
これまで紹介した「変数の操作」「条件分岐」「繰り返し処理」などを組み合わせて作った一連の作業を、一つの「パッケージ」としてまとめるパーツです。プログラムが長くなってくると、「あれ、どこで何をやっているんだっけ?」と混乱しがちです。そんな時に「整理整頓」のパーツです。これは、これまで紹介してきたパーツの組み合わせ(一連の動き)を、一つの便利なセットパーツとしてまとめておくためのもので、プログラミングでは、関数やクラスといったものがこれにあたります。
例えば、「5教科の点数(変数)から平均点を計算する」という一連の動きを一つの「平均点計算セット」としてまとめておけば、いつでも簡単に呼び出して使えます。このパーツを使う最大のメリットは、「プログラムが分かりやすくなり、同じことを何度も書かなくて済む」ことで、同じコードを2回書いたら、まとめるチャンスです。作品が大きくなればなるほど、この整理整頓の積み木が重要になります。
外部とのデータの受け渡し(プログラムの「外」と繋がる)
自分が作ったプログラムと「外部の世界」を繋ぐためのパーツです。このパーツがあるから、あなたの作ったプログラムはコンピュータの中だけで完結せず、よりパワフルで実用的なものになります。ゲームのセーブデータを「ファイル」に保存したり、SNSで送ったメッセージが友達のスマホ(他のコンピューター)に届いたりするのも、このパーツのおかげです。この「外部」には、主に以下のようなものがあります。
・データベース: 大量のデータをためておく場所
・ファイル: テキストファイルや画像ファイルなど
・他のコンピューター: インターネットを通じた通信相手
最も大切なことは、これらがそれぞれ独立しているのではなく、積み木のように自由に組み合わせていく作業こそがプログラミングだということです。プログラミングは、難しい命令文をひたすら暗記する作業ではありません。 それは、「どの順番で、どのパーツを使えば自分のやりたいことが実現できるか」を考えることです。
例えば、「クラス全員の名前に『さん』を付けたい」と考えたとき、頭の中で次のように組み立てていくのです。
1. まず、全員の名前を保存しておく場所が必要だ。よし、「変数」を使おう。
2. 次に、それぞれの名前に「さん」という文字を付け加える加工が必要になる。これは「変数の操作」の出番だ。
3. この作業をクラスの人数分やらなきゃいけない。40回も同じコードを書くのは大変だから、「繰り返し処理」の組立ラインに乗せて自動化しよう!このように、手元にある6種類のパーツを眺めながら、完成形を思い描きながら組み立てていく、創造的な作業なのです。「『ログイン機能を作りたいな。それなら、まずユーザー名を入れる『箱』が必要で、次にパスワードが合っているか『もしも』のパーツで判断して…』というように、作りたいものから逆算して積み木を選ぶパズルのような思考プロセスこそが、プログラミングの醍醐味です。
・まず、どんなパーツ(積み木)があるのかを知ること。
・次に、作りたいものに合わせて、どの順番でどのパーツを使えばいいか考えること。
| 積み木の種類 | 積み木としての役割 | プログラミングでの役割 |
| 変数 | 値を入れておく「箱」 | データの保存 |
| 変数の操作 | 「箱」の中身を加工する | データの計算や変更 |
| 条件分岐 | 「もしも」を判断し道筋を変える | 条件に応じた処理の実行 |
| 繰り返し処理 | 同じ作業を何度も自動で行う | 反復処理の自動化 |
| 処理をまとめる | 動きをセットにして部品化する | コードの整理と再利用 |
| 外部との連携 | 外の世界とデータをやりとりする | ファイル操作や通信 |
Pythonとは
3つの主要な特徴
「シンプルさ」「手軽さ」「拡張性」という3つの本質的な特徴
・シンプルで読みやすい構文
Pythonには、「The Zen of Python」という設計思想をまとめた有名な言葉があり、その一つにこう書かれています。「複雑であるよりは、シンプルであるほうが良い (Simple is better than complex)」という思想です。この設計思想は、コードの隅々にまで浸透しています。この思想に基づき、Pythonは他の多くの言語と比較して、文法が非常にシンプルに作られています。これにより、開発者は覚えるべきルールが少なく、プログラムの記述量を抑えることができます。結果として、誰が読んでも理解しやすく、メンテナンス性に優れたコードを書くことが可能になります。さらに、Pythonは「動的型付け言語」であることも、そのシンプルさに大きく貢献しています。これは、プログラム内で扱うデータの種類(「型」と呼ばれます)を、開発者がコード上で明示的に宣言する必要がない、という特性です。他の多くの言語では必須となる変数の「型宣言」という手続きを省略できるため、より直感的にプログラミングの思考プロセスに集中できるのです。
・すぐに実行できる手軽さ(スクリプト言語)
Pythonは「スクリプト言語」に分類されます。これは、記述したプログラムをコンピュータがすぐに解釈し、実行できるタイプの言語を指します。簡単に言えば、書いたコードをすぐに動かして結果を確認できるということです。プログラムを実行する前の専門的な準備作業を必要としないため、「コードを書いて、実行する」というサイクルを高速に回すことができます。
・豊富な「ライブラリ」による高い拡張性
Pythonの強力さを支えるもう一つの柱が、膨大な数の「ライブラリ」の存在です。ライブラリとは、特定の機能を実現するために作られた「プログラムのパーツ」の集まりです。これを「便利な道具箱」と考えると分かりやすいでしょう。世界中の開発者によって作成・公開されている豊富なライブラリ群は、Pythonの戦略的な価値を飛躍的に高めています。データ分析、Web開発、画像処理といった複雑な機能が必要になった場合でも、開発者はそれらをゼロから実装する必要がありません。多くの場合、目的に合ったライブラリを見つけ、自分のプログラムに組み込むだけで、高度な機能を迅速に実現できるのです。この高い拡張性により、開発者は本来の目的に集中し、効率的にプロジェクトを完成させることができます。
これら3つの特徴、すなわち「シンプルさ」「手軽さ」「拡張性」は互いに作用し合い、Pythonを「学びやすく、かつ強力な開発ツール」たらしめているのです。例えば、豊富なライブラリ(拡張性)を、シンプルな構文と手軽な実行環境で素早く試せるからこそ、Pythonは学習と実践のサイクルを高速に回せるのです。
「対話モード」と「ファイル実行」(Windows)
インタラクティブモード(対話モード)
対話モードは、まるでPythonと直接「会話」をするような感覚でコードを実行できる方法です。あなたが1行命令(コード)を入力すると、Pythonがすぐにその結果を返してくれます。
コマンドプロンプトで python と入力してEnterキーを押すと、対話モードが始まります。>>> という記号が表示されたら、それが開始の合図です。この >>> の後ろにコードを打ち込んでいきます。このモードを終了するには exit() と入力してEnterキーを押すか、「Ctrl」キーを押しながら「Z」キーを押します。
このモードは特定の場面で役立ちます。
簡単な計算を試したい時
例えば、5 + 10 のような簡単な計算結果をすぐに知りたい時に便利です。
短いコードの動きをサッと確認したい時
print のような基本的な命令(関数)がどのように動くかを、1行だけ書いて試すことができます。
メリット として、入力したコマンドの結果がすぐに返ってくることです。これにより、ちょっとしたアイデアやコードの断片を、手間をかけずに素早くテストできます。デメリットは 入力したコードは保存されません。対話モードを終了すると、書いた内容はすべて消えてしまいます。そのため、後で再利用したい本格的なプログラムを作るのには向いていません。
ファイル実行
ファイル実行は、プログラムのコード全体を1つの「レシピ」のようにテキストファイルに書き留めておき、そのレシピをPythonに渡して一度に実行してもらう方法です。この「レシピファイル」には、.py という拡張子(ファイル名の最後につける目印)を付けるルールがあります。これにより、コンピュータはそのファイルがPythonのプログラム(スクリプト)であることを認識できます。こちらがPythonプログラミングの標準的な方法です。保存して後でまた使いたいプログラムや、何度も編集・改良したい本格的なアプリケーションを作りたい場合に、必ずこの方法を使います。
メリット: コードをファイルとして保存・編集・再利用できることが最大の強みです。これにより、一行のテストでは作れないような、複雑で実用的なアプリケーションを構築することが可能になります。
デメリット: 対話モードのように直接コードを打つのではなく、「①メモ帳などでファイルを作成し、②.pyという名前で保存、③コマンドプロンプトでそのファイルの場所まで移動して実行する」という一連の手順が必要になります。
| 特徴 (Feature) | 対話モード (Interactive Mode) | ファイル実行 (File Execution) |
| 主な用途 | 簡単な計算や短いコードの動作確認 | 本格的なプログラムの作成 |
| コードの保存 | 保存されない | ファイルとして保存される |
| 手軽さ | 非常に手軽 | ファイル作成の手間が少しある |
| おすすめの場面 | 「ちょっと試したい」時 | 「プログラムを作りたい」時 |
「対話モード」と「ファイル実行」(Mac)
対話モード(インタラクティブモード)
①ターミナル(コマンドプロンプト)を開き、pythonと入力してEnterキーを押します。
②次のような記号が表示されたら、対話モードが開始した合図です。
>>> の後ろにPythonのコードを入力すると、即座に結果が返ってきます。
>>> の後に 5 + 10 と入力>Enterキーを押した瞬間に、計算結果の 15 が表示されます。
printという命令を使って文字を表示させてみます。
こちらも同様に、print命令を実行した結果がすぐ下に表示されます。
終了方法
対話モードを抜けるには、exit() または quit() と入力してEnterキーを押します。また、お使いの環境によってはショートカットキーも利用できます(macOSやLinuxでは Control + D、Windowsでは Control + Z を押した後にEnterキー)。
ファイル実行
ファイルを作成する
プログラムを記述するためのファイルを作成します。Pythonのプログラムファイルには、慣例として拡張子 .py をつけます。ここでは例として code.py というファイル名にします。
コードを記述する
作成した code.py ファイルの中に、実行したいPythonコードを記述します。
ファイルを実行する
ターミナルで、以下のコマンドを入力して実行します。ファイルに書かれたコードが上から順に実行され、結果が表示されます
再利用できること: 一度書いたプログラムを、いつでも好きな時に呼び出して再度実行できます。
複数の命令をまとめて実行できること: 何十行、何百行にもわたる複雑な処理をまとめて実行できます。
基本文法
3つの基本ルール
文の終わり、コメント、インデント
命令文の終わりは「改行」がルール
プログラムとは、コンピュータに対する「命令文」の集まりです。そして、一つの命令がどこで終わり、次の命令がどこから始まるのかを明確に示すことは、コードの構造を決定づける極めて重要な第一歩です。この区切りが曖昧だと、コンピュータは私たちの意図を正しく解釈できません。多くのプログラミング言語では、文の終わりにセミコロン(;)のような特別な記号を記述する必要がありますが、Pythonのルールは非常にシンプルです。日本語の文章の終わりに「。」を打つのと同じように、Pythonでは「改行」そのものが文の終わりを意味します。
例えば、画面に文字を表示するprint()という命令を複数実行したい場合、以下のように単純に改行して書き連ねるだけで問題ありません。
# 各行が独立した命令文として解釈される
print("こんにちは、Python!")
print("プログラミングの学習を始めましょう。")
特別な記号が不要なため、コードがすっきりと読みやすくなるのがPythonの大きな魅力の一つです。ただし、例外もあります。一つの命令文が非常に長くなってしまう場合、コードの可読性を保つために途中で改行したくなることがあります。その際は、行の末尾にバックスラッシュ (\) を置くことで、「この文はまだ続いています」とPythonに伝えることができます。
# 1行の長い計算式を、バックスラッシュを使って見やすく改行する例
total_price = 100 + 200 + 300 + 400 + 500 \
+ 600 + 700 + 800 + 900 + 1000
print(total_price)
バックスラッシュの前と後ろは、Pythonによって一つの文として解釈されます。
コードのメモ書き
プログラミングにおいて、コメントは非常に重要な役割を果たします。コメントとは、プログラムの実行には影響を与えない、開発者のための「メモ書き」です。後からコードを見返した自分自身や、チームの他の開発者が「このコードブロックは何を目的としているのか」「なぜこのような処理が必要なのか」を素早く理解するための、重要な手がかりとなります。Pythonでは、コメントを記述する方法が2種類用意されています。
①1行のコメント
1行単位の短いコメントや、特定の行のコードを一時的に無効化したい場合には、ハッシュ記号(#) を使います。#を記述した位置からその行の終わりまでが全てコメントとして扱われ、プログラムを実行する際には完全に無視されます。
# この行は全体がコメントなので、実行されません。
print("文字1") # print命令の後ろに、処理内容を補足するコメントを記述できます。
# print("文字2") # この行は行頭に#があるので、実行されません。
上の例を実行すると、文字2は表示されません。このように、行頭に置くか、コードの途中に置くかで柔軟に使い分けることができます。
②複数行のコメント
関数の説明や複雑な処理の注釈など、複数行にわたってコメントを記述したい場合、各行の先頭に#を付けるのは少し手間です。また、複数のコード行をまとめて一時的に無効化したい場合もあります。このようなケースでは、ダブルクォーテーション3つ (""") または シングルクォーテーション3つ (''') でコメントにしたい範囲を囲みます。
"""
以下の2行は、ダブルクォーテーション3つで囲まれているため、
まとめてコメントアウトされ、実行時には無視されます。
print("文字1")
print("文字2")
"""
print("この行だけが実行されます。")
この方法を使えば、広範囲のコードブロックを効率的にコメント化したり、詳細なドキュメントをコード内に直接記述したりすることが可能です。
インデントの重要性
インデントとは、行頭に入れる空白のことです。多くのプログラミング言語では、インデント(字下げ)は単にコードを読みやすくするための「見た目の整形」であり、必須ではありません。しかし、Pythonにおいてインデントは、コードのまとまりや階層構造を示すための、構文上の強制ルールです。これはPythonの思想を象徴する、極めて重要な特徴です。インデントを付けるには、行の先頭に「半角スペース4つ」または「タブ1つ」を挿入します。(一般的には、半角スペース4つが推奨されています。)では、なぜインデントが必要なのでしょうか。特定の構文、例えばdef(関数を定義する際に使用)などを使った場合、Pythonはその処理がどこからどこまでの範囲を指すのかを知る必要があります。その「範囲」を示すのがインデントです。
# 'my_function'という名前の処理のまとまりを定義する例
def my_function():
# 以下の2行はインデントされているため、my_functionの一部だと解釈される
print("これはインデントされたブロック内の処理です。")
print("この行も同様です。")
# この行はインデントされていないため、my_functionの外の処理だと解釈される
print("これはブロックの外の処理です。")
(注:ここではdefの詳しい機能を知る必要はありません。「特定の構文の下では、インデントされたコードブロックが必要になる」という点だけを理解してください。)
インデントにおける最も重要な注意点は、以下のルールです。
必要な場所では必ずインデントを付け、不要な場所では絶対につけてはいけない。
もし、インデントが必要ない場所でインデントを記述してしまうと、Pythonはそれを構文エラーとして認識し、IndentationErrorというエラーを発生させます。
# 正しくない例
print("文字1")
print("文字2") # ← この行にはインデントが不要なため、エラーが発生する
この厳格なルールによって、誰が書いても同じような見た目の、クリーンで読みやすいコードが生まれやすくなるのです。これらの基本ルールが、Pythonコード全体の骨格を形成します。
データ型と変数
変数と型
変数 (variable)
変数は、データを 値を入れておくための箱 のようなものです。
データを格納する仕組み
プログラミングとは、本質的には「データを処理すること」です。そのデータを効率的かつ正確に扱うために不可欠なのが「変数」という概念です。これは、データに意味のある名前を与え、プログラム全体で一貫して管理するための、最も基本的な戦略です。この戦略なくして、複雑なソフトウェアを構築することは不可能です。
値の代入方法
変数に値を入れる操作を「代入」と呼びます。Pythonでは、代入演算子である=記号を使って値を変数に代入します。構文は「変数名 = 値」となります。
例えば、xという名前の変数に「あああ」という文字列を代入してみましょう。結果を画面に表示するために、print()という組み込み関数を使用します。
# 変数xに文字列「あああ」を代入
x = "あああ"
# print()関数を使って変数xの値を表示
print(x)
# 実行結果
あああ
値の上書き
一度代入した変数には、新しい値を代入して上書きすることができます。
例えば、変数に'〇〇'を代入した後で'△を代入すると、変数の内容は△''になります。△'△
変数は最後に代入された値を保持します。
x = "あああ"
print(x)
# 実行結果
あああ
# 次に、同じ変数xに「トマト」を代入してみます。すると、xの中身は新しい値で上書きされます。
x = "いいい"
print(x)
# 実行結果
いいい
未定義変数のエラー
プログラミングにおいて非常に重要なルールがあります。それは、変数は使用する前に必ず値を代入(定義)しなければならない、というものです。もし、一度も値が代入されていない変数をいきなり使おうとすると、PythonはNameErrorというエラーを発生させます。これは「その名前の変数は定義されていません」というプログラムからの警告です。
# yという変数はまだ一度も定義されていない
print(y)
NameError: name 'y' is not defined
このエラーは、プログラムが知らない名前の「箱」を開けようとして戸惑っている状態だと考えると分かりやすいでしょう。
変数とメモリの関係
コンピュータのメモリと変数がどう関連しているかを理解することが重要になります。より厳密に言うと、変数は値そのものを保持しているのではありません。 変数は、データ(値)が実際に保存されているコンピュータのメモリ上の場所と結びつけられています。プログラムが扱うデータは、すべてコンピュータのメモリと呼ばれる領域に保存されます。変数は、その広大なメモリの中の「どこに」データが置かれているかを指し示すための目印なのです。コンピュータのメモリを「広大な倉庫」、そこに保管されている様々なデータを「荷物(値)」だと想像してください。 変数とは、その倉庫内の特定の荷物に付けられた「名札(ラベル)」のようなものです。x = "あああ"という命令は「”あああ”という荷物にxという名札を付ける」行為であり、print(x)は「xの名札が付いた荷物の中身を見せろ」という指示に他なりません。
型 (type)
データの種類を定義する
プログラミングにおいて、なぜデータに「型」という概念が存在するのでしょうか。それは、データがどのような種類であるかによって、コンピュータがそれをどう扱うべきか(例えば、足し算できる数値なのか、それとも連結すべき文字列なのか)が変わってくるからです。「型」は、プログラムの正確性と効率性を担保するための、極めて重要な設計思想なのです。
型の定義
型(データ型)とは、その名の通り「データの種類」を定義するものです。Pythonには様々な型が存在しますが、まずは代表的な2つを覚えましょう。
文字列型 (string):
"あああ" のように、文字の並びを表すデータ。引用符("や')で囲んで表現します。
整数型 (integer):
2021 のように、小数点のない数値を表すデータ。
動的型付け
プログラミング言語によっては、変数を使い始める前に「この変数は整数専用です」といったように、型をあらかじめ宣言する必要があります。しかし、Pythonは動的型付け言語と呼ばれ、その必要がありません。Pythonでは、変数に値を代入する際に、その値の種類に応じて自動的に変数の型が決定されます。
例えば、同じ変数xでも、代入する値によってその型は動的に変わります。
# "いいい"(文字列)を代入すると、xは文字列型になる
x = "いいい"
# 21(整数)を代入すると、同じxが今度は整数型になる
x = 21
このように、型が自動で決まるのは非常に便利です。
演算子の挙動変化
同じ記号(演算子)であっても、扱うデータの型によって全く異なる結果を生むという事実です。この挙動を理解することは、予期せぬバグを防ぎ、意図通りのプログラムを書くための鍵となります。
+演算子の比較
最も分かりやすい例が+演算子です。この記号は、扱うデータの型によってその意味が根本的に変わります。
| データ型 | + 演算子の意味 | コード例 | 実行結果 |
| 文字列型 | 文字列の結合 | print("Hello" + "World") | HelloWorld |
| 整数型 | 数値の加算 | print(10 + 5) | 15 |
上の表が示す通り、+は文字列に対しては2つの文字列を繋げる「結合」として機能し、整数に対しては数学的な「加算」として機能します。この挙動の違いから、極めて重要な結論が導き出せます。 それは、自分が今扱っている変数の型を正確に意識しないと、意図しない結果(バグ)を招く危険性があるということです。
例えば、数値の10と5を足し算したい場合を考えます。もし、これらの数値が誤って文字列型として扱われると、意図しない結果が生まれます。
# 型を意識しないと...
# '10'と'5'は数値ではなく文字列型
num1_str = "10"
num2_str = "5"
# +演算子は文字列の「結合」として機能してしまう
print(num1_str + num2_str)
# 実行結果
105
期待した15ではなく、"105"という文字列が生成されてしまいました。型の不一致が引き起こすこのような問題は、プログラムの信頼性を大きく損なう原因となります。
リスト
リストの重要性
Pythonプログラミングの中核をなすデータ構造。リストは、複数のデータを一つの変数にまとめて効率的に管理するための、非常に基本的かつ強力なツールです。
例えば、「クラスの名簿」に、もしリストという概念がなければ、生徒一人ひとりの名前をstudent1 = '田中', student2 = '鈴木', student3 = '佐藤'のように、個別の変数で管理しなければならず、非常に煩雑です。しかしリストを使えば、meibo = ['田中', '鈴木', '佐藤']のように、meiboという一つの変数で名簿全体をスマートに管理できます。この効率性こそが、リストの最大のメリットです。さらに、リストの真価はforループのような繰り返し処理と組み合わせることで発揮されます。名簿の全員に同じ処理を順番に行う、といった操作が簡単に実現できるのです。
リストの定義と作成方法
リストとは、いくつかの値を一つにまとめて管理するためのデータ構造です。リストを構成する一つ一つの値のことを「要素(element)」と呼びます。例えば、先ほどのクラス名簿の例では、「田中」「鈴木」「佐藤」という3つの文字列がそれぞれ要素となります。
Pythonでのリスト作成
Pythonでは、リストは角括弧 [] を使って非常に簡単に作成できます。以下に基本的な作成パターンを示します。
基本的なリスト
角括弧 [] の中に、カンマ , で区切って要素を並べます。
異なるデータ型を含むリスト
リストの要素は、同じデータ型である必要はありません。文字列と数値を混在させることも可能です。
空のリスト
最初は要素が何もない、空のリストを作成することもできます。後からプログラムで要素を追加していく場合によく使われます。
リストの長さを確認する
リストに含まれる要素の総数(リストの長さ)を知りたい場合は、len() 関数を使用します。
x = ['田中', '鈴木', '佐藤']
print(len(x))
# 出力結果: 3
len() 関数はリストの要素数を返すため、この場合は3が出力されます。
インデックス
リスト内の各要素には、その位置を示す住所のような番号が自動的に割り振られています。この番号を「インデックス(index)」と呼びます。インデックスを正しく理解することが、リストから特定のデータを正確に取り出すための鍵となります。
インデックスの基本ルール
インデックスには、一つだけ絶対に覚えなければならない重要なルールがあります。それは「0から始まる」ということです。
リスト: ['a', 'b', 'c', 'd']
インデックス:
0 1 2 3
最初の要素のインデックスは1ではなく0です。これは多くの初心者が間違いやすいポイントなので、常に意識するようにしてください。ちなみに、これは多くのプログラミング言語における慣習で、コンピュータがメモリ上のデータの位置を「基準点からの距離(オフセット)」で管理していることに由来します。最初の要素は基準点から距離が0、ということですね。
要素の取得方法
特定の要素を取得するには、リスト変数名[インデックス番号]という構文を使用します。
補足:ここでの「構文(こうぶん、英: Syntax)」とは、「Pythonという言語における、正しい書き方のルール(文法)」のことです。もっと言うと、「この順番で、この記号を使って書かないと動きませんよ」という決まりごとを指しています。具体的に、list_name[index] という構文が指している「ルール」は以下の3点です。
list_name[index] という書き方は、以下のパーツで構成されています。
list_name (リストの名前)ここには変数の名前が来ます。今回のコードでは x がこれに当たります。
[ ] (角括弧/ブラケット)これが最も重要です。 Pythonで「リストの中身を取り出す」という命令をする時は、必ずこの記号で囲むというルール(構文)があります。( ) や { } ではダメです。
index (インデックス番号)カッコの中には、取り出したい場所の「数字」が入ります。今回は 1 です。これらを組み合わせると、実際のコードである x[1] になります。
x = ['a', 'b', 'c', 'd']
# インデックス1の要素(2番目の要素)を取得する
element = x[1]
print(element)
# 出力結果: 'b'
このように、角括弧 [] の中に取得したい要素のインデックスを指定することで、簡単に値を取り出すことができます。
ネガティブインデックス
Pythonのリストには、末尾から要素を指定できる「ネガティブインデックス」という便利な機能があります。
最後の要素: -1
最後から2番目の要素: -2
最後から3番目の要素: -3 …
x = ['a', 'b', 'c', 'd']
# 最後の要素を取得する
last_element = x[-1]
print(last_element)
# 出力結果: 'd'
# 最後から2番目の要素を取得する
second_to_last = x[-2]
print(second_to_last)
# 出力結果: 'c'
リストの長さが分からなくても、常に最後の要素を-1で取得できるため、非常に実用的です。
リストの基本操作
プログラムを実行している間に、状況に応じてリストの内容を変化させることは、実用的なアプリケーションを開発する上で不可欠なスキルです。
最も頻繁に使用されるデータの「追加」「削除」「結合」という3つの基本操作
要素の追加:append()
リストの末尾に新しい要素を追加するには、append() メソッドを使用します。
補足:メソッドとは、「そのデータ(変数)の後ろに『ドット』をつけて呼び出す、そのデータ専用の便利機能」のことです。
x = ['a', 'c', 'e']
print(x) # 出力結果: ['a', 'c', 'e']
# 'g'という要素をリストの末尾に追加
x.append('g')
print(x) # 出力結果: ['a', 'c', 'e', 'g']
要素の削除:remove()
リスト内から、指定した値を持つ要素を削除するには、remove() メソッドを使用します。(インデックスではなく、削除したい要素の値を直接指定します。)
x = ['a', 'b', 'c', 'd']
print(x) # 出力結果: ['a', 'b', 'c', 'd']
# 'b'という要素を削除
x.remove('b')
print(x) # 出力結果: ['a', 'c', 'd']
注意点として、もし同じ値の要素がリスト内に複数存在する場合、remove()は最初に見つかった要素(インデックスが最も小さい要素)のみを削除します。
リストの結合
2つのリストを1つにまとめる(結合する)には、主に2つの方法があります。それぞれの挙動の違いは、どのような状況でどちらを使うべきかという実践的な判断に繋がるため、しっかり理解することが重要です。
extend() を使った結合
extend() メソッドは、あるリストに別のリストの全要素を追加します。この操作は元のリスト自体を変更します。元のリストを直接変更するため、特に巨大なリストを扱う際に新しいリストを作成するメモリコストを節約できるという利点があります。
x = ['a', 'b']
y = ['c', 'd']
print(f"操作前のx: {x}") # 出力結果: 操作前のx: ['a', 'b']
# xリストにyリストの要素を追加
x.extend(y)
print(f"操作後のx: {x}") # 出力結果: 操作後のx: ['a', 'b', 'c', 'd']
print(f"yは変更されない: {y}") # 出力結果: yは変更されない: ['c', 'd']
+ 演算子を使った結合
+ 演算子を使うと、2つのリストを連結して、新しいリストを生成します。元のリストはどちらも変更されません。そのため、関数の引数として受け取ったリストなど、元のデータを破壊したくない場合に安全で推奨される方法です。
x = ['a', 'b']
y = ['c', 'd']
# xとyを結合して、新しいリストzを作成
z = x + y
print(f"新しいリストz: {z}") # 出力結果: 新しいリストz: ['a', 'b', 'c', 'd']
print(f"xは変更されない: {x}") # 出力結果: xは変更されない: ['a', 'b']
print(f"yは変更されない: {y}") # 出力結果: yは変更されない: ['c', 'd']
スライス
スライスとは、リストの一部を切り出して、新しい小さなリストを作成する機能です。基本的な構文は以下の通りです。
リスト[開始インデックス:終了インデックス]
この構文を理解するコツは、「インデックスは要素と要素の間の仕切り線だ」とイメージすることです。
# インデックスを「要素の間の仕切り」としてイメージする
#
# │ 'a' │ 'b' │ 'c' │ 'd' │ 'e' │
# ↑ ↑ ↑ ↑ ↑ ↑
# インデックス 0 1 2 3 4 5
上の図のようにインデックスをイメージすると、スライスはとても分かりやすくなります。例えば、[1:4]というスライスは、「インデックス1の仕切り線でカットし、インデックス4の仕切り線でカットし、その間にある要素をすべて取り出す」という意味になります。
実際にコードで見てみましょう。まず、サンプルのリストを定義します。
x = ['a', 'b', 'c', 'd', 'e']
print(f"元のリスト: {x}")
このリストxから真ん中の3つの要素 'b', 'c', 'd' を取り出してみます。
# インデックス1からインデックス4の手前までを取得
middle = x[1:4]
print(f"x[1:4] の結果: {middle}")
# 出力結果: x[1:4] の結果:['b', 'c', 'd']
ここで非常に重要な注意点があります。それは、スライスでは終了インデックスで指定した位置の要素は含まれないということです。x[1:4]は、インデックス1, 2, 3の要素を取り出し、インデックス4の「仕切り線」の手前で処理を終えます。
スライスでは、開始インデックスや終了インデックスを省略することができます。これにより、非常に便利なショートカットが使えます。
最初の要素から指定位置まで取得する ([ : 終了インデックス ]) 開始インデックスを省略すると、自動的にリストの先頭(インデックス0)からスライスが始まります。
指定位置から最後の要素まで取得する ([ 開始インデックス : ]) 終了インデックスを省略すると、自動的にリストの最後までスライスが続きます。
補足解説:middle は、スライスした結果(['b', 'c', 'd'])を入れておくための「変数(変数の名前)」です。特別な意味を持つコマンド(予約語)ではありません。自由に決めていい名前です。今回は「真ん中(middle)の要素を取り出した」という意味でわかりやすく middle と名付けられていますが、これが box でも result でも y でも、コードは同じように動きます。
# 名前を変えても動きは同じです
sushi = x[1:4]
print(sushi)
f は 「f-strings(エフ・ストリングス)」 と呼ばれる機能で、「この文字列の中に、変数を埋め込みますよ」 という合図です。format(フォーマット)の略で、字列の引用符 " や ' の直前に f を置くと有効になります。これを使うと、文字列の中に { }(波括弧)を書くことで、そこに変数の変身をそのまま埋め込むことができます。print(f"x[1:4] 結果: {middle}") の意味この1行で行われている処理は、以下のようになります。
①Pythonが f を見つける → 「お、中身を入れ替える準備が必要だな」と判断。
②"x[1:4] の結果: " までは普通の文字として扱う。
③{middle} を見つける → 「ここに変数 middle の中身を入れるんだな」と判断。
④変数 middle の中身である ['b', 'c', 'd'] を持ってきて、置き換える。
⑤最終的に作られた文字列を画面に表示する。
もし f がなかったら?
f を付け忘れると、{ } がただの文字として扱われてしまいます。
# f がある場合(正解)
print(f"結果: {middle}")
# 出力: x[1:4] の結果: ['b', 'c', 'd']
# f がない場合(間違い)
print("結果: {middle}")
# 出力: x[1:4] の結果:{middle} <-- そのまま文字として出てしまう!
辞書
辞書(dictionary)とは、「キー(Key)」と「値(Value)」がペアになったデータを複数格納するコレクションです。現実世界の辞書で単語(キー)を引いて意味(値)を調べるように、Pythonの辞書もキーを使って瞬時に対応する値を取り出すことができます。
例:
’リンゴ’ は 120円
’バナナ’ は 300円
’イチゴ’ は 450円
この場合、「商品名」がキー、「値段」が値となり、これらを一つのまとまりとして管理するのが辞書の役割です。もし辞書を使わなければ、これらの情報を管理するために複数の変数が必要になったり、どの商品とどの価格が対応しているのかが分かりにくくなったりする可能性があります。辞書は、このようなデータ間の関連性を明確にし、一つの変数でスマートに管理することを可能にします。
リストとの違いを明確にする
リストと辞書は、どちらも複数の値をまとめて扱うための便利なデータ構造ですが、その内部構造と最適な利用シーンは根本的に異なります。どちらを使うべきか適切に判断することは、コードの可読性、保守性、そしてパフォーマンスに直接影響を与えます。
| 特徴 | リスト (list) | 辞書 (dict) |
| データ構造 | 複数の値を順序付けて格納する | キーと値のペアを格納する |
| データアクセス | 0から始まるインデックス番号でアクセス | 対応するキーでアクセス |
| 主な用途 | 順序が重要なデータのコレクション | 意味的な繋がりを持つデータの管理 |
この比較から分かるように、最も大きな違いはデータへのアクセス方法です。リストは「何番目にあるか」でデータを探しますが、辞書は「何という名前(キー)か」でデータを探します。
例えば、「バナナの値段を知りたい」という要求があったとします。リストで価格を管理している場合、「バナナがリストの何番目にあるか」を把握していなければなりません。一方、辞書であれば 'バナナ' というキーを指定するだけで、直接 300 という値を取得できます。この「意味」でデータに直接アクセスできる点が、辞書の最大の強みです。リストで同じことをするには、リスト全体を走査して「バナナ」を探すか、その位置を常に覚えておく必要があります。データが増えるほど、この差はパフォーマンスとコードの可読性の両面で決定的になります。
辞書の基本操作
アプリケーションにおけるデータ操作の基本は、CRUD(Create, Read, Update, Delete)に集約されます。「作成」「値の取得(Read)」「要素の追加(Create)」「値の更新(Update)」の4つの操作は、辞書を扱う上での土台となります。
辞書の作成
辞書を新しく作成するには波括弧 {} を使って作成します。'キー': 値 のようにキーと値をコロン : でペアにし、それぞれのペアをカンマ , で区切って波括弧の中に記述します。
以下のコードは、先ほどの価格表の例を辞書として作成するものです。キーは文字列、値は数値で表現されています。
# 商品名をキー、価格を値とする辞書を作成
kakaku_hyo = {'リンゴ': 120, 'バナナ': 300, 'イチゴ': 450}
print(kakaku_hyo)
# 出力: {'リンゴ': 120, 'バナナ': 300, 'イチゴ': 450}
値の取得
辞書の最大の利点は、キーを指定して直接、対応する値を取得できることです。角括弧 [] の中にキーを記述します。
辞書名[キー] という構文で、特定のキーに対応する値を瞬時に取り出すことができます。
# 'バナナ'というキーを使って値を取得
banana_price = kakaku_hyo['バナナ']
print(f"バナナの価格は{banana_price}円です。")
# 出力: バナナの価格は300円です。
要素の追加
既存の辞書に新しいキーと値のペアを追加するのも簡単です。辞書名[ という構文を使います。'新しいキー'] = 値
ここでは、価格表に「トマト」を新しく追加。
# 'トマト'を新しい要素として追加
kakaku_hyo['トマト'] = 340
print(kakaku_hyo)
# 出力: {'リンゴ': 120, 'バナナ': 300, 'イチゴ': 450, 'トマト': 340}
出力結果から、辞書の末尾に 'トマト': 340 が追加されたことが確認できます。
値の更新
既存のキーに対応する値を変更(更新)する場合も、要素の追加と全く同じ構文を使用します。辞書名[既存のキー] = 新しい値 と記述します。Pythonは、指定されたキーが辞書に既に存在するかどうかを自動で判断し、存在すれば値を更新し、存在しなければ新しい要素として追加します。この挙動は、キーが存在すれば更新し、存在しなければ挿入するため「upsert(update or insert)」として知られ、if key in my_dict:のような条件分岐のボイラープレートを排除します。これにより、コードはよりクリーンかつ簡潔になります。
例えば、「イチゴ」がセールで安くなった場合、以下のように価格を更新できます。
# 'イチゴ'の価格を更新
kakaku_hyo['イチゴ'] = 350
print(kakaku_hyo)
# 出力: {'リンゴ': 120, 'バナナ': 300, 'イチゴ': 350, 'トマト': 340}
'イチゴ' の値が 450 から 350 に正しく更新されている。
複数の辞書を結合する
実際のアプリケーション開発では、異なるソースから得られたデータを一つにまとめる場面が頻繁に発生します。Pythonでは、2つの辞書を結合するためのシンプルで効果的な方法が提供されています。
update()メソッドによる結合
update() メソッドは、呼び出し元の辞書を直接変更し、引数として渡された辞書の内容を追加します。この方法は、元の辞書を更新したい場合に便利です。
# 結合する2つの辞書を定義
x = {'a': 1, 'b': 2}
y = {'c': 3, 'd': 4}
# xにyの内容を結合する
x.update(y)
print(f"結合後のx: {x}")
# 出力: 結合後のx: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
この操作の後、変数 x の中身自体が変更されている点に注意してください。
キーが重複する場合の挙動 もし結合する辞書同士でキーが重複している場合、update() メソッドは引数で渡された辞書(右側の辞書)の値で上書きします。これは重要なルールです。
x = {'a': 1, 'b': 10}
y = {'b': 2, 'c': 3}
x.update(y)
print(f"キー重複時の結合結果: {x}")
# 出力: キー重複時の結合結果: {'a': 1, 'b': 2, 'c': 3}
キー 'b' の値が 10 から 2 に上書きされていることがわかります。
|演算子による結合
Python 3.9以降では、縦棒 | 演算子を使って2つの辞書を結合できます。この方法の最大の特徴は、元の辞書 (x と y) を変更せず、結合結果として新しい辞書を返す点です。
# 結合例のため、辞書を再定義
x = {'a': 1, 'b': 2}
y = {'c': 3, 'd': 4}
# | 演算子でxとyを結合し、新しい辞書zを作成
z = x | y
print(f"新しい辞書z: {z}")
# 出力: 新しい辞書z: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 元の辞書は変更されていないことを確認
print(f"元の辞書x: {x}")
# 出力: 元の辞書x: {'a': 1, 'b': 2}
キーが重複する場合の挙動 | 演算子も update() と同様に、キーが重複した場合は右側の辞書の値が優先されます。
x = {'a': 1, 'b': 10}
y = {'b': 2, 'c': 3}
z = x | y
print(f"キー重複時の結合結果: {z}")
# 出力: キー重複時の結合結果: {'a': 1, 'b': 2, 'c': 3}
辞書の調査と安全なアクセス
データ構造を操作するだけでなく、その状態をプログラム的に把握し、エラーを未然に防ぐことは、バリデーションや制御フローを実装する上で不可欠です。
要素数の取得:len()
データ処理を行う際、ループの回数を決めたり、条件分岐で処理を分けたりするために、コレクションにいくつの要素が含まれているかを知ることは非常に基本的な要件です。
辞書の場合、組み込み関数 len() を使うことで、含まれているキーと値のペアの総数を簡単に取得できます。
# 前のセクションで作成した結合後の辞書zを使用
element_count = len(z)
print(f"辞書zの要素数は {element_count} 個です。")
# 出力: 辞書zの要素数は 3 個です。
この len() は辞書専用の関数ではありません。リストの要素数や文字列の文字数を調べる際にも同じように使える、非常に汎用的な組み込み関数です。
キーの存在確認:in
inキーワードは、辞書に特定のキーが存在するかどうかを効率的にチェックするための標準的な方法です。
price_list = {'りんご': 120, 'バナナ': 300}
print('りんご' in price_list) # True
print('メロン' in price_list) # False
安全な値の取得:.get()
ブラケット記法(price_list['メロン'])で存在しないキーにアクセスしようとするとKeyErrorが発生し、プログラムが停止する可能性があります。.get()メソッドは、この問題を回避するためのベストプラクティスです。キーが存在しない場合はNoneを返し、第二引数で任意のデフォルト値を指定することもできます。
price_list = {'りんご': 120, 'バナナ': 300}
# .get()はKeyErrorを発生させない
melon_price = price_list.get('メロン')
print(f"メロンの価格: {melon_price}") # メロンの価格: None
# デフォルト値を指定
default_price = price_list.get('メロン', 0)
print(f"メロンの価格(デフォルト値使用): {default_price}") # メロンの価格(デフォルト値使用): 0
辞書ビューによるイテレーション:.keys(), .values(), .items()
.keys()、.values()、.items()メソッドは、辞書のキー、値、またはキーと値のペアを効率的に走査するための「ビューオブジェクト」を返します。これらはデータのコピーを作成しないためメモリ効率が良く、ループ処理で頻繁に利用されます。
price_list = {'りんご': 120, 'バナナ': 300, 'いちご': 350}
# .items()を使ってキーと値を同時にループ処理
for item, price in price_list.items():
print(f"{item}: {price}円")
数値演算
Pythonの標準機能(外部ライブラリ不要)で利用できる基本的な「演算子」と、複数の計算を組み合わせる際の評価順序を定めた「優先順位」について、体系的に解説します。
基本的な四則演算
四則演算(足し算、引き算、掛け算、割り算)は、プログラミングにおける数値計算の根幹をなすものです。これらの演算子を正確に使いこなすことは、あらゆる計算処理の基本であり、データ分析やアルゴリズム実装の正確性に直結します。
| 演算子 | 意味 | コード例 | 実行結果 | 説明 |
+ | 足し算 | 5 + 10 | 15 | 2つの数値を足し合わせます。 |
- | 引き算 | 5 - 10 | -5 | 左の数値から右の数値を引きます。 |
* | 掛け算 | 5 * 10 | 50 | 左の数値と右の数値を乗算します。 |
/ | 割り算 | 5 / 10 | 0.5 | 左の数値を右の数値で割ります。 |
割り算 (/) の特徴
Pythonの / 演算子は、計算結果が整数で割り切れる場合でも、常に小数点数(浮動小数点数)を返します。例えば、上記の 5 / 10 は整数同士の計算ですが、結果は 0.5 という浮動小数点数になります。これは、より正確な計算結果を保持するためのPythonの仕様です。
べき乗、剰余、切り捨て除算
べき乗 (**)
べき乗演算子 ** は、ある数値を累乗する際に使用します。「x ** y」は数学における「xのy乗」を意味します。この演算子は、平方根などの計算にも応用できます。例えば、4の平方根(√4)は4の1/2乗と同じ意味なので、4 ** (1/2) と表現することで計算できます。
print(5 ** 2) # 5の2乗
print(10 ** -2) # 10の-2乗(1/10の2乗)
print(4 ** (1/2)) # 4の平方根
実行結果:
25
0.01
2.0
剰余 (%)
剰余演算子 % は、割り算の「余り」を求めるために使用します。「x % y」は、「xをyで割ったときの余り」を計算します。この演算子は、一見すると用途が限られているように思えますが、実際には非常に便利です。例えば、「ある数値が偶数か奇数か(number % 2 の結果が0なら偶数)」を判定したり、「ループ処理の中で3回に1回だけ特別な処理を行う(loop_counter % 3 == 0)」といった周期的な処理の実装に広く使われます。
# 5を2で割ったときの余りを計算
print(5 % 2)
実行結果:
1
この例では、5を2で割ると商が2で余りが1となるため、結果は 1 となります。
切り捨て除算 (//)
通常の割り算 / が正確な値を返すのに対し、切り捨て除算 // は「商の整数部分のみが必要」という特定の目的に特化した演算子です。割り算を行った結果から小数点以下をすべて切り捨て、整数部分のみを返します。この演算子は、例えば「全100件のアイテムを1ページ10件で表示する場合の総ページ数を求める(100 // 10)」や、「商品を25個ずつ箱詰めする場合に必要な箱の数を計算する」といった、商の整数部分だけが意味を持つシナリオで役立ちます。
# 通常の割り算と切り捨て除算の比較
print(5 / 2)
print(5 // 2)
print(10 // 3)
実行結果:
2.5
2
3
このように、5 / 2 の結果が 2.5 であるのに対し、5 // 2 は小数点以下の .5 が切り捨てられ、2 となります。
/ と // のどちらを選択するかは、解決したい問題の性質に依存します。精度と数学的な正しさを求めるなら / を、アイテムやページ、グループといった分割できない単位を扱うなら // を使用します。
演算の優先順位
複雑な数式を記述する際、意図通りの計算結果を得るためには「演算子の優先順位」を理解することが不可欠です。Pythonは、数学のルールと同様に、厳格な優先順位に従って式を評価します。これにより、誰が書いても、どの環境で実行しても、数式の評価結果が常に一貫し、予測可能になるのです。Pythonにおける演算の優先順位は、高いものから順に以下の通りです。
1、カッコ (): カッコで囲まれた式が常に最優先で評価されます。
2、べき乗 **: 次にべき乗が評価されます。
3、掛け算 *、割り算 /、剰余 %: これらは同じ優先順位です。
4、足し算 +、引き算 -: これらが最も優先順位が低くなります。
このルールが実際にどのように適用されるか、以下の複雑な式を例に見ていきましょう。
3 + 6 * (1 + 1 * 2) ** 2
Pythonはこの式を以下のステップで評価します。
ステップ1: カッコ内の評価
最も優先順位が高いのはカッコ () です。まず (1 + 1 * 2) が評価されます。このカッコ内では、足し算 + よりも掛け算 * が優先されるため、1 * 2 が先に計算されて 2 となります。次に 1 + 2 が計算され、カッコ内の最終的な値は 3 となります。
式全体: 3 + 6 * 3 ** 2
ステップ2: べき乗の評価
次に優先順位が高いのはべき乗 ** です。3 ** 2 が計算され、結果は 9 となります。
式全体: 3 + 6 * 9
ステップ3: 掛け算の評価
残った式では、足し算 + よりも掛け算 * が優先されます。6 * 9 が実行され、結果は 54 となります。
式全体: 3 + 54
ステップ4: 足し算の評価
最後に足し算 3 + 54 が実行されます。
最終結果: 57
このコードを実際にPythonで実行すると、解説通りの結果である 57 が得られます。
条件式
プログラミングの世界では、コンピュータに「もし〜なら、こうしてね」という指示を出すことがよくあります。この判断の基準となるのが条件式です。条件式はシステムの「意思決定機関」として機能し、プログラムが入力値に対してどのように反応し、どの実行パスを選択するかを定義するこの制御ロジックの正確性は、アプリケーションの信頼性と論理的堅牢性に直結します。プログラミングにおける「判断」とは、提示された条件が「合っているか、間違っているか」をチェックすることで、主に if 文(もし〜なら、〜する)と一緒に使われます。条件式を計算した結果、コンピュータは以下の2つのうち、どちらか一方の答えを出します。これを「ブール型(論理型)」という特別なグループの値として扱います。
True(真): 「その通り!」条件を満たしている状態
False(偽): 「違うよ!」条件を満たしていない状態
コンピュータはこの2つの値を見て、次に進むべき道を「YesかNoか」で決めているのです。これは True(真)または False(偽)の二値のみをとるデータ型であり、プログラムの実行パスを分岐させる最小単位で、単なる値の比較に留まらず、ブール型による厳格な制御を理解することは、計算資源の最適化やロジックの透明化を促進し、保守性の高い制御フローの構築を可能にします。
実務レベルのコードにおけるロジックの実に7割から8割は、基本的な比較演算子の組み合わせで構成され、これらの仕様を精緻に把握することは開発効率の向上において不可欠です。
等価・不等価比較(==, !=)の仕様とデータ型別挙動
値の一致・不一致を判定する「等価比較」は、ビジネスロジックの実装において最も頻繁に利用される。Pythonでは数値、文字列、リストといった異なるデータ型に対して共通の演算子を使用できるポリモーフィズム的な性質を持つが、その評価基準は型ごとに厳密に定義されている。
データ型別に見る等価比較の仕様ルール
| 比較対象 | 演算子 | 判定条件と挙動の特性 |
| 基本等価 | == | 左右の値が等しい場合に True を返却する |
| 基本不等価 | != | 左右の値が異なる場合に True を返却する |
| 数値の等価 | == | 10 == 10 のように数値が完全に一致すれば True |
| 数値の不等価 | != | 10 != 11 のように数値が異なれば True |
| 文字列の一致 | == | 文字列が完全に同一であれば True |
| 大文字・小文字の区別 | == | 'Apple' と 'apple' は異なる値(False)と判定される |
| 文字列の不一致 | != | 文字列の内容が1文字でも異なれば True |
| リストの一致 | == | すべての要素の順序と値が完全に一致すれば True |
| リストの不一致 | != | リスト内の要素が一部でも異なれば True |
| 記号の定義 | != | エクスクラメーションマーク(!)と等号の組み合わせ |
特に文字列比較において、大文字・小文字が厳密に区別される仕様は、ユーザー入力のバリデーションや認証ロジックにおいて注意を要する。また、不等価演算子 != に用いられる「!」はエクスクラメーションマーク(通称:ビックリマーク)。これらの厳密なルールを理解し、データ型を跨いだ共通の演算仕様を把握することは、予期せぬ分岐ミスを排除し、デバッグコストの低減に大きく寄与します。
数値データの境界値制御
大小比較演算子の活用
数値の大小関係に基づく条件分岐は、閾値判定やシステムの安全動作を保証する範囲制限において決定的な役割を果たす。境界条件のわずかな設計ミスは重大な論理欠陥を招くため、演算子の正確な使い分けが求められる。
比較演算子の定義と境界条件
Pythonでは、4つの演算子を用いて数値の境界を制御する。
・a >= b (aはb以上 / Inclusive):
aがbと同じ、またはbより大きい場合に True。b自体も含みます。
例:20 >= 20 は Truea が b と等しい、または a が b より大きい場合に True。
・a > b (aはbより大きい・b超 / Exclusive):
aがbより大きい場合に True。b自体は含みません。
例:20 > 20 は False a が b より大きい場合に True(b 自体は含まない)。
・a <= b (aはb以下 / Inclusive):
aがbと同じ、またはbより小さい場合に True。b自体も含みます。
例:15 <= 20 は Truea が b と等しい、または a が b より小さい場合に True。
・a < b (aはb未満 / Exclusive):
aがbより小さい場合に True。b自体は含みません。
例:21 < 21 は Falsea が b より小さい場合に True(b 自体は含まない)。
境界値設計におけるリスク管理
境界条件における「等号(=)」の有無は、システムの仕様不備、特に「オフバイワンエラー(境界値が1ずれるエラー)」に直結する。例えば「20歳以上」を判定する際に age > 20 と記述すると、20歳が除外され仕様を満たさなくなる。実装時には、境界値そのものが条件に含まれるべきかを常に精査し、適切な演算子を選択することが論理的堅牢性の確保につながる。
包含判定(in演算子)による要素存在判定の効率化
含まれているかを確認する記号(in)
リストや文字列の中に、特定のデータが入っているかを調べたい時は in を使います。「〜の中に含まれている」という直感的なイメージで使えます。
問い: 文字列 'apple' の中に 'a' はある?
式:'a' in 'apple'
答え:True
問い: リスト ['apple', 'banana'] の中に 'orange' はある?
式:'orange' in ['apple', 'banana']
答え:False
文字列内の部分一致判定:
例:'a' in 'apple' 文字列 'apple' 内に文字 'a' が存在するかを1行で判定する。コンピュータは文字を厳密に区別します。文字列 'apple' に対して 'A' in 'apple' (大文字のAがあるか?)と聞くと、結果は False になります。
リスト判定:
例:'apple' in ['apple', 'banana'] 指定したオブジェクトがリストの要素として存在するかを判定する。例えば、リスト内に存在しない 'orange' を検索した場合は False が返却される。
複数の比較条件を or で連結する代わりに in を活用することで、開発者の意図をより明確に表現でき、チーム開発におけるメンテナンスコストの削減が可能となる。
バリデーション標準:
「含まれていない場合にFalseを返す」特性は、入力値が許容範囲内であるかを検証するバリデーション工程において決定的な役割を果たす。
複合条件の構築
論理演算(and, or, not)による高度な制御
複雑なビジネス要件をコードに落とし込むには、複数の条件式を組み合わせる論理演算子(and, or, not)の戦略的活用が不可欠である。
各演算子の論理構造
and (かつ):
すべての条件式がTrueである場合のみ、全体の評価をTrueとする。「厳しい」条件。
or (または):
少なくとも一つの条件がTrueであれば、全体の評価をTrueとする。「優しい」条件。
not (否定):
条件の真偽を逆転させる「not演算子」は、特定の条件に「合致しないこと」を判定する際に有効である。True なら False に、False なら True になります。
not を使うとき、Pythonでは not age >= 20 のように書けますが、not (age >= 20) のようにカッコをつけるのがおすすめ、カッコがあったほうが、人間にとっては「何をひっくり返そうとしているのか」が一目でわかって読みやすくなります。
正確な条件式設計がもたらすシステム品質の向上
等価・大小比較、包含確認、そして論理演算を自在に組み合わせる能力は、実務における開発効率とシステム品質を決定づける。曖昧な条件判定を排除し、厳密なブール型制御に基づく設計思想を徹底することは、バグの混入を未然に防ぎ、保守性の高いクリーンなコードを実現するための最短ルートである。
文字列
制御構文
条件分岐
プログラムは、単に上から下へ順番に処理を実行するだけではありません。優れたプログラムは、まるで人間のように状況を判断し、面倒な作業を自動で繰り返す能力を持っています。その中核を担うのが「制御構文」です。制御構文とは、プログラムの処理の流れをコントロールするための特別な命令であり、これらを使いこなすことで、画一的な処理しかできないプログラムから、状況に応じて動的に振る舞う、より高度なプログラムへと進化させることができます。
最もよく使われる制御構文であるif文(条件分岐)とfor文(繰り返し処理)の基本的な書き方のルール。
条件で処理を分ける「if文」
if文は、「条件分岐」を行うための命令です。if文は、指定された「条件式」がTrue(真)かFalse(偽)かを判定し、もしTrueだった場合にのみ、決められた処理を実行する仕組みです。
例えば、「もしある数値が特定の値より大きいなら、特別なメッセージを表示する」といったように、条件に応じてプログラムの動作を変えたいときに使います。
if と else
基本的な条件分岐は、if(もし〜なら)とelse(そうでなければ)を組み合わせて作ります。
| キーワード | 役割 | 書き方のポイント |
if | 「もし条件に当てはまるなら」という処理を指定します。 | if 条件式: のように、条件式の後にコロン(:)をつけ、次の行はインデント(字下げ)します。 |
| elif | 2つ目以降の別の条件をチェックする。「あるいは、もし〜なら」 | ifの後、elseの前に、いくつでも書ける。 |
else | ifの条件に当てはまらなかった場合の処理を指定します。 | else: のようにコロン(:)をつけ、次の行はインデントします。if文のブロックには一つしか書けません。 |
基本構文(if-else)
最も基本的なif文の形は、ifとelseを組み合わせたものです。
if 条件文:
# 条件文がTrue(真)のときの処理
else:
# 条件文がFalse(偽)のときの処理
この構文には、Pythonの重要なルールが詰まっています。
if, else: どのブロックがどの役割を持つかを示すキーワードです。
条件文: TrueかFalseを返す式を記述します。比較演算子(例:x > 10)などがよく使われます。
コロン(:): if文やelse文の終わりを示し、ここから処理ブロックが始まることを意味します。
インデント(字下げ): Pythonでは、インデント(通常は半角スペース4つ)によって処理のブロックを定義します。同じインデントの行が、同じブロック内の処理と見なされます。これは非常に重要なルールです。
条件を追加する: elif
「もしAなら」「そうでなければ」の2択だけではなく、「もしAなら」「そうではなく、もしBなら」「それ以外なら」というように、複数の条件で処理を分けたい場合には elif を使います。
elif は「前のifやelifの条件には当てはまらなかったが、こちらの条件には当てはまる場合」の処理を指定します。elifは一つのif文の中にいくつでも追加することができます。
【例】変数が10より大きいか、0以上10以下か、それ以外かを判定する
x = 5
if x > 10:
print("10より大きい")
elif x >= 0:
# 上のifがFalseだったので、xが10以下であることは分かっています。
# このブロックは x >= 0 も満たすため実行されます。
print("0以上10以下")
else:
print("マイナスの数")
x = 5の場合、プログラムの動作は以下のようになります。
① if x > 10:の条件をチェックします。5 > 10はFalse(偽)なので、このブロックの処理はスキップされます。
② elif x >= 0:の条件をチェックします。5 >= 0はTrue(真)なので、このブロックのprint("0以上10以下")が実行されます。
③ 一度elifブロックが実行されると、プログラムはif-elif-elseの構文全体を抜けます。そのため、最後のelseブロックは実行されません。
実践コード例①:数値比較
変数xの値が10より大きいか、それ以外(10以下)かを判定するプログラムです。
x = 11
if x > 10:
print("10より大きい") # 条件文 (x > 10) がTrueなので、こちらが実行される
else:
print("10以下")
x = 11 の場合:
if文の条件式 x > 10 が評価されます。
11 > 10 は正しいので、結果は True となります。
ifブロック内の print("10より大きい") が実行され、画面に「10より大きい」と表示されます。
elseブロックは無視され、処理は終了します。
x = 9 の場合:
if文の条件式 x > 10 が評価されます。
9 > 10 は間違いなので、結果は False となります。
ifブロックは無視され、処理はelseブロックへ移ります。
elseブロック内の print("10以下") が実行され、画面に「10以下」と表示されます。elifによる複数条件の追加
「もしAなら…、そうでなくもしBなら…、どちらでもなければ…」のように、3つ以上の分岐を作りたい場合もあります。その際に活躍するのが elif です。elif は “else if” の略で、新たな条件を追加するために使います。
実践コード例②:多段階分岐
変数xが「10より大きいか」「0以上か」「それ以外(負の数)か」を判定してみましょう。
x = 5
if x > 10:
print("10より大きい")
elif x >= 0:
print("0以上10以下") # 最初にTrueになるこのブロックが実行される
else:
print("負の数")
x = 5 の場合:
① if x > 10 が評価されます。5 > 10 は False なので、このブロックはスキップされます。
② elif x >= 0 が評価されます。5 >= 0 は True なので、このブロック内の print("0以上10以下") が実行されます。最初のif x > 10が偽(False)と評価された後なので、このelifブロックに到達した時点でxが10以下であることが確定しています。そのため、この条件は実質的に『0以上かつ10以下』を意味します。
③ 一度条件に合致するブロックが見つかったため、それ以降のelseブロックは評価されずに、if文全体の処理が終了します。
このように、プログラムは上から順に条件を評価し、最初にTrueとなったブロックの処理のみを実行します。
ここでif, elif, elseの重要なルールを確認しておきましょう。
・if と else は、1つの分岐処理の中に1つずつしか書けません。
・elif は、ifとelseの間にいくつでも書くことができます。
繰り返し処理を自動化する:for文
for文は、プログラミングにおける「自動化」の基本であり、繰り返し作業をコンピュータに任せるための強力なツールです。リストの各要素に対して同じ処理を適用したり、「決まった回数だけ処理を繰り返す」といった操作を、わずか数行のコードで実現できます。for文の基本的な構文は以下の通りです。
for 変数 in 繰り返し可能オブジェクト:
# 繰り返したい処理
繰り返し可能オブジェクト:
リスト、辞書、range()関数など、複数の要素をまとめたデータのことです。
in:
このキーワードの後ろにあるオブジェクトから、要素を1つずつ取り出す、という指示を出します。
変数:
取り出された要素が、ループの各回で一時的に格納される場所です。この変数を使って、ブロック内の処理を記述します。
実践コード例①:リストの活用
価格が格納されたリスト x_list があるとします。このリストの各数値に「円」という単位を付けて表示してみましょう。
x_list = [100, 200, 300]
for x in x_list:
print(str(x) + "円")
実行結果:
100円
200円
300円
処理の流れ:
1周目: x_list の最初の要素 100 が変数 x に代入され、print(str(100) + "円") が実行されます。
2周目: 次の要素 200 が変数 x に代入され、print(str(200) + "円") が実行されます。
3周目: 最後の要素 300 が変数 x に代入され、print(str(300) + "円") が実行されます。
リストの全要素が処理されたので、ループは終了します。
str()関数が使われている点に注目してください。Pythonでは、+演算子は数値同士に使うと数学的な足し算、文字列同士に使うと文字の連結として機能します。数値と文字列を直接 + で結合しようとするとTypeErrorというエラーが発生するため、str()を使って数値を文字列に変換してから結合する必要があります。
実践コード例②:辞書の活用
商品名(キー)と価格(値)がペアになった辞書を扱ってみましょう。
# .items() を使うことで、キーと値を同時に取り出せる
for key, value in {"アップル": 100, "バナナ": 350}.items():
print(key + "の価格は" + str(value) + "円です")
実行結果:
アップルの価格は100円です
バナナの価格は350円です
• 辞書をfor文でループさせる場合、.items()メソッドを使います。これにより、キーと値のペアを効率的に取り出すことができます。
・ .items()は、キーと値のペアをタプルという小さなデータ構造(例:('アップル', 100))の集まりとして返します。for key, value in ... という書き方は「タプルのアンパッキング」と呼ばれるPythonの強力な機能で、各タプルの1番目の要素をkeyに、2番目の要素をvalueに、ループの各回で自動的に代入してくれます。
実践コード例③:range()の活用
リストや辞書のような具体的なデータ集合がなくても、「決まった回数だけ処理を繰り返したい」という場面は頻繁にあります。その際に非常に便利なのが range() 関数です。
range(10) は、0から9まで(10は含まない)の連続した整数を生成します。
# 0から9まで、合計10回ループが実行される
for i in range(10):
print(i)
実行結果:
0
1
2
3
4
5
6
7
8
9
この方法は、処理の回数だけが重要な場合に非常にシンプルで効率的なコードを書くことができます。Pythonでは、このように範囲を指定する際に「最後の数値は含まない」というのが一般的なルールです。これにより、0から数え始めてちょうど10回の繰り返し処理が実現できます。
これらの制御構文は、単独で使われるだけでなく、forループの中でif文を使って特定の条件の要素だけを処理するなど、組み合わせて使うことで、さらに複雑で実用的なプログラムを作成することができます。
関数とクラス
関数
関数(function)とは「特定の処理を一つにまとめて定義したもの」で,一連の処理を一つの論理的な単位として独立させ、再利用可能な形で定義する仕組みです。関数化の本質は、単なるコードの短縮ではなく「抽象化」にあります。関数に適切な名前(関数名)を付与し、処理をカプセル化することは、そのコードが「何(What)をするのか」という意図を明確に定義する「Documenting Intent(意図の文書化)」の役割を果たします。これにより、後続のエンジニアは内部の具体的な計算手順(How)を詳細に解読することなく、定義された関数を呼び出すだけで安全に機能を活用できるようになります。
関数によるDRY原則(Don’t Repeat Yourself)の実現
「DRY原則(同じことを繰り返すな)」という鉄則があります。これは初心者がプロになるための必須知識です。複雑な計算手順を一つの定義に集約することには、以下の決定的なメリットがある。
エラーの抑制: ロジックが一箇所に集約されるため、仕様変更時の修正漏れが物理的に発生しなくなる。
コードの洗練: 呼び出し側は「平均値を算出する」という抽象的な意図を記述するだけでよくなり、実装の詳細から解放される。
変更への耐性: 除数の変更といった計算ロジックの修正が、関数定義の一箇所のみで完結する。
関数を使う3つのメリット
再利用性:一度定義すれば、どこでも何度でも使い回せる。
可読性:コードが整理され、誰が見ても「何をしているか」がすぐわかる。
メンテナンス性:ロジックの修正が必要な際、1箇所を直すだけで済む。
関数の基本構造と重要用語
Pythonで関数を定義する際は、def キーワードを使用します。ここで中心となるのが、「引数(ひきすう)」と「戻り値(もどりち)」という2つの用語です。
引数:関数に「これを使って処理してね」と渡される値のことです。例えば、平均点算出関数に first_scores という辞書データを放り込むイメージです。
引数を用いることで、関数内部のロジックを変更することなく、対象となるデータを動的に切り替えることが可能となります。
汎用性の拡張: 例えば、apple や orange といった特定の文字列を内部に持つのではなく、引数 text を通じて外部から受け取る設計にすることで、一つの関数で多様なデータ処理を完結させることができる。
多変数のハンドリング: 複数の情報が必要な場合は (text1, text2) のようにカンマで区切り、情報の受け渡しを構造化する。
インターフェースの一貫性: 外部からの入力を必要としない場合であっても、一貫性の観点から空の括弧 () を記述し、関数呼び出しであることを明示しなければならない。
戻り値:関数が処理を終えた後に「結果はこうなったよ」と呼び出し元へ返してくる値のことです。計算の結果導き出された「平均値」がこれに当たります。結果を戻り値として定義することで、その値を後続の業務プロセスで自由に再利用できるようになります。関数の真価は、処理結果を呼び出し元へ確実にパイプし、後続のロジックで活用可能にすることにある。return 文による戻り値の制御は、プログラムの健全なデータフローを形成するための生命線である。
明示的な返却: 文字列に「?」を付加する加工を行う question_text のような関数では、加工後の値を必ず return で返却する。これにより、呼び出し元で result_text といった変数へ結果を代入し、再利用することが可能となる。
複数戻り値の安全な受け取り: return text1, text2 のように複数の値を返す場合、呼び出し側は r1, r2 = function_name() という形式でタプル展開(Unpacking)を用いるべきである。この際、定義側の順序と受け取り側の変数を1:1で厳密に対応させ、シーケンスエラーを防止することが義務付けられる。
責務の分離: 値を返す「計算・加工」目的の関数と、値を返さずに出力等を行う「副次作用」目的の関数を明確に使い分けることで、コードの予測可能性を向上させる。
基本構文
def 関数名(引数):
# 処理の内容(必ず4文字分の半角スペースでインデントする)
加工処理など
return 戻り値
def(デフ): 「これから関数を定義(define)します」という宣言です。
def キーワードの後に、スネークケース(snake_case)を用いた関数名を記述する。
関数名:その関数を呼び出すための名前です。その処理にふさわしい名前を付けます。
シグネチャ:関数名の直後に括弧 () を配置し、末尾にコロン : を付与する。
(引数)::関数に渡す「材料」です。カッコ () の中に記述します。不要な場合は空欄にします。
コロン(:): 定義行の末尾に必ず付けます。
インデント(字下げ): 関数の中身であることを示すため、処理コードの行頭にスペースを入れます。
スコープの定義:
改行後、必ず適切なインデントを挿入する。Pythonにおいてインデントは単なる装飾ではなく、syntactic scope(構文上のスコープ)を定義する厳格な要件。
命名において、単にバナナと表示するなら print_banana
汎用的に文字列を扱うならprint_text
のように、動詞と名詞を組み合わせた具体的かつ推測可能な名前を付与することが命名規則の徹底により、開発者はソースコードを深く読み解くことなく、その関数の役割を瞬時に理解することが可能となる。
戻り値(return): 処理の結果を呼び出し元に返却します。
関数構成の比較一覧
関数は、引数や戻り値が必要かどうかに応じて、主に以下の4つの構成に分けられます。
| パターン名 | 引数 / 戻り値 | 特徴・役割 |
| パターンA | なし / なし | 常に決まった特定の動作(表示など)を行う。 |
| パターンB | あり / なし | 外部データを受け取り、そのデータを使って表示などのアクションを行う。 |
| パターンC | あり / あり | データを受け取り、加工した結果を返す。最も汎用性が高い標準的な形。 |
| パターンD | 複数 / 複数 | 複数の入力を受け取り、複数の結果を同時に得たい場合に活用する。 |
パターンA:引数なし・戻り値なし
外部からの影響を受けず、常に一定の処理を行う最もシンプルな形です。
def print_banana():
print(“バナナ”)
print_banana() # 実行結果: バナナ
呼び出すだけで、あらかじめ決めておいた定型処理を確実に実行できる。
実行すると必ず「バナナ」と表示されるなど、外部のデータによって内容が左右されないメッセージ表示に適しています。
パターンB:引数あり・戻り値なし
外部からデータを受け取りますが、結果を呼び出し元に返す必要がない場合に使用します。
def print_text(text):
print(text)
print_text(“apple”) # 実行結果: apple
渡すデータ(”Apple” や “Orange” など)を変えることで、一つの関数で異なる表示結果を生み出せる。受け取った文字を画面にプリントして処理を終了させるなど、その場での「アクション(出力)」が目的のシーンで使われます。
パターンC:引数あり・戻り値あり
データを受け取って加工し、その結果を「戻り値」として返します。計算結果をその後のプログラムで再利用したい場合に非常に重要です。
def question_text(text):
text_q = text + “?”
return text_q
result_text = question_text(“apple”)
print(result_text) # 実行結果: apple?
加工されたデータ(例:末尾に「?」を付与した文字列)を result_text などの変数に保存でき、さらなる計算や別の関数への受け渡しが可能になる。文字列を加工して保存したり、数値計算の結果を取得したりする、プログラミングで最も多用される「再利用性」に優れたパターンです。
パターンD:複数の引数・複数の戻り値
一度の呼び出しで複数の入力を処理し、複数の結果を同時に受け取りたい場合の効率的な方法です。
def question_exclamation_text(text1, text2):
return_text1 = text1 + “?”
return_text2 = text2 + “!”
return return_text1, return_text2
# 2つの戻り値を r1, r2 で受け取る
r1, r2 = question_exclamation_text(“apple”, “banana”)
print(r1) # 実行結果: apple?
print(r2) # 実行結果: banana!
apple を text1 へ、banana を text2 へと同時に渡し、それぞれに「?」と「!」を付けるような関連する処理を一括で行える。Pythonでは return text1, text2 のようにカンマで区切ることで複数の値を返せます。受け取る側も r1, r2 = 関数名() のように記述(アンパック)することで、別々の変数に一気に格納できます。
クラスとインスタンス
設計図(クラス)と実体(インスタンス)
プログラミングにおける「クラス」は「設計図」です。そして、その設計図から実際に生み出されたものを「実体(インスタンス / オブジェクト)」と呼びます。
【設計図と実体の決定的な違い】
クラス(設計図): 「どんなデータを持つか」「どんな処理ができるか」を定義した枠組み。これ自体には具体的な中身(数値など)はまだ入っていません。データの種類(身長、体重)と、やりたい処理(BMI計算)の「型」を定義したもの。 共通のルールを決めた「設計図」。
インスタンス / オブジェクト(実体): 設計図を元に作られた現実の「モノ」。具体的なデータを持つ「一人ひとりの実体」のこと。
この設計図に「150cm / 55kg」というデータを入れると、Aさんという実体が生まれます。
同じ設計図に「140cm / 45kg」を入れると、Bさんという別の実体が生まれます。
さらに「145cm / 50kg」を入れれば、Cさんというまた別の実体が生まれます。
もし、20人分の身体データを管理する場合、クラスを使わずに変数と関数だけで作ろうとすると、コードの中は「変数のスープ」のような大混乱に陥ります。
クラスを使うことで得られるメリットは、主に以下の3つです。
データの整理: 「身長」と「体重」という関連するデータを、一人の人間(オブジェクト)として一箇所にまとめられる。
計算処理の共通化: 「BMIを計算する」というプログラムを一度書くだけで、全員に同じルールを使い回せる。
個別の状態保持: Aさんのデータ、Bさんのデータ……と、お互いに混ざることなく、それぞれの「個性」を安全にキープできる。
データ(属性)とその計算処理(BMI計算)をセットにして管理することで、プログラムは驚くほどスッキリします。
クラスという箱の中には、大きく分けて2つの重要なパーツが入っています。
| 用語 | 別名・役割 | 具体例(身体データ) | アクセス方法(推奨) |
| インスタンス変数 | 属性・データ | 身長、体重 | instance.変数名 |
| メソッド | 振る舞い・関数 | BMIを計算する処理 | ClassName.変数名 |
self(セルフ)の意味
クラスの中に定義された関数のことを、特別にメソッドと呼びます。メソッドを書くとき、必ず一番最初の引数に書くルールになっているのがselfです。selfは、そのメソッドが動いている時に「今、誰のデータを扱っているのか(自分自身)」を指し示す役割を持っています。「自分自身のデータにアクセスするための合図」だと考えてください。メソッドを定義するときは、第一引数に必ず self と書きます。これにより、メソッドは「私(そのインスタンス)が持っている身長と体重を使ってね!」と自分のデータを見つけ出すことができます。なお、メソッドを呼び出すときには、Pythonが自動的に self を渡してくれるため、私たちは self の存在を気にせず呼び出すだけで大丈夫です。
カプセル化によるデータ一貫性(Data Integrity)の確保
メソッドが「自分自身のデータを用いて計算を行う」という自己言及的な構造(例:self.weight を用いたBMI算出)を持つことで、外部から不正な値を注入されるリスクを抑えられます。このようにロジックをオブジェクト内部に隠蔽することで、データと計算式のミスマッチ(不正な状態)を防ぎ、高い信頼性を担保した設計が可能となります。__init__による初期化
インスタンス(実体)が生成されるとき、一度だけ自動的に実行される特別なメソッドがあります。それが __init__(イニシャライザ) です。他のプログラミング言語では「コンストラクタ」とも呼ばれます。新しいインスタンスが生まれた瞬間に、必要なデータ(体重や身長など)をセットして、「準備完了」の状態にすることです。
[ ] データの受け取り: インスタンス作成時に外から渡された値(引数)を受け取る。
[ ] 自分の箱へ保存:self.変数名 = 受け取った値 という形で、自分自身の持ち物(インスタンス変数)として保存する。
※左側の self.変数名 が「保存場所(自分のポケット)」、右側の値が「外から入ってきたデータ」だとイメージしてください。
①実体の誕生: クラスを元に、新しいインスタンスが作られます。
②初期設定の実行: __init__ が呼び出され、外部から渡された「150cm」「55kg」といった数値を受け取ります。
③変数の記憶: self.weight = weight のように書くことで、その数値が「そのインスタンス専用のデータ」として記憶されます。
この self.変数名 という書き方があるおかげで、AさんとBさんのデータが混ざることなく、それぞれの個性が大切に保存されるのです。
インスタンス変数とクラス変数
クラス変数: メソッドの外で定義され、全てのインスタンスで共有する変数(selfは使いません)。
インスタンス変数(属性) それぞれの実体が個別に持つ値のことです。「Aさんの身長」「Bさんの体重」のように、インスタンスごとの個性を表します。self を使って、各実体が個別に持つ変数。
属性(アトリビュート): インスタンス変数とクラス変数をまとめた呼び名です。
クラス内で扱うデータ(属性)には、用途によって2つの種類があります。
| 比較項目 | インスタンス変数 | クラス変数 |
| 役割 | 個別のデータ(人により違う) | 共通のデータ(全員同じ) |
| 定義場所 | 主に __init__ メソッドの中 | メソッドの外(クラス直下) |
selfの有無 | 必要(self.weightなど) | 不要 |
| データの所有者 | 各インスタンス | クラス全体 |
| アクセス例 | bc.weight | BodyCondition.standard |
AさんとBさんのBMI計算
ボディコンディションクラス」という設計図から、AさんとBさんがそれぞれのBMIを導き出すまでのステップを追いかけてみましょう。
「ボディコンディション」という設計図が1枚あるとイメージしてください。そこには「身長と体重を記録する枠」と「BMIを計算する仕組み」が書かれています。
Aさん: 150cm / 55kgという値を持つ
Bさん: 140cm / 45kgという値を持つ
このように、「仕組み(設計図)」は1つでも、そこから作られる「実体(インスタンス)」はそれぞれ独自のデータを持てるわけです。
| 項目 | クラス (Class) | インスタンス (Instance) |
| イメージ | 設計図・共通の型 | 実体(オブジェクト) |
| 役割 | 「ルール」や「形式」を決める | 「具体的なデータ」を持って動く |
| 具体例 | 身長・体重という項目と、BMI計算のルール | Aさん、Bさん、それぞれの数値が入った体 |
| 存在の状態 | まだ形がない(定義のみ) | メモリ上に存在する(実際に計算できる) |
【コンピュータ内部で行われるステップ】
クラスの定義: BMIの計算ルール(体重 ÷ 身長(m) ÷ 身長(m))を設計図に書き込む。
インスタンス化: Aさんの数値(150, 55)を渡して、Aさん専用のオブジェクトを生成する。
初期化(): Aさんのインスタンス変数に値がセットされ、データが固定される。
単位の変換と計算: メソッド内で self.height / 100 を行い、cmからmへ変換した上でBMIを算出する。
結果の出力: Aさんがメソッドを呼べばAさんのBMIが、Bさんが呼べばBさんのBMIが、個別に正しく表示される。
同じ設計図を使いながら、self がそれぞれのデータを正確に指し示すことで、計算結果は「個人の値」として算出されます。
コードの動き
class BodyCondition:
# イニシャライザ(初期設定)
def __init__(self, weight, height):
self.weight = weight # 体重をセット
self.height = height # 身長をセット
# BMIを計算するメソッド
def bmi_calc(self):
# 身長をcmからmに変換(100で割るのがポイント!)
m_height = self.height / 100
bmi = self.weight / (m_height * m_height)
print(f"BMIは {bmi:.2f} です")
# Aさんの実体(インスタンス)を作成
bc_a = BodyCondition(55, 150)
bc_a.bmi_calc()
Step 1: 設計図の定義 class BodyCondition: で設計図を作ります。この時点ではまだ何も起きません。
Step 2: Aさんの実体を作る(インスタンス化) bc_a = BodyCondition(55, 150) を実行した瞬間、イニシャライザ __init__ が自動で動き出します。引数で渡した「55」と「150」が、Aさんの専用データとして書き込まれます。
Step 3: Aさんのメソッドを呼び出す bc_a.bmi_calc() を呼ぶと、メソッド内の計算が始まります。ここで注目なのが、self.height / 100 という部分です。ソースにある通り、BMI計算にはメートル法が必要なので、しっかり変換を行っています。
メソッドに出てくる self は、「自分自身(今まさに操作しているインスタンス)」を指す指差し棒のようなものです。 bc_a から呼び出せば、self は自動的に「Aさんの実体」を指します。だから、Aさんの身長と体重を迷わずに取り出して計算できるのです。
例外処理
例外
書いたコードが止まり、画面に赤い文字のエラーが表示されることがあります。多くの初心者はこれを見て「失敗した」と不安に思うかもしれませんが、実はこの仕組みこそがプログラムをより強固なものにする鍵となります。プログラミングの世界では、実行中に起きるこれらの問題を「例外(Exception)」と呼びます。
「例外」とは、「コンピューターが命令を実行した時に起きる問題」の総称です。 これが発生する原因は、大きく分けて2つのパターンがあります。
プログラム自体の不備(ソースコードの問題):
0で割る計算や、未定義の変数の参照など、書き手のミスによって起きるもの。
予測できない外部要因:
ネットワークの切断、コンピューターのメモリ不足、ファイルが見つからないといった、コードの外側で起きるトラブル。
プログラマーは自分のコードはコントロールできますが、外部環境(ネットワークの状態など)を完全にコントロールすることはできません。しかし、外部要因でトラブルが起きた際にも「適切に対処する責任」はプログラマーにあります。こうした例外を想定し、あらかじめ「例外処理」を組み込んだコードを「プロダクトコード(実用的なプログラム)」と呼びます。
「例外」の具体例
| 例外名 (例外型) | 発生する原因 | 具体的な例 |
ZeroDivisionError | 数値を「0」で割る計算を実行した時 | 10 / 0 |
NameError | 存在しない(定義されていない)変数にアクセスした時 | print(undefined_var) |
他にも、メモリが不足した時に発生する MemoryError や、オブジェクトに存在しない属性を呼び出した時の AttributeError など、公式ドキュメントには多くの種類が記載されています。これらの問題が起きた際、何も対策をしていないとプログラムはその瞬間に強制終了(クラッシュ)してしまいます。
try と except の基本構造
プログラムの強制終了を防ぐための仕組みが、try と except です。これらは必ず「ペア」で使用され、プログラムを安全に保護・対処する役割を担います。
tryブロック:
「例外が発生する可能性のある処理」を記述します。ここにあるコードは、いわばプログラムによって保護された状態で実行を試みられます。
exceptブロック:
try 内で例外が発生した際、その身代わりとして実行される「対処(ハンドル)」を記述します。except には「どのエラーを捕まえるか」という具体的な指定が必要です。そこで重要になるのが「例外型」という概念です。
構文のポイント
特定の例外(例:ZeroDivisionError)だけを狙ってキャッチするのが一般的です。
try:
# 1. 実行を試みる(保護エリア)
result = 10 / 0
except ZeroDivisionError:
# 2. 指定した例外が起きた時の対処
print("0で割ることはできません")
Pythonでは、try: や except: の後に必ずインデント(字下げ)が必要です。この視覚的な構造によって、どの範囲が保護され、どこが対処用なのかを明確に区別します。
Pythonにおける try-except 構文の適用に際しては、以下の原則を厳守しなければならない。
特定例外の明示的捕捉:
捕捉対象とする例外型(ZeroDivisionError や NameError など)は、可能な限り具体的に指定すべきである。汎用的な捕捉に頼りすぎると、コード内の論理的欠陥(例えばタイポによる NameError)を意図せず握りつぶし、原因究明を著しく困難にする「マスク(遮蔽)」を引き起こす恐れがある。
多角的なハンドリングの優先順位:
複数の except ブロックを並置する場合、より具体的な例外から記述し、広範な例外を後に配置することで、エラーの種類に応じた精密なリカバリ処理や通知の出し分けを実現できる。
例外を捕捉した後の制御フローを最適化するためには、単なるエラー対応に留まらず、正常系との分離および後処理の徹底が求められる。
応用的な4つのキーワード
as 変数名(例外の詳細を取得) 単にエラーが起きたことを知らせるだけでなく、except ZeroDivisionError as e: のように記述することで、Pythonが出力する詳細なエラーメッセージを「変数 e」として取得できます。なぜエラーになったのかを正確に把握するのに役立ちます。
else(成功時の処理) 例外が「一つも発生しなかった時」だけ実行したい処理を書きます。try ブロックを最小限にし、エラーが起きなかった場合の正常ルートを分けることで、コードの可読性が上がります。これは、本来監視すべきでない箇所で発生した例外を誤って捕捉することを防ぐ、防御的プログラミングの要諦である。
finally(後片付けの処理) 例外の有無に関わらず、最後に必ず実行される処理です。ファイルのクローズや接続の解除など、どんな状況でも必ず終わらせるべき作業を記述します。ファイル記述子のクローズやデータベース接続の切断など、クリーンアップ処理において決定的な役割を果たす。これの欠如は、リソースリークによるシステム全損を招くリスクがある。
Exception(例外の親玉) Pythonのほぼ全ての例外(MemoryErrorなど)は、この Exception という大元(親)のクラスを継承しています。except Exception: と書けば、個別のエラー名を指定しなくても、システム終了以外のあらゆるエラーをまとめてキャッチできます。
※なお、システムの電源オフや強制終了を司る SystemExit などは、この Exception の範囲外(別の親を持つ)となっています。これは「プログラムを絶対に止めなければならない非常事態」まで間違って捕まえてしまわないための安全設計です。
フローのイメージ:
【起点】try(やってみる)→( 例外が発生した場合のみ、ここへ飛びます。)except(分岐) →( 例外が発生しなかった場合のみ、正常系のご褒美として実行されます。)else(分岐) →(例外の有無に関わらず、最後に必ず実行されます。いわば「後片付けの責任者」です。)finally例外の有無に関わらず、最後に必ず実行されます。いわば「後片付けの責任者」です。例外の有無に関わらず、最後に必ず実行されます。いわば「後片付けの責任者」です。【終点】
なぜ例外処理が必要なのか
「外部とのデータのやり取り」が不確実性に満ちているからです。
ファイルの読み書き:
指定したファイルが削除されていたり、読み取り権限がない場合があります。
データベースとの通信:
データを送受信しようとした瞬間に、サーバーの電源が落ちたりネットワークが切断されたりするかもしれません。
プログラマーが完璧なコードを書いても、ユーザーがファイルを消してしまえばエラーは起きます。このような「自分では防ぎようがない問題」が起きた際に、無言でアプリを落とすのではなく、「現在、通信が不安定です」とユーザーに優しく通知し、安全な状態に復帰させるのが例外処理の真の目的です。
開発で遵守すべき基準
I/O境界の保護:
全てのファイル操作、DBアクセス、通信処理において例外処理が組み込まれているか。
具体的型指定の遵守:
理由なく広範な捕捉を行わず、ZeroDivisionError や NameError など、想定される最小単位の例外型を指定しているか。
ロギングの徹底:
例外を捕捉した際、単に処理を継続させるだけでなく、将来の調査のために十分なエラー詳細を出力しているか。
確実なクリーンアップ:
異常発生時でもファイルハンドルや接続が放置されないよう、finally 句によるリソース解放が保証されているか。
エラー読み解きガイド
メッセージを読み解き、発生場所を特定する
エラーが発生したとき、最優先でやるべきことは「エラーメッセージをしっかり読むこと」です。Pythonが出力するメッセージには、必ず以下の2つの要素が含まれています。
どこで(場所): どのファイルの、何行目でエラーが起きたか。
何が(理由): なぜエラーになったのか。
これらを特定するために、「トレースバック(Traceback)」を確認しましょう。これはエラーが発生するまでの「実行の歴史(経緯)」を記録したものです。まず一番下を確認してください。そこにエラーの種類と具体的な理由が書かれています。英語に抵抗がある場合は、Google翻訳などのツールを活用し、サクッと日本語で意味を把握しましょう。
エラー特定のための比較チェック表
| 確認項目 | 特定する方法 | 具体的な表示例 | 活用のコツ |
| どこで(場所) | File "...", line ... という記述を探す | File "main.py", line 5 | 指摘された行だけでなく、その周辺のコードも再確認する。 |
| 何が(理由) | メッセージの最後の一行を確認する | NameError: name 'x' is not defined | 英語のまま悩まず、翻訳ツールで「意味」を把握する。 |
検索技術を駆使して解決事例を探す
エラーの場所と理由がわかったら、次はそのメッセージをインターネットで検索してみましょう。
キーワードの組み合わせ:
エラーメッセージだけでなく、「Requests(ライブラリ名)」や「ファイルオープン時」など、具体的な「状況」を加えて検索すると精度が上がります。
英語情報の活用:
プログラミングの情報量は、日本語よりも英語の方が圧倒的に多いのが現実です。Chromeの右クリック翻訳(「日本語に翻訳」)などを使えば、海外の技術サイトも強力な味方になります。
信頼できる主要なWebリソース
Stack Overflow(スタック・オーバーフロー)
世界最大のエンジニア向けQ&Aサイト。
回答の横にある数字(アップボート)で情報の信頼度がわかります。特に緑色のチェックマークがついている回答は、質問者が認めた解決策なので最優先で確認しましょう。
GitHub Issues(ギットハブ・イシュー)
プログラムの設計図(ソースコード)が管理されている場所にある、バグ報告や相談の掲示板。
ライブラリ自体の不具合や、開発者による最新の回避策が見つかることがあります。
事例が見つからない場合は、その技術の「正解」が書かれた公式ドキュメントを確認しましょう。公式ドキュメントで「正しい使い方」を確認する
ネット上のブログ記事は情報が古い場合がありますが、公式ドキュメントには常に「最新の正しい使い方」が記されています。初学者がドキュメントで挫折しないコツは、「ピンポイントで読む」ことです。
・対象を絞って探す: 全部読む必要はありません。「Requestsライブラリの使い方は?」「JSONオブジェクトの扱いは?」「PySimpleGUIの関数の引数は?」など、エラー周辺で使っている技術に絞って検索します。
・データの形を確認する: 関数が受け取るデータの種類(数値なのか文字列なのか等)が、自分の書いたコードと合っているか確認します。
・サンプルコードと比較する: 公式が提示している「正しい例」と、自分のコードのどこが違うのかを照らし合わせます。
自分なりに調べ尽くしても解決しない時のために、最後の手段として「正しい質問の仕方」を覚えておきましょう。
質問時に含めるべき4要素
質問をする際は、以下の情報をまとめましょう。日本語版Stack Overflowやteratailといったサイトを活用するのもおすすめです。
・実現したいこと:(例:Requestsライブラリを使ってWebからデータを取得したい)
・実行したコード:(エラーが発生している箇所のコードを貼り付ける)
・発生しているエラーメッセージ:(表示された内容を、Tracebackを含めてそのままコピー&ペーストする)
・自分で試したこと:(例:公式ドキュメントで関数の引数を確認し、検索で見つけた○○という方法も試したが同様のエラーが出た)
質問の標準テンプレート
### 1. 実行したコード(抜粋)
[問題の箇所のコードをここに貼り付け]
### 2. 発生状況
[どのような操作やタイミングで発生したか]
### 3. エラーメッセージ
[Tracebackを含む全文を貼り付け]
### 4. 既に試したこと(分析・検索・公式確認の結果)
– ステップ1:[〇〇行目を確認し、変数の値はXXだった]
– ステップ2:[Stack Overflowで△△の方法を試したが解決せず]
– ステップ3:[公式ドキュメントの□□の仕様を確認したが、適合しているように見える]
さらにステップアップしたい方は、PyCharmやVS Codeといった開発ツールに備わっている「デバッカー」という機能も調べてみてください。プログラムを一行ずつ止めて動きを確認できる、さらに強力な武器になります。