PythonとC/C++のパフォーマンスの差はインタプリタ言語/コンパイル言語の差だけではない

一般的にPythonはC/C++と比べて実行速度が遅いです。
 ・Pythonのようなインタプリタ言語はソースプログラムを機械語に翻訳しながら実行
 ・C/C++のようなコンパイル言語は実行前にあらかじめ機械語に翻訳するため実行
そのためPythonよりC/C++のほうが高速であることはよく言われることですが、それ以外にもPythonよりC/C++のほうが効率的な点があります。

Pythonのint型変数はイミュータブル

整数を表すint型変数は、C/C++ではミュータブル(変更可)ですが、Pythonではイミュータブル(変更不可)です。以下にC/C++とPythonのプログラム例を示します。

#include <iostream>

int main()
{
  int a = 0;
  std::cout << &a << std::endl ;
  a = 10;
  std::cout << &a << std::endl ;
}
0000003C7113FC80
0000003C7113FC80

上記C/C++プログラムでは、5行目で変数aが生成され、そのアドレスは0000003C7113FC80です。(6行目)
7行目でa10を代入してもそのアドレスは変わらず0000003C7113FC80です。(8行目)

a = 0
print(id(a))
a = 10
print(id(a))
2122483198224
2122483198544

上記Pythonプログラムでは、1行目で変数aが生成され、そのアドレスは2122483198224です。(2行目)
3行目でa10を代入するとアドレスが2122483198544に変わります。(4行目)
これは、1行目で生成したaが破棄され、3行目で新たにaが生成されていることを意味します。

for文のカウンタ変数では

次にfor文のカウンタ変数のアドレスも確認してみます。

#include <iostream>

int main()
{
  int n = 10;
  for ( int i = 0; i < n; ++i )
  {
    std::cout << &i << std::endl ;
  }

  return 0;
}
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0
000000B3BE4FFCD0

上記C/C++プログラムではfor文で10回繰り返し処理を行っています。8行目でカウンタ変数iのアドレスを表示しています。そのアドレスは10回とも000000B3BE4FFCD0で変わっていません。

for i in range(10):
  print(id(i))
1356276525328
1356276525360
1356276525392
1356276525424
1356276525456
1356276525488
1356276525520
1356276525552
1356276525584
1356276525616

上記Pythonプログラムではfor文のカウンタ変数iのアドレスが変わっており、生成・破棄が繰り返されていることが確認できます。

Pythonのリスト内包表記を使用した場合も同様です。

[print(id(i), sep='\n') for i in range(10)]
1356276525328
1356276525360
1356276525392
1356276525424
1356276525456
1356276525488
1356276525520
1356276525552
1356276525584
1356276525616

iは生成・破棄が繰り返されています。

まとめ

Pythonでは、たとえ同じint型変数に値を代入したときでも生成・破棄が行われます。この現象はパフォーマンスの低下を招きます。ほかにもfloat型、str型、bool型などはイミュータブルです。NumPyを使うなどして回避できることがありますので、プログラムを書く際は気をつけたいですね。

動作確認環境
 Python:3.9.16
 C/C++:Visual Studio 2019