シーケンス型(list, tuple, range)

ここではシーケンス(sequence)型のlist、tuple、rangeについて紹介します。

list

まずシーケンス型で一番使われている list から始めます。

listとは

list は[ ] で囲んだ中に、カンマ( , )で区切って一つの変数に複数のオブジェクトを格納します。

weight = [55, 58, 70, 48, 63]
weight, type(weight), len(weight)

([55, 58, 70, 48, 63], list, 5)

上で定義した変数 weight の型(type)は list となります。
リストの長さ(=要素の数)は len関数を使用して確認できます。この場合長さは 5 です。
以下の様に中身が空っぽの list を定義することも可能です。

ls = []
ls, type(ls), len(ls)

([], list, 0)

この場合 list の長さは 0 です。

index

listの中のカンマで区切った一つ一つを要素と言います。そして要素の位置を表す数字をインデックス(index)と言います。
上記で定義した weight の1番目の要素(55)を取り出すには変数の後に四角カッコ[ ]を追加して weight[0] とします。

weight[0]

55

同様に2番目の要素は weight[1] 3番目は weight[2] 最後は weight[4] です。

weight[1], weight[2], weight[4]

(58, 70, 63)

この[ ]の中の数字がインデックス(index)です。お気付きの通り、pythonのindexは0から始まりますので、要素の数が5個の場合一番最後のindexは4となります。
間違ってweight[5]とすると IndexError: list index out of range (listのindexが範囲外)となります。
またindexはint型(整数)ですので[ ]内にfloat(小数点数)を使うと TypeError: list indices must be integers or slices, not float (listのindexは、floatではなくintまたはスライスでなければならない)となります。

一方、indexにマイナス(-)を付けることで、後ろから数えることも出来ます。この場合は一番後ろは-1、後ろから2番目が-2となります。

weight = [55, 58, 70, 48, 63]
weight[-1], weight[-2], weight[-5]

(63, 48, 55)

listの様に、要素には順番がありindexで要素を抽出できるオブジェクトをシーケンス(sequence)型と言います。listは代表的なsequenceですが、他には後ほど述べるtupleとrangeがあります。

listの要素にはオブジェクトが入りますので、数値型(intやfloat)以外にもstr(文字列)やbool(真偽値)も入れることが出来ます。複数種類の型の要素を同じlistに格納することも可能ですが、一般的には同型のデータを格納します。

stations = ['tokyo', 'kanda', 'akihabara', 'okachimachi', 'ueno']
stations

[‘tokyo’, ‘kanda’, ‘akihabara’, ‘okachimachi’, ‘ueno’]

listは要素を部分的に置換することが可能です。例えば上記の例では station[2] = ‘秋葉原’ とすれば、’akihabara’ のみを秋葉原’ に書き換えることが出来ます。

stations[2] = '秋葉原'
stations

[‘tokyo’, ‘kanda’, ‘秋葉原’, ‘okachimachi’, ‘ueno’]

この様に要素の置換が可能なsequenceをミュータブル(mutable)と呼びます。
反対に要素の追加、置換、削除が出来ないsequenceをイミュータブル(immutable)と言います。後ほど述べるtupleやrangeはイミュータブル(immutable)なsequenceです。

スライス(:) コロンが一つのとき

list内の要素を複数抽出する場合には、[]の中にスライス(slice)(:)を使用します。

  • [a:b]と記載するとでaからbの前までの要素を抽出
  • aを省略して[:b]とすると先頭からbの前まで抽出
  • bを省略して[a:]とするとaから最後まで抽出
  • aとbを省略して[:]とすると全てが抽出され、浅い(shallow)コピーとなる。(この浅いコピーについては後ほど説明します)
  • indexの範囲を超えてもエラーにはならず、範囲内にあるものを抽出
  • indexの範囲に全くかからない場合は [] が返る
ls = [10, 11, 12, 13, 14, 15, 16, 17]
print('ls ->', ls)
print('ls[:3] ->', ls[:3])  # 先頭から3の前まで
print('ls[3:6] ->', ls[3:6])  # 3から6の前まで
print('ls[6:] ->', ls[6:])  # 6から最後まで
print('ls[-3:] ->', ls[-3:])  # 後ろから3番目から最後まで
print('ls[:] ->', ls[:])  # 全部(浅いコピー)
# indexを超えた範囲を指定してもエラーにはならない
print('ls[5:10] ->', ls[5:10])  #index範囲内の5~7まで
print('ls[10:20] ->', ls[10:20])  #index範囲を超えているので[]が返る

ls -> [10, 11, 12, 13, 14, 15, 16, 17]
ls[:3] -> [10, 11, 12]
ls[3:6] -> [13, 14, 15]
ls[6:] -> [16, 17]
ls[-3:] -> [15, 16, 17]
ls[:] -> [10, 11, 12, 13, 14, 15, 16, 17]
ls[5:10] -> [15, 16, 17]
ls[10:20] -> []

