読者です 読者をやめる 読者になる 読者になる

はてブロ@ama-ch

https://twitter.com/ama_ch

今日のPython

Program Python

リスト内包表記(リストコンプリヘンション)

p.239 文字列の各文字を文字コードに変換してリストを作成

>>> char = []
>>> for s in "Python":
...     char.append(ord(s))
... 
>>> char
[80, 121, 116, 104, 111, 110]

これをリスト内包表記で書いたものが、以下。

>>> char = [ord(s) for s in "Python"]
>>> char
[80, 121, 116, 104, 111, 110]

リスト内包表記を実行すると、式を評価した結果のリストが返ってくる。構文は
[式 for hoge in シーケンス]
のようになる。式の部分には、代入のような「文」を書くことはできない。


p.240 値の2乗値をリストにして返す

>>> sq = [x ** 2 for x in range(10)]
>>> sq
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

タプルを使い2乗する前と後の値を表示

>>> sq = [(x, x**2) for x in range(5)]
>>> sq
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]


p.241 文字列を反転する

>>> str = u"あいうえお"
>>> rs = "".join([str[-x-1] for x in range(len(str))])
>>> print rs
おえういあ

ぱっと見でわかりにくいので、普通に書いてみます。

>>> str = u"あいうえお"
>>> for x in range(len(str)):
...     rs += str[-x-1]
... 
>>> print rs
おえういあ


この反転する処理を利用して、回文を判定

>>> str = u"スマートなトーマス"
>>> #与えられた文字列は反転したものと等しいか?
>>> str == "".join([str[-x-1] for x in range(len(str))])
True

リスト内包表記で利用するif

リスト内包表記のシーケンスの右には、if文を書くことができ、条件がTrueになった場合のみ、要素をリストに追加する。


p.241 与えられた数の約数をリストに追加する

>>> val = 100
>>> [x for x in range(1, val) if val % x == 0]
[1, 2, 4, 5, 10, 20, 25, 50]

複雑なリスト内包表記

リスト内包表記のforは複数記述できる。複数記述した場合、for文でループを入れ子にしたときと同じような動作になる。


こうすると、九九の結果がリストに格納される

>>> qq = [a*b for a in range(1, 10) for b in range(1, 10)]
>>> qq
[1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18, 3, 6, 9, 12, 15, 18, 21, 24, 27, 4, 8, 12, 16, 20, 24, 28, 32, 36, 5, 10, 15, 20, 25, 30, 35, 40, 45, 6, 12, 18, 24, 30, 36, 42, 48, 54, 7, 14, 21, 28, 35, 42, 49, 56, 63, 8, 16, 24, 32, 40, 48, 56, 64, 72, 9, 18, 27, 36, 45, 54, 63, 72, 81]

ただ、整形する方法がわからない>< 普通にfor文で書くのが楽な気が・・・
ちなみに、普通に書くとこうなります。

>>> qq = []
>>> for a in range(1, 10):
...     for b in range(1, 10):
...             qq.append(a * b)
... 
>>> qq
[1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18, 3, 6, 9, 12, 15, 18, 21, 24, 27, 4, 8, 12, 16, 20, 24, 28, 32, 36, 5, 10, 15, 20, 25, 30, 35, 40, 45, 6, 12, 18, 24, 30, 36, 42, 48, 54, 7, 14, 21, 28, 35, 42, 49, 56, 63, 8, 16, 24, 32, 40, 48, 56, 64, 72, 9, 18, 27, 36, 45, 54, 63, 72, 81]
リスト内包表記を入れ子にする

p.243 1から10までの数と約数を表示する

>>> [ (x, [y for y in range(1, x) if x % y == 0]) for x in range(1, 11) ]
[(1, []), (2, [1]), (3, [1]), (4, [1, 2]), (5, [1]), (6, [1, 2, 3]), (7, [1]), (8, [1, 2, 4]), (9, [1, 3]), (10, [1, 2, 5])]

これも違う書き方で!

>>> tmplst = []
>>> divisor = []
>>> for x in range(1, 11):
...     for y in range(1, x):
...             if x % y == 0:
...                     tmplst.append(y)
...     divisor.append((x, tmplst))
...     tmplst = []
... 
>>> divisor
[(1, []), (2, [1]), (3, [1]), (4, [1, 2]), (5, [1]), (6, [1, 2, 3]), (7, [1]), (8, [1, 2, 4]), (9, [1, 3]), (10, [1, 2, 5])]

