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]=99figs[1]=99と同じ意味になる為、figsの1番目要素が変わっていたのです。

試しにmyslice[0]=99figs[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, in 
    myslice[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]

おわり