繰り返しますが、ls[3:6]のとき、index3, 4, 5は含まれますが、6は含まれないことを注意して下さい。
listではスライスを使った複数要素の置換も可能です。

ls = [10, 11, 12, 13, 14, 15, 16, 17]
ls[2:5] = [22, 23, 24]
ls

[10, 11, 22, 23, 24, 15, 16, 17]

スライス(::) コロンが二つのとき

次はコロンが2つあるスライス(::)です。
[a:b:c]の様にコロンが2つある場合は、aからbの前までcの間隔で抽出します。cをマイナスの値にすると後ろから前にindexを減らす方向に進みます。

ls = [10, 11, 12, 13, 14, 15, 16, 17]
print('ls ->', ls)
print('ls[1:6:2] ->', ls[1:6:2])  # 1から6の前まで間隔2で
print('ls[5::-2] ->', ls[5::-2])  # 5から先頭まで間隔2で逆方向に
print('ls[::-1] ->', ls[::-1])  # 最後から先頭までの順に並び替え

ls -> [10, 11, 12, 13, 14, 15, 16, 17]
ls[1:6:2] -> [11, 13, 15]
ls[5::-2] -> [15, 13, 11]
ls[::-1] -> [17, 16, 15, 14, 13, 12, 11, 10]

以下はコロン2個のスライスで抽出した要素を置換する例です。

stations = ['tokyo', 44132, 'fuchu', 44116, 'nerima', 44071]
stations[::2] = ['東京', '府中', '練馬']
stations

[‘東京’, 44132, ‘府中’, 44116, ‘練馬’, 44071]

この場合、stations[::2]によって抽出されるlistの長さ(要素の数)と置換するlistの長さは一致する必要があります。長さが異なる場合はValueErrorとなります。

多次元 list

listの要素にlistを入れると多次元のlistとなります。

  • [ ]や( )の中ではバックスラッシュ(\)無しで改行ができる。但しprintすると一行表示に戻る。
  • 一つ目のindexで子要素、二つ目のindexで孫要素のindexを指定できる。
ls = [[ 0,  1,  2],
      [10, 11, 12],
      [20, 21, 22]]
print('ls ->', ls)
print('ls[1] ->', ls[1])  # index 1 の要素
print('ls[1][2] ->', ls[1][2])  # index 1 の要素の中のindex 2

ls -> [[0, 1, 2], [10, 11, 12], [20, 21, 22]]
ls[1] -> [10, 11, 12]
ls[1][2] -> 12

tupleとrange

list 以外の immutable な sequence である tuple と range について述べます。

tuple

tupleは通常丸括弧()を使って複数の要素を代入します。

height = (165, 168, 180, 158, 173)
height, type(height), len(height)

([55, 58, 70, 48, 63], tuple, 5)

変数 height の型(type)は tuple となります。
indexを使った要素の抽出は、四角括弧 [ ] を使用します。

height[0], height[3], height[-1]

(165, 158, 173)

list がミュータブル(mutable/要素の置換が可能)であるのに対して、tuple はイミュータブル(immutable/要素の追加・置換・削除が不可)なsequenceですので、height[1] = 186 の様に置換しようとすると、TypeError: ‘tuple’ object does not support item assignment(tupleは要素の割り当てをサポートしない) が発生します。

listと違うもう一つの点は、tupleは丸括弧()を省略して定義しても構いません。(listの場合は四角括弧[]を省略してはいけません)つまりtupleかどうかは丸括弧ではなくカンマ(,)の有無で判断されます。

height = 165, 168, 180, 158, 173
height, type(height), len(height)

((165, 168, 180, 158, 173), tuple, 5)

jupyter labの機能として、セルの最後の行にカンマ(,)を入れて変数を並べると、丸括弧で括られて変数の中身が出力される理由は、この最後の一行がtupleとして認識されているためです。

ただし、空の tuple を作成するときは、a = () と丸括弧を記載する必要があります。
また、要素が一つのtupleを作成する時は、a = (10,) もしくは a = 10, と必ず要素の後にカンマ(,)をつける必要があります。

a = 10
print('a = 10 :', type(a))
a = 10, 
print('a = 10, :', type(a))
a = (10)
print('a = (10) :', type(a))
a = (10,) 
print('a = (10,) :', type(a))

a = 10 : <class ‘int’>
a = 10, : <class ‘tuple’>
a = (10) : <class ‘int’>
a = (10,) : <class ‘tuple’>

上記 a = (10) の様にをカンマを省いた場合は、tupleではなく、a = 10 と同じ int になります。

range

