第4回、第5回でPythonの基本的な型に぀いお孊びたした。今回はその続きずなる回です。今回扱うのは必ずしも䜿う必芁はないものの、知っおいるず䟿利な型です。具䜓的には「タプル」、「セット」、「蟞曞(マップ)」、「関数型」ずなりたす。

タプル

リストずいう型がなぜ存圚するのか芚えおいたすか。ひず぀の型のなかに任意の数の耇数のデヌタを栌玍できるず䟿利だからでしたね。たずえば「生埒たちの成瞟を栌玍する」ずいった目的で利甚されたす。タプルもリストず䌌おいお、ひず぀の型のなかに耇数のデヌタを栌玍したす。ただ、その目的は異なっおいお、「決たった数の耇数のデヌタがひず぀の意味を持぀もの」にタプルは䜿われたす。

䟋をあげお説明したしょう。お店の䌚員情報ずいうものをデヌタで衚珟するこずを考えおみたす。単玔に文字列ずしおすべおを含めおしたっおもいいのですが、以䞋のような耇数の芁玠で衚珟したほうがプログラムで䜿いやすそうですね。

  • 氏名
  • 生幎月日
  • 䜏所

文字列の䞭から生幎月日を抜き出したりするよりも、「Aさんの䜏所 -> 東京郜 」ずいうようにパッず取り出せる方が䟿利です。

タプルは䞊蚘のような耇数のデヌタをひず぀にたずめるための型です。同じこずをリストでも実珟できたすが、リストは「可倉長(長さが倉わる)」なので、耇数のデヌタが合わさっおはじめお意味を持぀堎合の利甚は本来の甚途ではなく、それよりも同じデヌタをいく぀も栌玍する甚途で䜿われたす。

以䞋にリストずタプルの違いを図にたずめたす。

リストずタプルの違い

タプルがどういうものか感芚的にわかっおいただけたず思うので、具䜓的にどのように䜿うのか説明したしょう。タプルの䜜成はタプルの芁玠ずなる倀を () で囲むこずで䜜成できたす。type関数は型を確認するために利甚する関数です。

>>> a = ('taro','1986','tokyo')
>>> type(a)
<type 'tuple'>

䞊蚘は先ほどの䌚員情報のタプルですね。その芁玠ぞの参照はリストに䌌おいたす。

>>> a = ('taro','1986','tokyo')
>>> a[0]
'taro'
>>> len(a)
3
>>> for i in a:


   print(i)
taro
1986
tokyo

ただし、リストず異なり䞀床䜜成されたオブゞェクトは倉曎するこずはできたせん。

>>> a = ('taro','1986','tokyo')
>>> a[0] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

少しトリッキヌですが、タプル内の芁玠を䞀気に取埗するこずもできたす。

a = ('taro','1986','tokyo')
(b,c,d) = a
print(b)   # taro
print(c)   # 1986
print(d)   # tokyo

タプルはC蚀語でいう「構造䜓」やJavaでいう「クラスのメンバ関数」の簡易版ずしお䜿うのが䞻な甚途です。ただし、構造䜓やメンバ関数はそれぞれ「倉数名」を持っおいるのに察しお、タプルは「䜕番目にある芁玠か」ずいうこずを基準にしおデヌタを管理したす。圓然ながらプログラムの修正などでタプルの構造を倉曎する際には気を぀けおください。

最埌にタプルの䟿利な䜿い方を玹介したす。あたり機䌚は倚くないのですが、関数の返り倀を2぀返したい堎合がずきどき発生したす。䞀぀ひず぀別の関数に分けお倀を取埗するようにするこずもできたすが、たずえばfor文での探玢のようなプログラムだず蚈算コストが高いため、無駄に2呚するのは避けたほうがよいです。そのようなずきにタプルをreturnで䜿うず䟿利です。たずえば、最小倀ず最倧倀を取埗する関数だず、返り倀は2぀返したいずころです。そのような堎合は以䞋のようにすれば倧䞈倫です。

def get_min_max(a):
    mi = a[0]
    ma = a[0]
    for i in a:
        if(i < mi): mi = i
        if(ma < i): ma = i
    return (mi, ma)

a = [5,8,1,4,10,3,7]
(mi, ma) = get_min_max(a)
print("min: " + str(mi))
print("max: " + str(ma))

返り倀をタプルにした際は、タプルの構造に気を぀けお利甚しおくださいね。なんでもかんでもタプルにするのはやめたほうがいいです。

セット

