Pythonのrange関数を基本から応用まで解説!

19/01/09 19:30:11     19/05/05 17:30:16

Pythonのrange関数

rangeはPython以外の言語にもある概念ですが、範囲を指定するためのコードです。

rangeを和訳すると範囲なので、そのままの意味です。rangeの基本的な使い方は範囲を指定するだけなので簡単なのですが、そこから派生した応用的な使い方も用意されています。

rangeの基本的な使い方

rangeの構文を知るために、さっそく具体例を紹介します。サンプルコードというよりは、rangeの部分のみピックアップしますが、以下のようになります。

list(range(0,10,2))

公式にすると以下のようになります。

list(range(start, stop, step))

たとえばlist関数からrangeで値を指定する場合、上記のコードです。まず第一引数には0と書いてありますが、これは初期値です。初期値はデフォルトでは0になっており、省略も可能です。

次に第二引数が10となっていますが、これは最後の次の数字です。10の場合、初期値が0であることも含めると0~9の数字ということになります。なぜ最後の数字の次の数字になるかというと、0から数えると9は10番目の数字に該当するからです。

そして初期値を後ろにずらしてもシステム内部のカウントは変わらないため、最後の数字が9であれば初期値に関わらず第二引数は10になります。第三引数はステップ幅を表し、2であれば2ずつ増えていきます。

その結果、上記のコードを出力すると以下のようになります。

[0, 2, 4, 6, 8]

0から9までの数字で、2ずつ増えながら出力しています。第三引数も省略が可能で、省略した場合デフォルトの1ずつの出力になります。つまり、シンプルに0~9の数字が順番に出力されます。

範囲指定を逆順にする

上の例では通常の昇順で指定しましたが、逆に降順に指定することも可能です。具体的には、以下のサンプルコードのようになります。

list(range(10,0,-1))

このコードを実行した出力結果は以下です。

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

初期値が10で、そこから0に向かって1ずつ数字が減っています。昇順のときには最後の値+1で第二引数を設定していましたが、降順の場合第二引数は最後の値-1になります。理由としては、ベースとなる昇順と表記方法を統一するためです。

第三引数は昇順のときと同じ考え方で、インクリメントかデクリメントかで+-を使い分けるだけです。

range関数をfor文のなかで使う

次に、ループ処理のなかでrange関数を使用する方法を紹介します。たとえば以下のように記述すると、5回ループしながら出力されます。

for i in range(5):
print(i)

上記のコードを実行すると、以下のようにコンソール出力されます。

0
1
2
3
4

range関数で小数を扱う

range関数は整数だけでなく少数も扱うことが可能です。ただしもともとrange関数に小数を指定するロジックが備わっているわけではないので、自分で小数を指定できるように工夫します。具体的には以下のようなコードです。

for i in range(10):
print(i*0.1)

上記のソースコードを実行すると、以下のようにコンソール出力されます。

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

iには0から順に数値が入りますが、出力する際に0.1を掛けて小数にしています。同様の方法で、分数などで出力することも可能です。

Python2でのrange関数

上で説明してきた内容はPython3に該当するものなので、おそらく多くの方の開発環境に合致しているはずです。しかし、なかにはPython2を使用していて、簡単にバージョンを変えられない状況の方もいるかもしれません。

そこで、Python2でのrange関数の扱いについても触れておきます。多くのコードはPython2でもPython3でもだいたい同じなのですが、range関数についてはPython3で大幅に仕様変更されました。

仕様変更について知っておくと、開発者がプログラミング言語のどのような点に問題を感じ、どういった改良を加えているのかがよくわかります。開発者の視点を持つことはソースコードの改善にも役立つので、Python3を使用されている方もPython2でのrange関数の扱いについて知っておいて損はないかと思います。

両方知った上で、Python3でどのようにrange関数が改良されたのか考えると良いですね。Python2からPython3で変わったことを一言でざっくりと言えば、range関数の扱いが簡単になりました。

Python2ではrange関数とは別にxrange関数というものが用意されていたのですが、Python3ではこれがなくなっています。Python3ではxrangeの機能がrangeにまとめられ、その代りに従来のrange関数のリスト機能を使用する際にはlist(range())と書いてリストを明示するイメージです。

Python2ではrangeはリスト、xrangeはジェネレータオブジェクト、となっていたのですが、Python3ではrangeをデフォルトで使用するとジェネレータオブジェクト、リストを指定するとリスト、という仕様になっています。

つまり、Python2の感覚のままrange関数を使用すると、リストと思っていたのにジェネレータオブジェクトが返ってきた、ということになるかもしれません。それぞれの使い分けは上述の通りですが、そもそもジェネレータオブジェクトがよくわからないという方もいるかもしれないので、それについて解説します。

ジェネレータオブジェクトとは

ジェネレータはイテレータの一種です。また新しくわかりにくい用語が出てきましたが、イテレータとは反復して要素を取り出すことが可能な型のことです。そして、ジェネレータはイテレータのなかでも要素を取り出すごとに処理を実行して要素を生成できるもののことです。

つまり、listはイテレータのなかのジェネレータに該当するということです。

リストとジェネレータの違い

Python2ではrangeとxrangeで使用しているオブジェクトが違うということは上記の通りですが、なぜ使い分ける必要があるのか、という疑問が生じます。その理由は、rangeで作るリストは利便性が高いものの、メモリに負荷を掛けるからです。

その結果、Python3ではrange関数のデフォルトがジェネレータになっています。リストの場合処理をする際に最初にすべての要素が入ったリストを生成します。ジェネレータの場合順次要素にアクセスするため、処理が分散されます。

これはPython2でもPython3でも同じなので、Python3のrange関数でリストを指定する場合やPython2でrange関数を使用する場合、要素数が多いとメモリに負荷が掛かることを知っておくと良いでしょう。

要素数が少なければリストでもジェネレータでも処理速度に大差ありませんが、要素数が万単位になるとリストの方はわずかに遅くなるかもしれません。遅くなると言っても秒単位ではありますが、今の時代システムの処理速度はシビアです。

ウェブサイトも数秒開くのが遅くなるだけでも離脱者が増えてしまうので、処理速度は死活問題です。

Pythonのフリーランス案件をチェック

→Pythonの案件情報一覧はこちら

人気記事

編集部おすすめ記事

この記事を読んだ人はこんな記事を読んでいます

案件探しやフリーランスになるための相談する