range型のsequenceは以下の様にrange関数で表現します。
引数は一つの場合 range(stop)、二つの場合 range(start, stop)、三つの場合は range(start, stop, step)を表します。

表現意味数式( i はindex)
range(stop)0からstopの前まで連続する整数
長さはstop
i
range(start, stop)startからstopの前まで連続する整数
長さは(stop – start)
start + i
range(start, stop, step)startからstopの前までstepの間隔で抽出した整数
長さは(stop – start)//step
start + i * step
表10 range関数

0 から 99 までの整数を表す場合は、range(100)
1 から 100 までの整数を表す場合、range(1, 101)
-10 から 90 までの10個置きの整数を表す場合、range(-10, 91, 10)
中身を確認する場合は、indexで一つ一つ指定するかもしくはlist関数またはtuple関数を使って変換する必要があります。

print(range(10))        #rangeをprintしても中身は確認できない
print(type(range(10)))  #type(変数型)は'range'
print(range(10)[0])     #[index]で要素を一つずつ見ることはできる
print(range(10)[:5])    #スライスによる抽出はrangeで表現される
print(list(range(10)))  #list化すれば中身を確認できる

range(0, 10)
<class ‘range’>
0
range(0, 5)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

上記の通りrange(100)を出力したとしても、range(0, 100) が出力されるだけです。
type(変数型)は’range’となります。
range(10)[0]の様にindexを指定すれば一つずつ確認することはできます。
但し、スライスによる抽出は類似表現のrangeで返されるので中身の確認には使えません。
list関数を使ってlist化すれば、中身を見ることが出来ます。

rangeの目的はメモリの節約です。start + i * step で表される様な簡単なsequenceはわざわざ展開して全要素分のメモリを消費するよりも、start/stop/stepのみをメモリに格納して抽出時に計算して出力した方がメモリの節約になります。
オブジェクトのメモリサイズ(バイト数)は .__sizeof__() というメソッドを使用することで確認できます。

range(1000).__sizeof__(), list(range(1000)).__sizeof__()

(48, 8040)

range(1000)のバイト数は48バイト、これをlist化した場合は8040バイトですので特に長さが大きい場合にメモリ節約の効果は大きい事になります。

浅いコピー、深いコピー

= でコピーは作れない

要素毎の書き換えが可能(mutable)なlistでは、= を使って別の名前の変数を作成してもコピーをした事にはなりません。
例えば、la = [10, 20, 30] を定義して lb = la とします。その後に la の要素を一部書き換えると、lb の要素も書き変わります。

la = [10, 20, 30]
lb = la
la[1] = 22
la, lb, id(la), id(lb)

([10, 22, 30], [10, 22, 30], 140188826991104, 140188826991104)

また lb の要素を書き換えても la の要素が同様に書き変わります。
pythonでは、la = [10, 20, 30] は、list型オブジェクト[10, 20, 30]をメモリ上に確保して、変数 la と結び付けた(binding)という意味になります。その次の lb = la は la と結びついているオブジェクト[10, 20, 30] と変数 lb を結び付けることを意味しています。
従って la も lb も同じlist型オブジェクト[10, 20, 30]を表す変数ですので、la[1] = 22 で要素を変更するとその変更内容は lb にも反映される事になります。
id()関数は括弧内のオブジェクトのid(実際はメモリの番地)を返す関数ですが、id(la)とid(lb)が同じ 140…104 である、つまり la と lb は同じオブジェクトと結びついていることが確認できます。

では同じことをlistではなくintで実行してみましょう。

a = 10
b = a
print(a, b, id(a), id(b))
a = 20
print(a, b, id(a), id(b))

10 10 140188806343248 140188806343248
20 10 140188806343568 140188806343248

a = 10 を定義し、b = a とすると、a も b も同じid 140…248 のオブジェクトに結び付くところまではlistと同じです。しかしその後 a = 20 と再定義した時点で、a のid(番地)は 140…568 に変わります。つまり a = 20 は以前のオブジェクト10(id: 140…248)を書き換えたのではなく、改めてオブジェクト20(id: 140…568)と結びつき、オブジェクト10との結び付きは解かれた(unbound)事になります。結果として b の値は a の変更の影響を受けずに10のままとなります。
同様にイミュータブル(immutable/要素の追加・置換・削除が不可)なsequenceであるtupleやrangeもintと同じ様に変更する場合には全体を再定義する必要があり、その際にオブジェクトのidが変わる事になります。
また数値型のオブジェクト(int, float, boolなど)や文字列は、この様にオブジェクトの追加・置換・削除ができないイミュータブル(immutable)なオブジェクトに分類されます。