はあはあ、面倒くさかった・・・
※リスト内包表記は短く書ける反面、可読性が落ちるので注意されたし!

イテレータを使う

イテレータとは

要素を順番に取り出すとき、特定のデータ形式だけに利用できる方法ではなく、より汎用的にデータを順番に取り出すための仕組み。例えば、リストだけではなく改行で区切られた文書データなどを同じように取り出す。


p.245 イテレータオブジェクトを扱う

>>> i = iter([1, 2, 3])  #リストをイテレータオブジェクトに変換
>>> i.next()  #「次の要素」を取り出す
1
>>> i.next()
2
>>> i.next()
3
>>> i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration  ##要素がなくなったことを示すエラー
ループとイテレータ

p.246
Pythonでは、ループ中で必要に応じて自動的にイテレータオブジェクトへ変換してくれる。そのため、本来インデックスのない辞書型や、open()関数などを使って取得したファイルオブジェクトをfor文で使うことができる。


例. 辞書のキーを基にループ

>>> dict = {"h":"Hello", "p":"Python", "a":"ama-ch", "b":"byebye"}
>>> for key in dict:
...     print key,
... 
a h b p

ジェネレータを使う

ジェネレータとは

関数の一種。普通の関数と違い、関数を動かしながら、yield文を使って何度も結果を返すことができる。イテレータと同じく、ループに添えて使う。イテレータのような機能を、関数を使って手軽に定義するための機能。

ジェネレータ関数を定義する

内部にyield文のある関数を、ジェネレータ関数とみなす。yieldとは、「譲歩する、譲る」という意味。
returnとyield文をひとつの関数内で使うことはできない。
yield文を実行すると、関数の処理がいったん止まり、制御が譲られる


p.250 素数を返すジェネレータ関数

>>> def get_primes(x=2):
...     while True:
...             for i in range(2, x):
...                     if x % i == 0:
...                             break
...                     else:
...                             yield x
...                     x += 1
... 
>>>

これの動作がよくわからない。 とりあえず実行してみると、こんな感じ

>>> i = get_primes()
>>> print i.next()
3
>>> print i.next()
5
>>> print i.next()
5
>>> print i.next()
5
>>> print i.next()
7

なんで5が3回も?
つづけてループでnext()メソッドを呼び出す例

>>> for c in range(10):
...     print i.next()
... 
7
7
7
7
9
11
11
11
11
11

んん〜〜わからん!そもそもこの関数だと2が素数として表示されないし、みんPyでは「素数を順番に表示する」はずなのに・・・まず素数を返す関数をジェネレータで作る意味がわからない。returnで良いと思うんだけど。


p.252 カレントディレクトリ以下のにある.pyファイルをすべて表示する

>>> for dpath, dnames, fnames in os.walk("./"):
...     for fname in fnames:
...             if fname.endswith(".py"):
...                     print dpath, ":", fname
... 
./ : a.py
./ : argtest.py
./ : astring.py
./ : dicformat.py
./ : find_a.py
./ : fizzbuzz.py
./ : fizzbuzzgolf.py
./ : get_prime.py
./ : selfintroductions.py
./ : test.py
./ : testmodule.py
./ : wordcount.py

lamda(ラムダ)式

def文を使わずに関数を定義する式。
lambda 引数のリスト : 引数を使った式
のように書く。


p.252

>>> foo = lambda x, y, z : x + y + z
>>> foo(1, 2, 3)
6

defで書いたものが以下。

>>> def foo(x, y, z):
...     return x + y + z
... 
>>> foo(1, 2, 3)
6

Pythonオブジェクト指向

Setクラスを使う

集合型であるSetクラス。
p.259

>>> from sets import Set
>>> s = Set([1, 2, 3, 4])
>>> s
Set([1, 2, 3, 4])
>>> s.add(5)  #要素を追加
>>> s
Set([1, 2, 3, 4, 5])
>>> s.add(1)  #既に含まれている要素を追加
>>> s
Set([1, 2, 3, 4, 5])  #反映されない
>>> s2 = Set([4, 5])
>>> s3 = s - s2  #集合の差
>>> s3
Set([1, 2, 3])
>>> s4 = Set([3, 4, 5, 6])
>>> s5 = s3 | s4  #集合の和
>>> s5
Set([1, 2, 3, 4, 5, 6])
クラスを定義する

p.264
class クラス名:
という形で定義する。クラス名には通常、大文字から始まるアルファベット文字列を用いる。

クラスメソッドの定義

p.265

  • クラスのブロック内に定義する
  • 必ず最初にself引数*1を定義する
