golang(go言語) / python2 / python3 におけるスライス
go言語の勉強をしている際にpython2とpython3ともスライスの挙動が違うことを知ったので備忘録として。
python2
個人的に一番慣れていて挙動としてもしっくりくるpython2から紹介します。
figs=range(10) # figs==[0,1,2,3,4,5,6,7,8,9] myslice=figs[1:5] # myslice==[1,2,3,4] myslice[0]=99 print(figs) print(myslice)
実行結果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [99, 2, 3, 4]
1行目にて、0~9の数値が順番に並んだlistを変数figとして作成。
2行目にて、figsの1~4番目の要素によるlistを変数mysliceとして保存。
3行目にて、mysliceの0番目に99を代入すると、
実行結果にて、mysliceは書き換わるが、figsの値は1行目の時点から変わっていないことが分かります。
golang
package main import "fmt" func main() { figs:=[10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} myslice:=figs[1:5] myslice[0]=99 fmt.Println(figs) fmt.Println(myslice) }
実行結果
[0 99 2 3 4 5 6 7 8 9] [99 2 3 4]
一方、golangでも若干文法の書き方は違いますが同じ処理内容のコードに見えますが、実行結果を見るとfigsの1番目要素が99に変わっています。
このpython2との実行結果の違いはpython2とgolangでのスライスの処理の違いによるものです。
python2の場合はスライスを使うと元のlistの一部をコピーするので、myslice変数は新しい変数として作られていました。
一方、goの場合はスライスはデータを持たず、元のlistの一部へのポインタを作成します。なので、myslice変数は実はfigs[1:5]へのポインタを覚えているだけなのです。
つまり、ポインタを作ってmysliceとしてポインタの場所を覚えているだけなので、myslice[0]=99はfigs[1]=99と同じ意味になる為、figsの1番目要素が変わっていたのです。
試しにmyslice[0]=99をfigs[1]=99に書き換えても結果は変わりません。 figsの1番目要素は99になりますし、mysliceの0番目要素も99になります。
python3
最後にpython3です。
まずはpython2と同じコードを実行してみましょう。
figs=range(10) # figs==[0,1,2,3,4,5,6,7,8,9] myslice=figs[1:5] # myslice==[1,2,3,4] myslice[0]=99 print(figs) print(myslice)
実行結果
Traceback (most recent call last): File "Main.py", line 3, inmyslice[0]=99 TypeError: 'range' object does not support item assignment
エラーになってしまいました。 これはpython2とpython3のrange関数の処理の違いによるものです。
python2ではrange関数を使用すると戻り値はlist型で戻ってきますが、 python3ではrange関数を使用すると戻り値はrange型で戻ってきます。
range型をスライスでコピーした新たな変数mysliceもやはりrange型なので、0番目要素だけ書き換える、ということはできないのです。
対処策は色々考えられますが、簡単なところだとスライス結果をlist型に変換してしまえばいいかなと思います。 なお、スライスの処理についてはpython2と同じく元の変数のコピーを作っているのでmysliceを書き換えてもfigsには影響が出ません。
以下がその例です。
figs=range(10) print(type(figs)) print(type(figs[1:5])) myslice=list(figs[1:5]) print(type(myslice)) myslice[0]=99 print(figs) print(myslice)
実行結果
range(0, 10) [99, 2, 3, 4]
おわり