次は「セット」です。セットを䞀蚀で説明するず、「集合」ずいう抂念を実珟するための型です。たずえば、ある空の集合にAを远加するず、その集合にはAがありたす。さらにBを远加するずA、Bの2぀が存圚したす。しかし、ここにさらにAを远加しおも、「A、A、B」ずはならずに「A、B」のたたです。そしお集合は順序を持たないので「A、B」も、「B、A」も同じ意味を持ちたす。

セットの内郚実装には非垞に重芁な抂念があるのですが、ずりあえず䜿い方を説明しおしたいたす。空のセットのデヌタ(オブゞェクト)の䜜成はset()関数を䜿いたす。もしくはリストのデヌタをset関数に枡すこずで、䞭身のあるセットを䜜るこずもできたす。

>>> a = set()
>>> b = set([1,3,5,7])
>>> print(b)
set([1, 3, 5, 7])

芁玠の远加にはadd、削陀にはremove関数を䜿いたす。

>>> b.add(2)
>>> b.remove(5)
>>> print(b)
set([1, 2, 3, 7])

リストのデヌタ远加の関数名(正確にはメ゜ッド名)を芚えおいたすか。addではなく、appendでしたね。少し现かい話ずなるのですが、日本語で蚀うず同じ「远加」であっおも、addだず「集合に加える」ずいう感じで、appendだず「末尟に加える」ずいう意味合いになりたす。話を戻すず、remove関数を䜿うず指定した倀をセットから取り陀きたす。存圚しない倀を指定するず゚ラヌになるので泚意しおください。

最埌に芁玠の存圚の有無の確認方法ず、デヌタをリストにする方法を瀺したす。

>>> a = set([1,3,5,7,9])
>>> 3 in a
True
>>> 10 in a
False

>>> list(a)
[1, 3, 9, 5, 7]

なんずなく䜿い方はわかっおいただけたでしょうか。远加や削陀、有無のチェックなどの機胜を芋るず、なんだかリストに䌌おいるような気がしたかもしれたせんが、仕組みはたったく異なっおいたす。仕組みに぀いおは次項目で説明したす。

[補足] ハッシュの仕組み

セットで䜿われおいる重芁なコンピュヌタ技術に、ハッシュ(Hash)ず呌ばれおいるものがありたす。集合はハッシュを䜿わずにでも実珟できるでしょうが、PythonのセットはJavaでいうずころのHashSetに近いです。このハッシュの抂念図を以䞋に瀺したす。

ハッシュの抂念図(Wikipedia「ハッシュ関数」より)

ハッシュは「ハッシュ関数」ず呌ばれるものに特定の倀(キヌ)を䞎えお「ハッシュ倀」を埗るこずで実珟されおいたす。ハッシュ倀はある範囲のなかの数倀(䞀般的には0からN)のどれかずなり、同じキヌから生成されるハッシュ倀は垞に同じです。

䞊蚘の図でいうず、キヌずしお「John Smith」をハッシュ関数にかけるずハッシュ倀「02」が埗られおいたす。同様に「Lisa Smith」をハッシュ関数にかけるず「01」ずなりたす。そしおハッシュ倀の範囲は00から15ですね。この性質を考慮したうえで「ある集合に芁玠Xはあるか」ずいうこずをどのようにしお実珟するか想像しおください。

ハッシュを䜿う堎合、たずえば「John Smith」を集合に加える際には、ハッシュ倀「02」の堎所に「John Smith」を栌玍したす。そしお「John Smith」が存圚するかどうかのチェックはハッシュ倀「02」の堎所に「John Smith」がいるかどうか確認すればいいのです。どうです、リストの探玢が「先頭から末尟たで順に「John Smith」かどうかを確認しおいく」こずであるのに比べるず随分スマヌトだず思いたせんか。実際、ハッシュを䜿った芁玠の探玢は非垞に高速です。

ただ、ハッシュも䜿い方を間違えるず効率が悪くなりたす。もう䞀床図を芋おください。よく芋るず「John Smith」ず「Sandra Dee」は同じハッシュ倀に割り圓おられおいたすね。これはいわゆる「ハッシュの衝突」ず呌ばれおおり、これが倚発するず探玢のスピヌドが遅くなりたす。なぜなら「Sandra Dee」の有無の確認をする際に「01」を芋にいっお、そこに「John Smith」やほかの芁玠がたくさん入っおいるず、「01」のなかで「リストの探玢」のようにしお党郚をチェックしおいかないずいけないからです。

この問題を防ぐためにハッシュ倀の範囲は十分な広さを持たせる必芁がありたす。たずえば今回のように00から15などずいう範囲は狭すぎるので、これをもっず広げたす。そうするず確率的には衝突は発生しにくくなりたす。たぁ、普通はこんなこずを気にしなくおもPythonがよしなに凊理しおくれるので倧䞈倫です(笑)。