コピーを別に取っておきたい時などに使用するのが、以下に説明する浅いコピーと深いコピーです。
浅いコピーと深いコピーで違いが出るのは、listの要素にlistを使用する場合です。共に新しいオブジェクトを作成するのは同じです。

  • 浅いコピーの場合は子要素以下の要素についてはコピー前と同じidのオブジェクトを使用します。
  • 一方深いコピーの場合は子要素以下の全ての要素についても新しいオブジェクトを作成します。

浅いコピー

listの浅いコピーは次の3つの方法で実行できます。ここでは2番の.copy() メソッドを使用します。理由は、1番のスライス[:]よりもコピーしたという意味が明示的で分かり易いこと、3番のimportの必要がないことです。

  1. スライスの[:]を使用する。 例)la_copy = la[:]
  2. listの .copy() メソッドを使用する。 例)la_copy = la.copy()
  3. copy モジュールの copy関数を使用する。
    • 例)
    • import copy
    • la_copy = copy.copy(la)

ちなみにモジュールの copy は浅いコピーや深いコピーの関数を行うためにpythonに標準で備わっているオプションモジュールの一つです。 オプションなのでimport文でcopyを呼び出す必要があります。実際使用する場合は、copy.copy(la)の様に モジュール名.関数名(list) という形で使用します。

la = [10, 11, 12]
lb = [20, 21]
lc = [la, la, lb]
lc_copy = lc.copy()
lc = [la, lb, lb]
print('lc:', lc, ' lc_copy:', lc_copy)
la[1] = 1
print('lc:', lc, ' lc_copy:', lc_copy)

lc: [[10, 11, 12], [20, 21], [20, 21]] lc_copy: [[10, 11, 12], [10, 11, 12], [20, 21]]
lc: [[10, 1, 12], [20, 21], [20, 21]] lc_copy: [[10, 1, 12], [10, 1, 12], [20, 21]]

まず la と lb というlistを定義して、それを要素にもつ lc = [la, la, lb] を作成します。
次にlc[:]を使って浅いコピーのlc_copyを作成し、lc = [la, lb, lb] と変えてみます。
その状態で中身を確認したのが最初のprint文で、期待通りに lcは変更され lc_copy は前の状態が保持されています。
2番目のprint文では、lc および lc_copy の要素の一つである la の一部を変更した場合の中身を確認しています。 la の変更は lc にも lc_copy にも反映されます。この様に要素の修正をオリジナルである lc にもコピーである lc_copy にも反映させたい場合は、浅いコピーが有効です。

一方、以下の様に、lc = [[10, 11, 12], [20, 21]] の lc[0][1] だけを修正したいが、lc_copyは修正したくない場合はどうでしょうか。

lc = [[10, 11, 12], [20, 21]]
lc_copy = lc.copy()
print(lc, lc_copy)
lc[0][1] = 31
print(lc, lc_copy)

[[10, 11, 12], [20, 21]] [[10, 11, 12], [20, 21]]
[[10, 31, 12], [20, 21]] [[10, 31, 12], [20, 21]]

浅いコピーの場合、要素となるオブジェクトはオリジナルと同じものを使用しますので、lc[0][1] = 31としただけで、lc_copy[0][1]の方も31になってしまいます。
この様に lc_copyの方に影響を与えたく無い場合は以下に述べる深いコピーを使用します。

深いコピー

listの深いコピーは copyモジュールの deepcopy関数を使用します。上記の後者のケースを深いコピーで実行してみましょう。

# copy モジュールの読み込み(import)
import copy

lc = [[10, 11, 12], [20, 21]]
lc_copy = copy.deepcopy(lc)
print(lc, lc_copy)
lc[0][1] = 31
print(lc, lc_copy)

[[10, 11, 12], [20, 21]] [[10, 11, 12], [20, 21]]
[[10, 31, 12], [20, 21]] [[10, 11, 12], [20, 21]]

深いコピーの場合は、lc の要素である [10, 11, 12] や [20, 21] も含めて新規に作成します。なので lc[0][1] = 31 で要素の [10, 11, 12] の一部を修正したとしても lc_copy には影響しません。

反対に以下の例は、前節浅いコピーの前半の例の様に、要素の修正をコピー先にも反映したい場合に深いコピーを使ってしまった場合です。

import copy

la = [10, 11, 12]
lb = [20, 21]
lc = [la, la, lb]
lc_copy = copy.deepcopy(lc)
lc = [la, lb, lb]
print('lc:', lc, ' lc_copy:', lc_copy)
la[1] = 1
print('lc:', lc, ' lc_copy:', lc_copy)

lc: [[10, 11, 12], [20, 21], [20, 21]] lc_copy: [[10, 11, 12], [10, 11, 12], [20, 21]]
lc: [[10, 1, 12], [20, 21], [20, 21]] lc_copy: [[10, 11, 12], [10, 11, 12], [20, 21]]

このように深いコピーを使えば、要素の修正 la[1] = 1 はコピー元の lc のみ修正され、コピー先の lc_copy には反映されません。

