Pythonの罠

listのコピー

a = [1,2,3]
b = a
b[0] = 0
print(a)
[0,2,3]

なんと,bの代入元であるaも変わってしまった.

これを回避するために,acopyを作る必要がある.

a = [1,2,3]
b = a.copy()
b[0] = 0
print(a)
[1,2,3]

割り算の演算子

a = 5
b = 2
print(a/b)
print(a//b)
2.5
2

/は小数点以下を含む割り算を行う.//は小数点以下を切り捨てた割り算を行う. /を大きな整数で使おうとすると正しい結果が得られない.

次のプログラムでは,$9999\cdots9999$を$3$で割った値,すなわち$3333\cdots3333$を求めたい.

a = 999999999999999999999999999999999999999999999999999999999999999999999999999999999999
b = 3
print(a/b)
print('{:.1000g}'.format(a/b)) #eを使わない表示にする.
print(a//b)
3.333333333333333e+83
333333333333333316642274409837779174589735593005847147516488057690807469867494539264
333333333333333333333333333333333333333333333333333333333333333333333333333333333333

演算子/を使った場合,でたらめな値が得られたことがわかる. //を使うと,正しい値が得られる.

小数を扱う関数

sqrt関数,pow関数などは小数で計算をする.

a = 10
b = pow(a,100)
print('{:.10000g}'.format(b))

x = 10000000000000000000000000000000000
a = x*x
b = sqrt(a)
print('{:.10000g}'.format(b))
10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104
9999999999999999455752309870428160

わけのわからない値が得られる. 基本的に標準ライブラリの関数は信用してはいけない.PyCryptodome,sagemath,NumPyなどのライブラリを使うか,自分で実装する必要がある.

再帰関数の深さ

def f(n):
    if n==0:
        return 0
    return f(n-1)+1
print(f(100000))
RecursionError: maximum recursion depth exceeded in comparison

再帰関数の深さ(recursion depth)の最大値は,デフォルトでは$1000$である. これを変更するには,sysモジュールを使う.

import sys
sys.setrecursionlimit(1000000)