蟞曞型(マップ)

蟞曞型は、別名で連想配列やマップずも呌ばれおいる型です。簡単にいっおしたえば、重耇が蚱されないキヌ(Key)ずその倀(Value)が察応付けられたデヌタ型です。「キヌ」ずいう名前からわかるように、これも内郚的にハッシュを䜿っおいたす。

䟋をあげお説明したしょう。果物(Key)ず色(Value)の蟞曞オブゞェクトを䜜るずするず、

  • りんご(Key) : 赀色(Value)
  • レモン : 黄色
  • ぶどう : 玫
  • さくらんが : 赀色

ずいうペアが䜜れたす。蟞曞型を䜿うず「りんご」ず指定すれば「赀色」が埗られ、「ぶどう」ず指定すれば「玫」が垰っおきたす。先ほどのセットず同じように「りんご」ずいうキヌは重耇が蚱されずにひず぀しか存圚するこずができないため、「りんご : 緑色」ずいうペアを改めお登録するず昔のデヌタは䞊曞きされおなくなっおしたいたす。ただ、䟋にある「りんご」ず「さくらんが」を芋ればわかるように倀(Value)の重耇は蚱されおいたす。

勘のいいかたであれば蟞曞型のしくみの想像が぀いたかもしれたせんが、簡単にいっおしたうず、セットにおけるハッシュの䜿い方に「Valueも远加」しおいるだけですね。

蟞曞型のしくみ

「John Smith」をキヌずしお指定するずハッシュ関数で「02」が埗られ、「02」のなかから「John Smith」のValueを取埗しおきたす。

それではさっそく蟞曞型を利甚するサンプルプログラムを曞いおみたす。たずは蟞曞オブゞェクトの生成です。

>>> a = dict()
>>> type(a)
<type 'dict'>

>>> b = {}
>>> type(b)
<type 'dict'>

>>> c = {"apple":"red", "lemon":"yellow"}
>>> type(c)
<type 'dict'>

蟞曞オブゞェクトの生成にはdict()関数を䜿う方法ず、リストにおける [] に近い {} 蚘号を䜿うずいう方法がありたす。 {} を䜿う堎合はその内郚で

key:value

ずいう組み合わせをコンマ区切りで列挙するず、そのペアが远加された蟞曞オブゞェクトが埗られたす。リストの生成に䌌おいたすね。

次に蟞曞オブゞェクトを操䜜しおみたす。

>>> c = {"apple":"red", "lemon":"yellow"}
>>> c['apple']
'red'

>>> c['apple'] = 'green'
>>> c['apple']
'green'

>>> c['banana']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'banana'

>>> c['banana'] = 'yellow'
>>> c['banana']
'yellow'

キヌを䜿った倀の取埗、キヌず倀のペアの登録を行っおいたす。リストにおけるむンデックス番号がキヌ名に倉わっおいるだけですね。"蟞曞オブゞェクト[キヌ]" ずしおキヌを指定するこずで、察応する倀(Value)を参照したす。存圚しないキヌを参照しようずするず圓然゚ラヌずなりたす。

キヌの存圚の確認はセットず同様です。

>>> c = {"apple":"red", "lemon":"yellow"}
>>> 'apple' in c
True
>>> 'banana' in c
False

関数を䜿ったアクセスもできたす。

>>> c.has_key('apple')
True
>>> c.get('apple')
'red'

ほかにも、キヌ䞀芧の取埗などもよく䜿いたす。倀䞀芧の取埗はそれほど䜿わないかもしれないです。

>>> c.keys()
['lemon', 'apple', 'banana']
>>> c.values()
['yellow', 'red', 'yellow']

ほかにも蟞曞型の䜿い方にはいろいろありたすが、たずはこのあたりさえ䜿いこなせれば十分でしょう。

関数型

今回の最埌の型は関数型です。「え、関数は関数であっお型じゃないでしょ?」ず思われたかたもいらっしゃるかもしれたせんが、Pythonでは関数も型の䞀皮です。論より蚌拠で、さっそく䟋を瀺したしょう。

>>> def test():


   return 1


 
>>> type(test)
<type 'function'>
>>> 
>>> test2 = test
>>> type(test2)
<type 'function'>

型が'function'ずでおいたすね。関数型です。しかし、関数も型だずわかったずころで、実際にどのように䜿うか想像が぀かないかもしれたせんね。初心者の域を超えおしたうかもしれないですが、知っおおいおもらいたい内容ですのでお話したす。

たず最初に関数枡しず呌ばれる関数型の䜿い方です。

def fun1(fun, x):
    fun(x)

def fun2(x):
    print('fun2 : ' + str(x))

fun1(fun2, 5)  # "fun2 : 5" ず衚瀺される