コピーまとめ

以上の様に、list にはその構造上、浅いコピーと深いコピーの2種類が存在し、コピーの目的によりどちらかを使用するのかを判断する必要があります。以下はコピーに関するまとめです。

  • 変数同士を b = a の様に = でコピー定義しても、コピーされるのはオブジェクトの参照番地(id)のみであり、オブジェクトの実際の中身はコピーされない。
  • list のような変更可能な mutable オブジェクトでは、コピー元またはコピー先のどちらかの要素を修正するとコピー元とコピー先の両方が修正される。一方その他 immutableオブジェクトは、変更した時点で新規作成されるのでこの問題は起こらない。
  • list にはその構造上、浅いコピーと深いコピーの2種類が存在し、コピーの目的によりどちらかを使用するのかを判断する必要がある。
  • 要素が全てimmutableであればどちらを使っても同じなので浅いコピーを使う。浅いコピーにはやり方が複数あるが、明示的にコピーが分かる.copy()メソッドがお勧め。
  • 一つでもmutableな要素がある場合、その要素の修正をコピー元とコピー先の両方に反映したい場合は浅いコピーを使用する。
  • mutableな要素の修正をコピー元もしくはコピー先のどちらか修正した方のみに留めておきたい、修正しない方には反映させたくない場合に深いコピーを使用する。

シーケンス(sequence)型の演算

sequence 共通の演算

以下はsequence(list, tuple, range)に共通のimmutableな(追加、置換、削除を含まない)演算です。
これらの関数やメソッドをこの時点で覚える必要はありません。一通り目を通して頂いてこんな機能があるんだということを頭の片隅に置いておけば、後々実際に必要になった時に調べる時間が短縮されるかと思います。

代数演算子結果注釈
x in ss のある要素が x と等しければ True
そうでなければ False
(1)
x not in ss のある要素が x と等しければ False
そうでなければ True
(1)
s + ts と t の結合(2)
s * n または n * ss 自身を n 回足すのと同じ(2)
s[i]s の 0 から数えて i 番目の要素上記index参照
s[i:j]s の i から j までのスライス上記スライス参照
s[i:j:k]s の i から j まで、 k 毎のスライス上記スライス参照
len(s)s の長さ(3)
min(s)s の最小の要素(3)
max(s)s の最大の要素(3)
s.index(x[, i[, j]])s 中で x が最初に出現するindex
(index i 以降からindex j までの範囲)
(4)
s.count(x)s 中に x が出現する回数(5)
表11 共通のシーケンス演算
参照元: https://docs.python.org/ja/3/library/stdtypes.html#sequence-types-list-tuple-range

(1) in, not in

x in s は list s に 要素 x が含まれていれば True 含まれていなければ False となるbool値を返す演算です。
反対に x not in s は list s に 要素 x が含まれていれば False 含まれていなければ True となるbool値を返す演算です。

ls = [10, 11, 12, 13, 14, 15, 16, 17]
print('ls ->', ls)
print('10 in ls ->', 10 in ls)
print('20 in ls ->', 20 in ls)
print('10 not in ls ->', 10 not in ls)
print('20 not in ls ->', 20 not in ls)

ls -> [10, 11, 12, 13, 14, 15, 16, 17]
10 in ls -> True
20 in ls -> False
10 not in ls -> False
20 not in ls -> True

(2) 足し算と整数倍

文字列の演算と同様に、足し算はlistの結合、整数倍はlistの繰り返し連結となります。
足し算と整数倍以外はTypeErrorとなります。エラーを確認する場合はcntl+/(⌘+/)でコメントアウトを戻して実行して下さい。

ls1 = [10, 11, 12]
ls2 = [20, 21, 22]
print('ls1 + ls2 ->', ls1 + ls2)
# print('ls1 - ls2 ->', ls1 - ls2)  # TypeError
# print('ls1 * ls2 ->', ls1 * ls2)  # TypeError
print('ls1 * 2 ->', ls1 * 2)
# print('ls1 * 2.0 ->', ls1 * 2.0)  # TypeError
# print('ls1 / ls2 ->', ls1 / ls2)  # TypeError

ls1 + ls2 -> [10, 11, 12, 20, 21, 22]
ls1 * 2 -> [10, 11, 12, 10, 11, 12]

(3) len, min, max

listの要素の数を出力する組み込み関数は len です。lenは長さを表すlengthからきています。
maxとminはそれぞれlistの要素のうちの最大値、最小値を返します。文字列の場合は文字のUnicodeのコードポイントの大小を判定します(Unicodeのコードポイントはord関数で確認できます)

