Pythonのsequenceについて

Pythonではlist , tuple , str , rangeオブジェクトを多用してプログラムを書きますが、これらはすべてsequence(シーケンス)と呼ばれており、共通の性質を持っています。ここでは、その性質について詳しく見ていきましょう。

Pythonのsequenceとは?

collections.abc.Sequenceという抽象基底クラスがあり、この派生クラスをsequenceと呼びます。 abc は Abstract Base Class(抽象基底クラス)の略です。

sequenceはiterableです。 iterableは、for x in y の yのところに書けるものだと思って良いでしょう。詳しくはこちらの記事を参考にどうぞ。→ Pythonのiterableについて

sequenceの代表的なものは?

sequenceの代表的な組み込み型として、list , tuple , str , rangeオブジェクトがあります。

意外かも知れませんが、str(文字列)型もsequenceの一種であるので、str型のオブジェクトに対しても以下で紹介するようなシーケンス演算が一通り行えます。

sequenceは何ができるの?

__len__() メソッド : シーケンスの長さが返ります。
__gettiem__() メソッド : 整数インデックスによる効率的な要素アクセスをサポートします。

そのほかにもたくさんのメソッドが定義されています。これらのメソッドを通例、直接呼び出すことはありません。他のメソッドなどが内部的にこれらのメソッドを呼び出します。

以下ではlen()関数がどのように実現されているか見てみましょう。

sequenceに対するlen()

組み込みのlen()関数は、内部的には__len__()メソッドを呼び出します。

class MySequence:
  def __len__(self)->int:
     return 123

seq = MySequence()
print(len(seq)) # 123 と表示される

sequenceの__len__()メソッドは、組み込み型であればシーケンスの長さを返すように実装されているはずなので、シーケンスの長さをlen()関数によって調べることができます。例えば、次の例では、文字列の長さをlen()関数で調べています。

s = "ABC"
print(len(s)) # 3 と表示される

sequenceの派生クラスでなくとも、__len__()メソッドさえ持っていればlen()関数から使えるところが面白いですね。

sequenceに対するインデックスによる要素の取得

sequenceの__getitem__()メソッドは、インデックスによる要素の取得を実現します。

listやtupleに対して、x[n]のように角括弧を使ってn番目の要素(nは0番目から始まるとする)を取得できますが、角括弧による要素へのアクセスは、内部的には、__getitem__()メソッドの呼び出しによって実現されています。

次の例は、この角括弧を用いたときに__getitem__()メソッドの呼び出しが行われていることを示す例です。(本来は、シーケンスのn番目の要素を返すような実装をするべきですが、ここでは__getitem__()メソッドが呼び出されていることを確認するためにこのような実装にしています。)

class MySequence:
    def __getitem__(self,key:int)->int:
        return key*5

x= MySequence()
print(x[10]) # 50と表示される

sequenceの派生クラスでなくとも、__getitem__()メソッドさえ持っていれば、y = x[10]のように角括弧が使えるところが面白いですね。

sequenceに対するインデックスによる値の代入

sequenceの__setitem__()メソッドは、インデックスによる要素への代入を実現します。

listやstrに対して、x[n] = 10のように角括弧を用いてn番目(nは0番目から始まるとする)の要素に代入を行えますが、これは内部的には__setitem__()メソッドの呼び出しによって実現されています。

class MySequence:
    def __setitem__(self,key:int,value:int):
        print(key , value)

x = MySequence()
x[5] = 10  # 5 10 と表示される

これもsequenceの派生クラスでなくとも__setitem__()メソッドさえ持っていれば x[5] = 10のような代入が行えます。

ただし、代入は要素が書き換わるので、mutable(更新可能)な型でなければこのメソッドを持っていないはずです。list型はmutableなのでこのメソッドを持っていますが、tuple型 , str型はimmutable(更新不可)なので、このメソッドを持っていません。