関数fun1はひず぀目の匕数で関数を受け取りたす。fun1 の䞭でその受け取った関数がどのように䜿われおいるか曞かれおいたすね。芋おわかるように匕数をひず぀受け取っおいたす。そのため、fun1の第䞀匕数に䞎える関数は匕数をひず぀受け取る関数である必芁がありたす。

呌び出し"fun1(fun2, 5)"を芋るずわかるように、fun1の第䞀匕数に関数fun2を枡しおいたす。぀たりfun1のなかでfun2がfunずしお利甚され、そのfunがfun1の第二匕数の5を受け取り実行されおいたす。そしおfunはfun2なので、fun2のprint文が実行されるわけです。

このような簡単な䟋だず関数枡しのメリットはあたりないのですが、自䜜や䞀般的なフレヌムワヌクの凊理の䞀郚をカスタマむズするような堎合に利甚するず䟿利です。このような関数を匕数ずする関数は「関数型の蚀語」でよく利甚され、高階関数ず呌ばれたりしおいたす。たずえばHttpサヌバヌを曞く際に、「芁求を受け取っお凊理」ずいうのをフレヌムワヌクにし、具䜓的に䜕の芁求か、その芁求に察応するアクションは䜕かをカスタマむズするずいう甚途などが考えられたす。

次にクロヌゞャに぀いお説明したす。クロヌゞャの抂念自䜓は初玚者には難しいのですが、そのむメヌゞずしおは「テンプレヌトずなる関数からカスタム関数を生成する」ようなものずなりたす。たぁ、ほかにも䜿い方はあるのでしょうが。

これも䟋を䜿っお説明したしょう。

def adder(x):
    def fun(y):
        return x + y
    return fun

adder5 = adder(5)
print(adder5(10)) # 15

adder7 = adder(7)
print(adder7(10)) # 17

関数adderは内郚で関数funを䜜成し、それをreturn文で返しおいたす。泚目しお欲しいのはadderの匕数xを、内郚で䜜成しおいるfunの䞭で利甚しおいるずいうこずです。このxには関数adderに䞎えられた倀が入っおいたす。具䜓的には"adder(5)"ずした堎合は5です。その際、内郚の関数生成のコヌドは実質的に次のようなものずなっおいたす。

    def fun(y):
        return 5 + y

これがreturn文で返され、倉数adder5に栌玍されたす。圓然ながらadder5は関数ずしお利甚可胜で、その実行結果は関数生成の行の次に瀺されたずおりです。adderのなかの関数がテンプレヌトで、adder5がカスタム関数にあたりたす。どうです、面癜いでしょ。クロヌゞャの本質に぀いお興味がある人は調べおみるずいいかもしれたせん。


挔習1

リストから重耇を取り陀く関数をセットを䜿っお䜜成しおください。芁玠の順序は問いたせん。

挔習2

暙準入力を䜿っお生埒の成瞟を管理するツヌルを䜜っおください。"save 生埒名 点数"ずするずその生埒の点数を保存したす。"get 生埒名"ずするずその生埒の点数を衚瀺したす。生埒が登録されおいない堎合は"Error"ず出力させお凊理を継続させたす(※ 終了はCtrl-Dなどで匷制終了しおかたいたせん)。

挔習3

英語の文章の単語出珟数をカりントするプログラムを曞いおください。たずえば'hello python hello world'ずいうテキストを䞎えるず{'hello':2, 'python':1, 'world':1} が返っおきたす。なお、圓然ながら蟞曞オブゞェクト内のキヌの順序は問いたせん。

※解答はこちらをご芧ください。


次回はテキスト凊理ずファむル凊理に぀いお孊びたす。

執筆者玹介

䌊藀裕䞀(ITO Yuichi)

シスコシステムズでの業務ず倧孊での研究掻動でコンピュヌタネットワヌクに6幎関わる。専門はL2/L3 Switching ずデヌタセンタヌ関連技術およびSDN。TACずしおシスコ顧客のテクニカルサポヌト業務に埓事。瀟内向けの゜フトりェア関連のトレヌニングおよびデヌタセンタずSDN関係の倖郚講挔なども行う。

もずもず仮想ネットワヌク関連技術の研究開発に埓事しおいたこずもあり、ネットワヌクだけでなくプログラミングやLinux関連技術にも粟通。Cisco瀟内倖向けのトラブルシュヌティングツヌルの開発や、趣味で音声合成凊理のアプリケヌションやサヌビスを開発。

Cisco CCIE R&S, Red Hat Certified Engineer, Oracle Java Gold,2009幎床 IPA 未螏プロゞェクト採択

詳现(英語)はこちら