ls1 = [10, 11, 12, 13, 14, 15, 16, 17]
print('ls1 ->', ls1)
print('len(ls1) ->', len(ls1))
print('min(ls1) ->', min(ls1))
print('max(ls1) ->', max(ls1),'\n')

ls2 = ['0', 'A', 'a']
print('ls2 ->', ls2)
print('code point ->', ord('0'), ord('A'), ord('a'))
print('len(ls2) ->', len(ls2))
print('min(ls2) ->', min(ls2))
print('max(ls2) ->', max(ls2))

ls1 -> [10, 11, 12, 13, 14, 15, 16, 17]
len(ls1) -> 8
min(ls1) -> 10
max(ls1) -> 17

ls2 -> [‘0’, ‘A’, ‘a’]
code point -> 48 65 97
len(ls2) -> 3
min(ls2) -> 0
max(ls2) -> a

(4) .index(x[, i[, j]])

この.index()の様に、この場合sequenceやsequenceの変数名の後ろにピリオド(.)を付けてその後に配置して関数の様に処理を行うものをメソッド(method)と言います。メソッドの定義の仕方についてはクラス(class)という概念と一緒に応用編で紹介したいと思います。
s.index(x[, i[, j]])の括弧内のx[, i[, j]]は[ ]内は省略できるという意味です。詳しくは以下となりますが、公式ドキュメントによく出る記載法ですので覚えておいた方が良いと思います。

  • xは省略できないので括弧内が一文字の時は x を表し、全要素に対して x が最初に現れるindexを返す
  • 括弧内が二文字の時は x, i を表す。つまりindex i 以降に対して x が最初に現れるindexを返す
  • 括弧内が三文字の時は x, i, j を表、index i 以降 j までに対して x が最初に現れるindexを返す
ls = [10, 11, 12, 13, 10, 11, 12, 13]
print('ls ->', ls)
print('ls.index(13) ->', ls.index(13))
print('ls.index(13, 4) ->', ls.index(13, 4))
# print('ls.index(13, 4, 7) ->', ls.index(13, 4, 7))  #ValueError

ls -> [10, 11, 12, 13, 10, 11, 12, 13]
ls.index(13) -> 3
ls.index(13, 4) -> 7