>>> class Bookmark:
...     def print_outline(self):
...             # クラスの概要を説明するメソッド
...             print u"[Bookmarkクラス]",
...             print u"ブックマークを管理するためのクラスです"
... 
>>> b = Bookmark()  #Bookmarkクラスのインスタンスを作る
>>> b.print_outline()  #メソッドを呼び出す
[Bookmarkクラス] ブックマークを管理するためのクラスです
アトリビュートの定義

p.267
インスタンスオブジェクトに独自のデータを保存する。クラスが持つ変数のようなもの。
クラス内に新しいアトリビュートを作るには、クラス内でメソッドを定義して、その中で代入を行うのが一般的。代入するには、第一引数selfに、ドットで区切って設定したいアトリビュート名を続け、代入する。


p.268 ブックマークのタイトルとURLに関する4つのメソッドを追加したBookmarkクラス

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Bookmark.py
class Bookmark:
    def print_outline(self):
        # クラスの概要を説明するメソッド
        print u"[Bookmarkクラス]",
        print u"ブックマークを管理するためのクラスです"

    def set_title(self, title):
        # タイトルを設定するメソッド
        self.title = title

    def get_title(self):
        # タイトルを取得するメソッド
        return self.title

    def set_url(self, url):
        # URLを設定するメソッド
        self.url = url

    def get_url(self):
        # URLを取得するメソッド
        return self.url

b = Bookmark()
b.set_title("ama-ch's Blog")
print b.get_title()
b.set_url(u"http://d.hatena.ne.jp/ama-ch/")
print b.get_url()

実行結果

h$ python Bookmark.py
ama-ch's Blog
http://d.hatena.ne.jp/ama-ch/
インスタンスオブジェクトの初期化

p.269
__init__()というメソッドを使うと、インスタンスオブジェクトを作るときに、特別な処理を実行することができる。
このメソッドは、インスタンスオブジェクトを作るときに呼び出される共通のメソッド名で、メソッド名を指定して呼び出すことはない。


p.270 __init__()メソッドを追加したBookmarkクラス

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Bookmark.py
class Bookmark:
    def __init__(self, title, url):
        # インスタンス初期化用メソッド
        self.title = title
        self.url = url

    def print_outline(self):
        # クラスの概要を説明するメソッド
        print u"[Bookmarkクラス]",
        print u"ブックマークを管理するためのクラスです"

    def set_title(self, title):
        # タイトルを設定するメソッド
        self.title = title

    def get_title(self):
        # タイトルを取得するメソッド
        return self.title

    def set_url(self, url):
        # URLを設定するメソッド
        self.url = url

    def get_url(self):
        # URLを取得するメソッド
        return self.url

b = Bookmark("ama-ch's Blog", "http://d.hatena.ne.jp/ama-ch/")
print b.get_title()
print b.get_url()

実行結果

$ python Bookmark.py
ama-ch's Blog
http://d.hatena.ne.jp/ama-ch/

初期化時に引数を渡してタイトルとURLが設定できるようになった。


更に、特殊メソッド__str__()を定義する。このメソッドは、print文にオブジェクトを添えた時や、組み込み関数のstr()でオブジェクトを文字列に変換しようとした時に自動的に呼ばれる。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Bookmark.py
class Bookmark:
    def __init__(self, title, url):
        # インスタンス初期化用メソッド
        self.title = title
        self.url = url

    def __str__(self):
        # インスタンスオブジェクトの概要を表示する
        return "Title : %s, URL : %s" % (self.title, self.url)

    def print_outline(self):
        # クラスの概要を説明するメソッド
        print u"[Bookmarkクラス]",
        print u"ブックマークを管理するためのクラスです"

    def set_title(self, title):
        # タイトルを設定するメソッド
        self.title = title

    def get_title(self):
        # タイトルを取得するメソッド
        return self.title

    def set_url(self, url):
        # URLを設定するメソッド
        self.url = url

    def get_url(self):
        # URLを取得するメソッド
        return self.url

b = Bookmark("ama-ch's Blog", "http://d.hatena.ne.jp/ama-ch/")
print b

実行結果

$ python Bookmark.py
Title : ama-ch's Blog, URL : http://d.hatena.ne.jp/ama-ch/

最後のprint bを、str(b)にしても同じ結果が得られる。
他にも特殊メソッドは沢山あり、本書の後半部分で更に学習する。らしい。

*1:メソッドの中でインスタンスオブジェクト自体を利用したいときに使う引数