St_Hakky’s blog

Data Science / Human Resources / Web Applicationについて書きます

Effective python シリーズ5:Know How to Slice Sequences

こんにちは。少し日が空いてしまいましたが、今日も頑張る。

○読んでいる本

以下の本を勉強がてら読んでいます。

www.effectivepython.com

ここにある通り、Pythonプログラムを改良する59項目が掲載されています。詳細は本に書かれているので、それを読めば良しとして、大事そうなところと、これに関連して勉強したことを書きます。

まとめページは以下より。

st-hakky.hatenablog.com


さて、今回は5つ目:「Know How to Slice Sequences」をやります。

○Know How to Slice Sequences

どうやってシーケンス(list, str, bytes)をスライスできるか知っておけというやつですね。

■スライス構文の基本形

スライスの基本形は、以下のような形で、startの要素が含まれ、endの要素が含まれません。

somelist[start:end]

マイナスとかもよく使うやつですね。

■リストの境界を超えた添え字も適切に扱われて便利

リストの境界を超えたあたいも、適切に表現されるので、かなり便利。

a = [1,2,3,4,5,6,7,8,9,10]
print(a[:20])
print(a[-20:])

上のコードの出力は、どちらも、

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

になるという感じです。

これは入力シーケンスを考慮して最大長を設定したコードも簡単に書けるようになっています。

■代入するスライスの長さは同じでなくていい

これは「へぇー」ってなりました笑(えっ、もしかして常識?いや、こんなコード書くの?笑)

例えば、

a = [1,2,3,4,5,6,7,8,9,10]
a[2:7] = ["a", "b", "c"]
print(a)

とすると、出力は、

[1, 2, 'a', 'b', 'c', 8, 9, 10]

となるという笑。上の例は要素を減らしたパターンだが、要素を増やすパターンでやってみると、つまり、

a = [1,2,3,4,5,6,7,8,9,10]
a[2:3] = ["a", "b", "c", "d", "e"]
print(a)

とすると、

[1, 2, 'a', 'b', 'c', 'd', 'e', 4, 5, 6, 7, 8, 9, 10]

となるという笑。

■代入周りで覚えておきたいこと

これはほんまにいつもややこしいなぁ思う。笑

  • スライスした結果は新しいリストが生成
  • スライスせずに入れると元のリストのコピー
  • コピー後、代入を行うと要素が入れ替わる

以下、それぞれ見ていく。

■スライスした結果は新しいリストが生成

スライス時は新しいリストが生成されるので、元の値を編集しても影響は出ない。つまり、

a = [1,2,3,4,5,6,7,8,9,10]
b = a[4:]
a[6] = "b"
print(a)
print(b)

とすると、以下のようになる(#以降はわかりやすさのために書いた)。

[1, 2, 3, 4, 5, 6, 'b', 8, 9, 10]   # a
[5, 6, 7, 8, 9, 10]  # b
■スライスせずに入れると元のリストのコピー/コピー後、代入を行うと要素が入れ替わる

今度はスライスせずに入れると、元のリストのコピーになる。そのため、代入を何らかの方法で行うと要素が入れ替わることに注意。

これは、immutableかmutableかが問題で、リストはmutableなオブジェクトなので、このようなことが起きるんですね。この辺りは以下を参照。めちゃめちゃわかりやすいです。

qiita.com


対策は上のサイトでも書かれている通り、以下の三つでしのぐしかない。

  • スライスを使う(例:copylist = somelist[:]):浅いコピー
  • copyモジュールのdeepcopy()関数を使う(import copy, copylist = copy.deepcopy(somelist)):深いコピー
  • for内包表記を使う:浅いコピー

浅いコピーの場合だと、以下のサイトで示されている通り、何もかもコピーするわけではなく、リストの中身が何かを参照している時、その参照先自体はコピーされないのです。

参考:Pythonの浅いコピー、深いコピー – HimaJew Blog

そのため、以下のような感じのことがおきます。

p=[1,2,3]
q=[4,5,6]
r=[7,8,9]
 
A=[p,q,r]
B=[x for x in A]
 
A[0][0]="a"
print(A)
print(B)

上記のコードを実行すると、

[['a', 2, 3], [4, 5, 6], [7, 8, 9]]
[['a', 2, 3], [4, 5, 6], [7, 8, 9]]

となりますので要注意ですね。

pythonで文字列反転

よく使うのでメモ。リストでは、list[start:end:step]という感じなんだけどこれをうまく利用するとこうなるってやつ。

somestring[::-1]

参考:Python で文字列反転 - 似非プログラマの覚え書き

でも、これ次の項目でダメって言われているんご笑