それゆえ、str型のオブジェクトにs[3] = “A”のような代入をしようとすると実行時にTypeError ” ‘str’ object does not support item assignment”という例外がでます。これは、str型が__setitem__()というメソッドを持っておらず、このメソッドを呼び出せなかったために生じた例外です。

sequenceはNoneか空のとき、falsyである

sequenceをboolに変換してみましょう。次の例では、”ABC”(これはstr型であり、str型はsequenceです)をboolに変換しています。

b = bool("ABC")
print(b) # Trueと表示される

同様に、if文の条件式ではbool型が要求されるので、上のような型変換が行われていると考えられます。この型変換で呼び出される関数が__bool__()という組み込みメソッドです。

詳しくは、こちらの記事を御覧ください。→ Pythonでthuthyとfalsyとは何か?

sequence同士の足し算

sequence同士の足し算は連結になります。

x = "ABC"
y = "DEF"
print(x+y)
# ABCDEF

x = [1,2,3]
y = [4,5,6]
print(x+y)
# [1, 2, 3, 4, 5, 6]

sequenceとintの掛け算

sequenceに対するintの掛け算は、その回数だけ反復するsequenceになります。

x = "ABC"
y = 3
print(x*y)
# ABCABCABC

x = [1,2,3]
y = 3
print(x*y)
# [1, 2, 3, 1, 2, 3, 1, 2, 3]

sequenceと0の掛け算

sequenceに対して0を掛けると空のsequenceになります。

x = "ABC"*0 + "DEF"
print(x)
# DEF

x = [1,2,3]*0 + [4,5,6]*1
print(x)
# [4, 5, 6]

Pythonでは、Trueは1、Falseは0と等価なので、次のようにしてtがTrueであるかFalseであるかによって表示する文字列を切り替えることができます。(あまり褒められた書き方とは言えないですが…)

t = True
print("ABC"*t + "DEF"*(not t))
# ABC

t = False
print("ABC"*t + "DEF"*(not t))
# DEF

sequenceに対する+=演算子による連結

a += b は、 a = a + b と等価なので、上の結論を利用すると次のように書けます。

a = [1,2,3]
a += [4] # これは連結(append)
print(a)
# [1, 2, 3, 4]

a = (1,2,3)
a += 4,
print(a)
# (1, 2, 3, 4)

あと、listには、tupleを += で連結できます。(このような連結はstr型は不可) これは、listの場合、+= 演算子の右側にiterableが書けるようになっているためです。

a = [1,2,3]
a += 4, # tuple
print(a)
# [1, 2, 3, 4]

a = [1,2,3] + (4,)
# TypeError : can only concatenate list (not "int") to list

a = [1,2,3]
a += "ABC"   # これは、"ABC"がiterableとみなされて連結される。
print(a)
# [1, 2, 3, 'A', 'B', 'C']

a = [1,2,3] + "ABC"
# TypeError : can only concatenate list (not "str") to list

a = [1,2,3]
a.append("ABC")
print(a)
# [1, 2, 3, 'ABC']

list型のxに関して
x = x + y
x += y
x.append(y)
は、それぞれ挙動が違うことに注意しましょう。(上例)

  • x = x + y は、yもlistである必要があります。(動作はlistの連結)
  • x += yは、yがiterableであれば良く、iterableから1つずつ要素を取り出し、それをxの末尾に追加していきます。
  • x.append(y)は、yの型は問いません。xの末尾にyが追加されます。

⚠ x = x + y と x += y との挙動が異なる言語は珍しいですね。前者もyにiterableを取れるようにすべきのような気がしますが。

また、str型もsequenceなので+=で連結ができます。

a = "ABC"
a += "D"
print(a)
# ABCD

条件を満たしたときにlistにappend

上述した、listに += 演算子で連結(append)することができるので、*演算子と組み合わせると条件式が成立したときにだけappendすることができます。(あまり良い書き方とは言えないですが…)

a += [5]*条件式
# 条件式がTrueのとき、右辺は[5]*1とみなされて、5がaにappendされる。

(Visited 22 times, 1 visits today)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です