ls.index(13)は lsの中で13が最初に現れるindexですので 3 となります。
ls.index(13, 4)は index 4 以降で13が最初に現れるindexですので 7 となります。
ls.index(13, 4, 7)は index 4 以降 7 の前迄に13は存在しませんので、ValueErrorとなります。確認する場合は最後の行のコメントアウト(#)をctrl+/(⌘+/)で外して下さい。

(5) .count(x)

s.count(x)は sequence s の中に要素 x が何個あるかを返すメソッドです。

pets = ['dog', 'cat', 'cat', 'dog', 'fish']
pet1 = 'dog'
print('pets.count(pet1) ->', pets.count(pet1))
print("pets.count('turtle') ->", pets.count('turtle'))

pets.count(pet1) -> 2
pets.count(‘turtle’) -> 0

list pets の中には pet1 = ‘dog’ は2個ありますが、’turtle’ は0個です。

ミュータブルsequence の演算

以下は、ミュータブル(mutable/要素の置換が可能)なsequence(これ迄では list が該当)に適用される演算です。
従ってtupleやrangeに適用するとTypeErrorが発生します。こちらも同様に関数名やメソッド名は忘れても問題ありません。こんなことが可能なのだということが分かればよろしいかと思います。
s はlist、t は複数の要素を持つオブジェクト、x は要素のオブジェクト、i はindex、nは整数です。

代数演算子結果注釈
s[i] = xs の要素 i を x と置換上記 list 参照
s[i:j] = ts の i から j 番目までの要素を t に置換上記 list 参照
del s[i:j]s[i:j]の部分を削除 (s[i:j] = [] と同じ)(1)
s[i:j:k] = ts[i:j:k] を t に置換上記 list 参照
del s[i:j:k]s[i:j:k]の部分を削除(1)
s.append(x)要素 x を最後に追加(2)
s.clear()s から全ての要素を削除 (del s[:] と同じ)(3)
s.copy()s の浅いコピーを作成 (s[:] と同じ)上記浅いコピー参照
s.extend(t) 
または s += t
t (複数要素)を追加(2)
s *= ns を n 回繰り返したもので更新(4)
s.insert(i, x)インデックス i の位置に x を挿入 (s[i:i] = x と同じ)(5)
s.pop([i])s から i 番目の要素を削除しその削除した要素を返す(6)
s.remove(x)x に一致する最初の要素を削除(7)
s.reverse()s を後ろから並び替える (これをインプレースに行う)(8)
表12 mutableシーケンス演算
参照元: https://docs.python.org/ja/3/library/stdtypes.html#immutable-sequence-types

(1) del s[i:j]

del s[i:j] により sequence s の中のスライスによって指定されたindexの要素が削除されます。

pets = ['dog', 'cat', 'cat', 'dog', 'fish']
del pets[2:4]
pets

[‘dog’, ‘cat’, ‘fish’]

上記例では、pets[2:4]で指定された、index 2 の ‘cat’ と index 3 の ‘dog’ が削除されています。
以下はコロンが2つのスライスで指定した要素を削除する場合です。

stations = ['tokyo', 44132, 'fuchu', 44116, 'nerima', 44071]
del stations[1::2]
stations

[‘tokyo’, ‘fuchu’, ‘nerima’]

del文はlist以外にも様々なオブジェクトを削除できます。メモリに余裕がない時に、del 変数名 で変数を削除し、その変数が使っていたメモリを解放することが出来ます。

(2) s.append(x) s.extend(t)

s.append(x)は要素xをsの末尾に追加するメソッドです。

stations = ['tokyo', 'fuchu', 'nerima']
stations.append('oume')
stations

[‘tokyo’, ‘fuchu’, ‘nerima’, ‘oume’]

listに対応するメソッドに共通することですが、stations = stations.append(‘oume’) と代入し直す必要はありません。上記二行目の stations.append(‘oume’) を実行すれば、stationsに要素’oume’が追加されます。
ここで試しに append を使って二つ以上の要素を追加してみます。

stations = ['tokyo', 'fuchu', 'nerima']
stations.append(['oume', 'haneda'])
stations

[‘tokyo’, ‘fuchu’, ‘nerima’, [‘oume’, ‘haneda’]]

するとエラーは発生しないのですが、[‘oume’, ‘haneda’] というlistが一つの要素として追加されていることが判ります。もし’oume’と’haneda’を別々の要素として追加したのであれば、s.extend(t)を使用する必要があります。

stations = ['tokyo', 'fuchu', 'nerima']
stations.extend(['oume', 'haneda'])
stations

[‘tokyo’, ‘fuchu’, ‘nerima’, ‘oume’, ‘haneda’]

これは s + t すなわち [‘tokyo’, ‘fuchu’, ‘nerima’] + [‘oume’, ‘haneda’] に他なりません。上記表12にもあります様に以下の演算でも実行可能です。

stations = ['tokyo', 'fuchu', 'nerima']
stations += ['oume', 'haneda']
stations

[‘tokyo’, ‘fuchu’, ‘nerima’, ‘oume’, ‘haneda’]

(3) s.clear()

s.clear()はsの全要素を削除するメソッドです。del s[:] と同じです。

stations = ['tokyo', 'fuchu', 'nerima']
stations.clear()
stations

[]

クリア後は空のlist [] となります。

(4) s *= n

s を n 回繰り返したもので更新されます。

la = [0]
la *= 10
la

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

*= は累算代入演算子で、la *= 10 は la = la*10 と同じ意味です。

(5) s.insert(i, x)

インデックス i の位置に x を挿入します。置換ではなく挿入なので s の長さは一つ増えます。

la = [10, 11, 12, 14, 15]
print('la before insert:',la, ' len(la):', len(la))
la.insert(3, 13)
print('la after insert:',la, ' len(la):', len(la))

la before insert: [10, 11, 12, 14, 15] len(la): 5
la after insert: [10, 11, 12, 13, 14, 15] len(la): 6

s[i:i] = x としても同様に i の位置に x を挿入することが出来ます。

(6) s.pop([i])

s からindex i の要素を取り出します。言い換えれば s のindex i の要素を削除しその削除した要素を返します。
s.pop([i]) の [i] は省略可能の意味です。省略時は i = -1 つまり一番最後の要素を取り出します。

la = [10, 11, 12, 13, 14, 15]
x = la.pop(2)
x, la

(12, [10, 11, 13, 14, 15])

pop が append や insert など他のメソッドと異なる点は戻り値があることです。従って、x = la.pop(2) の様に取り出した要素を代入することが出来ます。戻り値が無いメソッドで x = la.insert(3, 13)とすると None が返されます。

(7) s.remove(x)

x に一致する最初の要素を削除します。

pets = ['dog', 'cat', 'cat', 'dog', 'fish']
pets.remove('dog')
pets

[‘cat’, ‘cat’, ‘dog’, ‘fish’]

上記の場合、’dog’ が2つありますので、index 0の方が削除されています。もし pets.remove(‘turtle’) の様に s に無い要素を指定するとValueErrorとなります。

(8) s.reverse()

s の要素を後ろから並び替えます。

la = [10, 11, 12, 13, 14, 15]
la.reverse()
la

[15, 14, 13, 12, 11, 10]

「これをインプレース(in-place)に行う」という意味は、実行時に追加のメモリ使用量を最小限にして並べ替えを行うことです。同じid(同じメモリ)上で並べ替えを行うため、並べ替え前後でidは変化しません。

la = [10, 11, 12, 13, 14, 15]
print(id(la))
la.reverse()
print(id(la))

140188672907776
140188672907776

上記の様にreverse 前後でid(メモリ番地)は同じ値となります。
一方、スライスを使って要素を後ろから並び替えることも可能です。

la = [10, 11, 12, 13, 14, 15]
print(la, id(la))
la = la[::-1]
print(la, id(la))

[10, 11, 12, 13, 14, 15] 140188675689280
[15, 14, 13, 12, 11, 10] 140188675123200

しかしその場合は、上記の通り異なるid(メモリ番地)に格納することになり、並べ替えには一時的に2倍のメモリ容量が必要となります。サイズの大きいデータを並び替える場合は、.reverse()を推奨します。

sort(*, key=None, reverse=False)(list 専用のメソッド)

sortメソッドの使い方

前述のmutable sequence用のメソッドとは違い、sortはlistのみに定義された要素を並び替えるためのメソッドです。key と reverse の2つの引数がありますが、何も指定しない場合は数字の場合は小さい順、str(文字列)の場合は先頭文字から文字コードの小さい順(アルファベット順)に並び替えられます。

la = [-10, 15, -12, 11, -14, 13]
la.sort()
la

[-14, -12, -10, 11, 13, 15]

reverse = True にすると上記とは逆に大きい順となります。

la = [-10, 15, -12, 11, -14, 13]
la.sort(reverse = True)
la

[15, 13, 11, -10, -12, -14]

key = abs とすると絶対値が小さい順に並べ替えられます。

la = [-10, 15, -12, 11, -14, 13]
la.sort(key=abs)
la

[-10, 11, -12, 13, -14, 15]

absは組み込み関数なのでそのまま使えますが、自分でkeyを作る場合はdef文により関数を定義するか、もしくはlambda式を使う必要があります。(lambda式については後述します)

# 関数を定義する場合
def func(x):
    return abs(x-1)

la = [-10, 15, -12, 11, -14, 13]
la.sort(key=func)
la

[11, 13, 15, -10, -12, -14]

# lambda式を使う場合
la = [-10, 15, -12, 11, -14, 13]
la.sort(key=lambda x: abs(x-1))
la

[11, 13, 15, -10, -12, -14]

sorted関数との違い

listの要素を並べ替える方法として、組み込み関数の sorted(iterable, *, key=None, reverse=False) があります。関数とメソッドの違いがありますが、引数は同じ(keyとreverse)なので基本的な考え方は同様です。

la = [-10, 15, -12, 11, -14, 13]
la = sorted(la, key=abs, reverse = True)
la

[15, -14, 13, -12, 11, -10]

但し、sortメソッドとsorted関数の違いは、前期のsortメソッドがメモリ容量の一時的な増大を抑えたインプレース(in-place)な置き換えであるのに対して、sortedは一時的に2倍のメモリが必要となります。

シーケンスの比較

list や tuple のシーケンスで同じ型の比較演算は同じシーケンス型の str に準じた形となります。
始めに最初(Index 0)の要素の大小を比較して、差がない場合は次の要素(Index 1)を比較する方法をとります。

[1, 2, 3] < [1, 2, 4], (1, 2, 3, 4) < (1, 2, 4), (1, 2) < (1, 2, -1)

(True, True, True)

'ABC' < 'C' < 'Pascal' < 'Python'

True

lambda式

lambda式は、小さな関数を作成して文中に埋め込む場合に使用します。
まず小さな関数 y = (x – 1)**2 をdef文で表した場合を示します。

def y(x):
    return (x-1)**2
y(3)

4

次にlambda式で同じことを記載してみます。ここで注意ですが、pythonではこの使い方を推奨しておりません。lambda式の書き方を説明するのに分かり易いのであえて記載します。

y = lambda x: (x-1)**2
y(3)

4

lambda式の書き方は、lambda の後に引数を記載します。引数が複数ある場合はカンマで区切ります。その後にコロン(:) を置いて戻り値を記載します。一行で表すことができる関数はこの様に lambda式を使って表すことができます。

ところが、pythonではlambda式を上記の様に関数名(ここではy)に代入して使用するのでは無く、無名の関数として使用することを推奨しています。
では無名関数として lambda式を使用してみましょう。代表的な例は、前節のsortメソッドやsorted関数の様に、引数に関数を用いるメソッドや関数に使用する場合です。

la = [-10, 15, -12, 11, -14, 13]
la.sort(key=lambda x: (x-1)**2, reverse = True)
la

[-14, 15, -12, 13, -10, 11]

このsort関数の意味は、各要素をxに代入したとき (x-1)**2 が大きい順に並べ替えるという意味になります。

タイトルとURLをコピーしました