CTC 教育サービス
[IT研修]注目キーワード Python Power Platform 最新技術動向 生成AI Docker Kubernetes
こんにちは、菱沼です。
今回も「きれいなPythonプログラミング(マイナビ出版)」という書籍を利用して学習します。
前回は、文字列フォーマットと、文字列フォーマットの手法のひとつであるf-stringについて学びました。
今回は、スライス構文について学びつつ、リストをコピーする方法について確認していきます。
まずはスライス構文とは何かというところからです。まずは引用文です。
------------------------------------
P.101
6.5 リストのコピー
スライス構文は、新しい文字列やリストを既存のものから簡単に作成することができます。
------------------------------------
スライス構文が使える既存のもの...とは何なのか。
まず、スライスは「順番のあるデータ(並び順を持つデータ)」に対して使える機能だそうです。なので、Pythonでスライス構文が使えるのはシーケンス型(リスト、タプル、文字列など)が対象になります。辞書、集合、イテレータ/ジェネレータ、数値型では使用できません。
スライスが使える、使えない、どっちだっけ...と悩んだときには、「インデックス番号を持つものは基本的にスライスが使える」と覚えておけばいいようです。ちなみに、dir()関数の__getitem__メソッドの有無を調べる方法もあるのだとか。その方法がこちら。
# リストの場合
print("__getitem__" in dir([])) # True -> スライスOK
# 集合(set)の場合
print("__getitem__" in dir(set())) # False -> スライスNG
とはいえ、ここまでせずとも、とりあえず試してみて、「TypeError: ’...’ object is not subscriptable」というエラーが出たら、「あ。これはインデックスアクセスできない型なんだな」と判断すれば問題ないというものでもあります。
さて、では具体的にスライスの方法を確認していきます。文字列やリストなどのシーケンス型は次の書式でスライスできます。
シーケンス[start:stop:step]
※インデックス番号なので、最初の値は0から始まります。最後の値から数えるなら-1からです。
※stopで指定した位置の文字は含まれません。なので、stopに10と書いたら、9までが対象です。
ではここでサンプルコードをもとにどのようにスライス構文を使うのか見てみます。
------------------------------------
P.101
’Hello, world!’[7:12]
’world’
’Hello, world!’[:5]
’Hello’
[’cat’, ’dog’, ’rat’, ’eel’][2:]
[’rat’, ’eel’]
------------------------------------
こちらのサンプルコードでは、それぞれ次の値を取ってくるように指定されています。
文字に対しては、次のようにインデックス番号が振られています。
| インデックス | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 文字 | H | e | l | l | o | , | w | o | r | l | d |
つまり、、、
1つ目はインデックス7~11の5つ = world
2つ目はインデックス0~4の5つ =Hello
3つ目はインデックス2~最後まで(4)の2つ = rat、eel
が取得されます。
という基本を確認したところで次のサンプルコードです。
------------------------------------
P.102
spam = [’cat’, ’dog’, ’rat’, ’eel’]
eggs = spam[:]
eggs
[’cat’, ’dog’, ’rat’, ’eel’]
id(spam) == id(eggs)
False
spamとeggsの識別情報は異なることに注目してください。eggs = spam[:]の行は、spamのリストの内容をコピーしますが、eggs = spamはリストへの参照のみをコピーします。
------------------------------------
先ほどとはまた別のスライス構文が出てきました。
[:]は最初から最後までを意味します。なので、eggs = spam[:]は、spamのリストをすべてコピーして新たにeggsという新しいリストを作るという意味になります。
例えばこれが、eggs = spamだったらeggsはspamを参照することになるため、eggsとspamは同じリストを指すことになります。一方、このサンプルでは同じ内容のリストではあるものの、新しく作られた別のリストということになります。それがid(spam) == id(eggs)で示されているという形になります。
ちなみにこの書き方...。シャローコピー(浅いコピー)と呼ばれるもので、リスト自体は新しく作られるものの、リストの中にさらにリストなどが入っているようなケースではそれらの要素は同じオブジェクトを参照する状態になるようです。なので、もし値を完全にコピーしたいのなら、ディープコピー(深いコピー)の手法を使えば内部の要素も含めて独立したオブジェクトとしてコピーすることができます。
より詳しく知りたい方はこちらにわかりやすくまとめられていましたので、興味がある方はどうぞ!
https://qiita.com/Prosamo/items/56080126142c4e5e4f7f
ちなみに、このサンプルコードの後に、copyモジュールを使った別の書き方が示されています。
------------------------------------
P.102
import copy
spam = [’cat’, ’dog’, ’rat’, ’eel’]
eggs = copy.copy(spam)
id(spam) == id(eggs)
False
このような構文を使っているコードに出会ったときのために知っておくべきですが、自分のコードに書くのはおすすめしません。とにかく、[:]とcopy.copy()はどちらもコピーを作成するものだということは覚えておいてください。
------------------------------------
すすめないんかい。とちょっと思いましたが、こういった書き方もあるということで...。
今回はスライス構文と、スライス構文を使ったコピーについて学びました。
最後にスライス構文の補足です。
Python 3.7以降で辞書は要素を追加した順序を保持する仕様になりましたが、それでも辞書はインデックスを持たないので、スライスはさせてくれません。そんな辞書をなんとしてもスライスしてやると思うのならば、辞書の中身(キーや値)をリスト形式に変えてあげるという手段があるそうです。
例)
data = {"a": 1, "b": 2, "c": 3, "d": 4}
# 1. キーだけ取り出して最初の2つをスライス
keys = list(data.keys())[0:2]
print(keys) # [’a’, ’b’]
# 2. (キー, 値) のセットで最初の2つをスライス
items = list(data.items())[0:2]
print(items) # [(’a’, 1), (’b’, 2)]
大量のデータがあったとき、全部をリストにしてスライスするのは時間がかかって大変です。そういったときには、標準ライブラリ itertools にある islice() を使うことで、イテラブル(リストやジェネレータなど)から必要な範囲だけ取り出すことができるそうです。
具体的な使い方はここでは割愛しますが、以下のリンクにisliceについて詳しく書かれていましたので、気になる方はぜひご参考に。
isliceの紹介、具体例添え(pythonのitertoolsを使いこなすために)
最後の値から取得したいときもあるかもしれません。そういったときは変数名[::-1] のように step を -1 にすると、逆順に取り出すことができます。

それではきりが良いのでこちらで終了です。
今回もお付き合いいただきありがとうございました。
[IT研修]注目キーワード Python Power Platform 最新技術動向 生成AI Docker Kubernetes