pythonのrange関数の使い方は?ステップ指定と逆順ループの小技をご紹介!
プログラミングを学んでいると、必ずと言っていいほど遭遇するのがループ処理、いわゆる繰り返しです。 Pythonで繰り返しを記述する際、最も頻繁に登場するのがrange関数ではないでしょうか。
一見すると単純に数字を並べるだけの機能に見えますが、実は奥が深く、使いこなすとコードの質が劇的に変わります。 エンジニア歴10年の私も、初心者のコードをレビューする際には、このrange関数の使い方が適切かどうかを必ずチェックしています。
今回は、基本から応用、そして知っておくと便利な逆順ループのテクニックまで、徹底的に解説していきます。
range関数とは何か?¶
まずは、range関数がどのような役割を持っているのかを整理していきましょう。 基本を疎かにすると、思わぬバグを生む原因になります。
range関数を一言で説明するなら、連続する整数のリストのようなものを生成する関数です。 実際にはリストそのものではないのですが、まずは数字の列を作る道具だと考えて間違いありません。
なぜrange関数が必要なのか?¶
例えば、100回同じ処理を繰り返したいとき、あなたならどう書きますか。 まさか、同じコードを100行コピペすることはありませんよね。
そんなときに0から99までの数字を順番に出してと命令できるのがrange関数の強みです。 コンピューターに単純作業を任せるための、最も基礎的な指示書だと考えてください。
リストとの違いに注目する¶
初心者の方がよく誤解するのが、range関数を呼び出すとすぐに巨大なリストが作られるという点です。 実は、Python 3のrange関数はイテラブルオブジェクトと呼ばれる特殊な仕組みを採用しています。
# 100万までの範囲を指定しても、メモリはほとんど消費しません
r = range(1000000)
これについては後ほど詳しく解説しますが、まずは「必要なときに、必要な分だけ数字を出す賢いヤツ」だと覚えておきましょう。 エンジニアの間では、こうしたメモリ効率の良さがPythonの魅力の一つとして語られます。
シンプルな使い方(引数が1つの場合)¶
range関数の最も基本的な形は、カッコの中に数字を1つだけ入れる方法です。 これが全ての応用の出発点になります。
引数に渡した数値が、どこで止まるかを指定する役割を果たします。 このとき、多くの初心者がつまずくポイントが1つだけあります。
0から始まるという鉄則¶
range(5)と書いたとき、どのような数字が出力されるか想像してみてください。 1から5までだと思った方は、少し注意が必要です。
for i in range(5):
print(i)
# 出力結果: 0, 1, 2, 3, 4
Pythonの世界では、カウントは常に0からスタートします。 そして、指定した数値(この場合は5)に到達する直前で止まります。
終了値は含まれない¶
5は含まれず、4で終わる。 この「未満」という考え方は、配列やリストのインデックスを扱う際に非常に都合が良いのです。
エンジニア歴10年の私の感覚では、この仕様に慣れるかどうかが、Pythonicなコードを書けるかどうかの分かれ道だと感じています。 「指定した数だけ繰り返すが、その数自体は出てこない」と念じておきましょう。
【関連記事】100行のコードが10行に?Pythonic(パイソンらしい)な書き方への第一歩
開始と終了を自由自在に操る(引数が2つの場合)¶
次は、開始位置を0以外に設定する方法を見ていきましょう。 引数を2つ(開始値, 終了値)指定することで、特定の範囲だけを抽出できます。
業務アプリの開発では、0からではなく特定のIDの範囲を指定して処理を行うことが多々あります。 そのような場面で、この2引数の形式が活躍します。
範囲の指定方法¶
書き方は非常にシンプルで、コンマで区切るだけです。 ここでも「終了値は含まれない」というルールは継続されます。
for i in range(3, 8):
print(i)
# 出力結果: 3, 4, 5, 6, 7
3から始まって、8の直前である7で終わっていますね。 この感覚が身につくと、リストの部分的な操作(スライス)なども理解しやすくなります。
負の数も指定できる¶
range関数はプラスの整数だけでなく、マイナスの数値も扱うことができます。 あまり頻繁には使いませんが、温度計算や座標計算などで役立つことがあります。
for i in range(-3, 2):
print(i)
# 出力結果: -3, -2, -1, 0, 1
このように、数直線上の左から右へ移動していくイメージを持つと分かりやすいでしょう。 もし開始値が終了値よりも大きい場合、何も出力されないという点だけ気をつけてください。
ステップ指定で「飛ばし読み」をする(引数が3つの場合)¶
いよいよ中級者への第一歩、ステップ指定の解説です。 引数を3つ(開始値, 終了値, 増分)指定することで、数値を飛ばしながら生成できます。
例えば「偶数だけを処理したい」「3の倍数だけを取り出したい」といった要望に応えることができます。 コードの中でif文を使って判定するよりも、range関数で制御する方が遥かにスマートです。
増分(ステップ)の使い方¶
3番目の引数には、数字がいくつずつ増えるかを記述します。 これがデフォルトでは1になっているのですが、自由に変更可能です。
# 0から9までの偶数を出力する
for i in range(0, 10, 2):
print(i)
# 出力結果: 0, 2, 4, 6, 8
このように、2つ飛ばしで数字が出てくるのが分かります。 このテクニックは、大量のデータを間引いて処理したいときや、特定のパターンで画面を描画するときに非常に便利です。
ステップ指定の注意点¶
ステップ数に0を指定することはできません。 なぜなら、0ずつ増えてもいつまでも終了値に到達せず、無限ループになってしまうからです。
Python側でエラー(ValueError)を出してくれるので、うっかりミスをしてもすぐに気づけます。 エンジニアとしての私の経験上、ステップ指定は計算アルゴリズムの高速化に直結することが多いので、ぜひマスターしてほしい小技です。
逆順ループ!カウントダウンを実装する¶
ここからが本題の小技、逆順ループについてです。 数字を減らしながら繰り返したい場合、range関数には2つの主要なアプローチがあります。
特に「リストの末尾から要素を削除したい」といった場合、逆順ループは必須のテクニックです。 前から消すとインデックスがズレてしまいますが、後ろからなら安全に処理できるからです。
負のステップを使う方法¶
最も直接的な方法は、ステップ数にマイナスの値を指定することです。 この場合、開始値を終了値よりも大きく設定する必要があります。
# 5から1までカウントダウン
for i in range(5, 0, -1):
print(i)
# 出力結果: 5, 4, 3, 2, 1
ここでも「終了値(0)は含まれない」というルールが適用されます。 つまり、1で止まるということですね。
reversed関数を組み合わせる方法¶
もうひとつの、より可読性が高いとされる方法がreversed関数との組み合わせです。 通常の昇順rangeを、ガバッとひっくり返すようなイメージです。
# 直感的で読みやすいカウントダウン
for i in reversed(range(1, 6)):
print(i)
# 出力結果: 5, 4, 3, 2, 1
どちらを使うべきかという議論はありますが、個人的には後者の方が意図が伝わりやすいと感じます。 「1から5までの範囲を、逆から読みたい」という日本語のロジックに忠実だからです。
range関数とメモリの深い関係¶
さて、エンジニア歴10年の視点から、少し技術的な仕組みの話をさせてください。 なぜPython 3のrange関数は、1億といった巨大な数字を指定してもパソコンが重くならないのでしょうか?
その理由は、range関数が怠け者(Lazy)だからです。 専門用語では、遅延評価やイテレータといった概念に近い動きをしています。
数値をその場で作っている¶
range(100)を実行したとき、Pythonは100個の数字をメモリ上に並べてはいません。 「次はどの数字を出すか」「どこで止まるか」というルールだけを保持しています。
import sys
r1 = range(100)
r2 = range(1000000000)
print(sys.getsizeof(r1)) # 48バイト程度
print(sys.getsizeof(r2)) # 48バイト程度(全く同じ!)
このように、範囲がどれだけ広がっても、保持する情報のサイズは変わりません。 この仕組みのおかげで、Pythonはメモリを節約しながら大規模なループを回すことができるのです。
リストに変換したいときは?¶
どうしても全数字が並んだ「リスト」として扱いたい場合は、明示的に変換してあげる必要があります。 デバッグ中に中身を一度に確認したいときなどに使われます。
numbers = list(range(5))
print(numbers) # [0, 1, 2, 3, 4]
ただし、これを巨大な数に対して行うと、パソコンのメモリを食い潰してフリーズする原因になります。 実務では、必要がない限りリスト化は避けるのが賢明です。
【関連記事】Pythonのリスト内包表記を使いこなせ!3行のループを1行にまとめる書き方
実践で役立つrange関数の小技一覧¶
ここまでの知識を整理し、実務でよく使うパターンを表にまとめました。 困ったときはこの一覧を辞書代わりに使ってください。
| やりたいこと | コード例 | 出力のイメージ |
|---|---|---|
| n回繰り返す | range(n) |
0, 1, ..., n-1 |
| aからb-1まで | range(a, b) |
a, a+1, ..., b-1 |
| 偶数のみ | range(0, 10, 2) |
0, 2, 4, 6, 8 |
| 奇数のみ | range(1, 10, 2) |
1, 3, 5, 7, 9 |
| カウントダウン | range(10, 0, -1) |
10, 9, ..., 1 |
| 逆順(推奨) | reversed(range(5)) |
4, 3, 2, 1, 0 |
このように並べてみると、range関数がいかに汎用性の高いツールであるかが分かりますね。 特に「偶数・奇数の判定」をループの最初に行えるステップ指定は、if文のネストを減らす強力な武器になります。
range(len(obj)) はもう古い?最新の書き方¶
最後に、少しだけ「脱・初心者」のためのアドバイスをさせてください。 リストの中身を取り出しながらインデックスも知りたいとき、以下のように書いていませんか。
fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
print(i, fruits[i])
実は、この書き方は現在のPythonでは推奨されていません。 よりPythonらしい洗練された方法があります。
enumerate関数を活用しよう¶
インデックスと値を同時に取得したいなら、enumerate関数を使うのがベストです。 range(len())と書くよりも短く、読み間違いも防げます。
for i, fruit in enumerate(fruits):
print(i, fruit)
「range関数の記事なのに、使わない方を勧めるの?」と思われるかもしれませんね。 しかし、適切な場所で適切な道具を選ぶことこそが、エンジニアとしての真のスキルだからです。
まとめ¶
いかがでしたでしょうか。 range関数は、単なるループの補助ツールではなく、Pythonの設計思想(メモリ効率や簡潔さ)が詰まった素晴らしい機能です。
開始値、終了値、ステップの3つの引数をマスターすれば、どんな複雑な数値生成も怖くありません。 特に逆順ループやステップ指定は、あなたのコードをより「プロっぽく」見せてくれるはずです。
最初は「0から始まる」「終了値は含まれない」というルールに戸惑うかもしれません。 しかし、何度もコードを書いているうちに、それが自然と身体に馴染んできます。
「プログラミングは習うより慣れろ」です。 今日学んだ小技を、ぜひ今書いているプログラムに組み込んでみてください。
ここまでお読みいただきありがとうございました。