リストと辞書と繰り返し

○回繰り返す

>>> for i in range(0, 5):
...     print u"%d回目" % (i + 1),
... 
1回目 2回目 3回目 4回目 5回目
>>>
>>> for i in xrange(0, 5):
...     print u"%d回目" % (i + 1),
... 
1回目 2回目 3回目 4回目 5回目

range() はリストを作って繰り返します。xrange() は繰り返しのたびに値を返します。大きな数を扱う場合は xrange() を使った方が良いです。

リストの要素を一つずつ取り出す

>>> l = ["a", "b", "c", "d", "e"]
>>> for v in l:
...     print v,
... 
a b c d e

リストの要素を逆順に取り出す

>>> for v in reversed(l):
...     print v,
... 
e d c b a

リストの要素を添え字付きで取り出す

>>> l = ["a", "b", "c", "d", "e"]
>>> for i, v in enumerate(l):
...     if i % 2:
...         print v,
... 
b d

辞書の値を一つずつ取り出す

>>> d = {"a":1, "b":2, "c":3} 
>>> for k in d:
...     print d[k],
... 
1 3 2
>>> for v in d.itervalues():
...     print v,
... 
1 3 2

辞書のキーを一つずつ取り出す

>>> d = {"a":1, "b":2, "c":3} 
>>> for k in d:
...     print k,
... 
a c b
>>> for k in d.iterkeys():
...     print k,
... 
a c b

辞書のキーと値を取り出す

>>> for k, v in d.iteritems():
...     print "%s=>%s" % (k, v),
... 
a=>1 c=>3 b=>2

辞書からキーと値のリストを作る

>>> keys = d.keys()
>>> keys
['a', 'c', 'b']
>>> values = d.values()
>>> values
[1, 3, 2]
>>> items = d.items()
>>> items
[('a', 1), ('c', 3), ('b', 2)]

cursesサンプル

cursesというのは端末操作用のライブラリです。キーボードから入力を受け取ったり、画面に文字を表示したり、サブウィンドウを作ったり、カーソルを自由に移動させたりできます。もしかしたら vim っぽいものも作れるかもしれません。僕には vim は無理っぽいので、hlkj を押すと文字を表示して、カーソルを移動するだけのものを作ってみました。

#!/usr/local/python
# vim: fileencoding=utf-8

import curses

# ライブラリを初期化し、スクリーンを表すWindowObjectを返す
win = curses.initscr()

# キー入力を自動的に画面に表示しない
curses.noecho()

# Enterキーを押さずに、キー入力に直ちに反応する
curses.cbreak()

keys = (
    ("h", "Left"), 
    ("l", "Right"), 
    ("k", "Up"), 
    ("j", "Down"),
    ("c", "Clear"),
    ("q", "Quit"),
)
for y, key in enumerate(keys):
    # カーソルを座標(x, y) に移動
    win.move(y, 0)

    # カーソル位置に文字を上書き
    # 改行文字は使えないので、1行ずつ表示する
    win.addstr("%s:%s" % key)

# メインウィンドウを更新
win.refresh()

# メインウィンドウの高さと幅を取得
y, x = win.getmaxyx()

# サブウィンドウを作成
swin = win.subwin(y, x-11, 0, 10)

# サブウィンドウの枠線を描画
# 左、右、上、下、左上、右上、左下、右下
swin.border("|", "|", "-", "-", "+", "+", "+", "+")

# カーソルが移動できる範囲は
# (0, 0)〜(ウィンドウの高さ - 1, ウィンドウの幅 - 1)
# この範囲を超えた座標をmove()に渡すとエラー
min_x = 0
min_y = 0
max_y, max_x = swin.getmaxyx()
max_x -= 1
max_y -= 1

# 枠線を上書きしないように調整
min_x += 1
min_y += 1
max_x -= 1
max_y -= 1

# カーソルをサブウィンドウの座標(x, y)に移動
# サブウィンドウの左上の角が(0, 0)
x, y = min_x, min_y
swin.move(y, x)

# カーソル位置のチェックと座標変更用の無名関数
c_move = {
    "h":(lambda x, y: x > min_x, lambda x, y: (x - 1, y)),
    "l":(lambda x, y: x < max_x, lambda x, y: (x + 1, y)),
    "k":(lambda x, y: y > min_y, lambda x, y: (x, y - 1)),
    "j":(lambda x, y: y < max_y, lambda x, y: (x, y + 1)),
}

while 1:
    # 入力されたを文字で取得
    c = swin.getkey()
    
    # 入力されたキーを文字コードで取得
    # c = swin.getch()
    # c = chr(c)

    # 入力されたキーがh,l,k,j
    if c in c_move:
        # カーソル位置が端でなければ、座標を変更
        if c_move[c][0](x, y):
            x, y = c_move[c][1](x, y)

        # 入力された文字を表示した後にカーソルを移動
        # 文字を表示するとカーソルが右端に移動するので
        # 文字表示、カーソル移動の順にしている
        swin.addstr(c)
        swin.move(y, x)
    elif c == "c":
        # サブウィンドウをクリア
        swin.erase()
        # 枠線も消えるので再描画
        swin.border("|", "|", "-", "-", "+", "+", "+", "+")
        swin.move(min_x, min_y) 
    elif c == "q":
        break

    swin.refresh()

# スクリーンを閉じる
curses.endwin()

オブジェクトの要素を一つずつ取り出す

反復可能なオブジェクト

for i in x:
    print i
 
l = [n for n in x if n % 2]

x は要素を一つずつ取り出せる反復可能なオブジェクトです。反復可能なオブジェクトは __iter__() でイテレータオブジェクトを返す必要があります。

class Test:
    def __iter__(self):
        return self.__dict__.iteritems()

__dict__ はオブジェクトの属性の辞書、iteritem() はキーと値のタプルを返すイテレータオブジェクトを返します。

t = Test()
t.s = 1
t.a = 2
t.y = 3
for kv in t:
    print "%s => %s" % kv
a => 2
y => 3
s => 1

イテレータオブジェクト

イテレータオブジェクトは反復可能なオブジェクトですが、__iter__() で自分自身を返し、次の要素を返す next() を持つ必要があります。next() は最後の要素を返した後は、何度呼び出しても StopIteration 例外を投げなければなりません。そうしないと無限ループになってしまいます。


では、0〜n までの素数を返すイテレータオブジェクトを作ってみます。

import math

class Prime:
    def __init__(self, n):
        self.nums = range(2, n+1)
        self.root_n = math.sqrt(n)

    def __iter__(self):
        return self

    def next(self):
        if len(self.nums):
            # 1回目のpは2、つまり素数
            p = self.nums.pop(0)
            if self.root_n > p:
                # 素数pで割り切れる要素を全て取り除く
                # これでself.numsの先頭は次の素数になる
                self.nums = [n for n in self.nums if n % p]
            return p
        raise StopIteration

まず素数で始まる数値のリストを用意します。そして、next() が呼ばれるたびにリストの先頭の要素を返し、。同時にリストからその要素で割り切れる数を取り除きます。こうするとリストの先頭の要素は必ず素数になります。また、n以下の素数でない数は必ず√n で割り切れるので、素数で要素を取り除くは、先頭の要素が√n 以下の間だけです。

p = Prime(100)
print [n for n in p]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

クラスについて

クラス定義とインスタンスの作成

class Test:
    pass

何もしないクラスを定義しています。


クラス名を関数のように呼び出して、インスタンスを作ります。

t = Test()
print t
#=> <__main__.Test instance at 0x009AD3A0>

インスタンスメソッド

class Test:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def p(self):
        print "x = %s" % self.x
        print "y = %s" % self.y

__init__() というメソッドを定義すると、インスタンスを作成する時に呼ばれます。いわゆるコンストラクタというやつで、変数の初期化などはここで行います。
また、インスタンスメソッドは一番目の引数に自分自身(インスタンス)を受け取ります。そしてメソッド内では、このインスタンスを使って変数にアクセスします。


メソッド定義では一番目の引数はインスタンスになっていますが、自動的に渡されるので、引数にインスタンスを渡す必要はありません。

t = Test(5, 5)
t.p()
x = 5
y = 5

インスタンス変数へのアクセス

t = Test(5, 5)

print t.x
#=> 5

print t.y
#=> 5


定義されていないインスタンス変数にアクセスしてみます。

print t.z
#=> AttributeError: Test instance has no attribute 'z'

定義されていないインスタンス変数を参照しようとするとエラーになります。


では、定義されていないインスタンス変数に値を代入してみます。

t.z = 5
print t.z
#=> 5

今度はエラーにはなりません。これは未定義のインスタンス変数に値を代入すると、その変数が新たに定義されるからです。

プライベート変数

Pythonでは外部からのインスタンス変数へのアクセスを完全に防ぐことはできません。代わりに変数名をアンダーバーを二つ並べたもので始めることで、そのまま名前で外部からアクセスすることを防ぐことができます。

class Test:
    def __init__(self, x=0, y=0):
        self.x = __x
        self.y = __y

    def p(self):
        print "x = %s" % self.__x
        print "y = %s" % self.__y


外部からインスタンス変数にアクセスしてみます。

print x.__x
#=> AttributeError: Test instance has no attribute '__x'

エラーになります。


どうしても、__x の値を参照したい場合は以下のようにします。

print t._Test__x
#=> 5

つまり変数名をアンダーバーで始めると、外部からは _クラス名を付けないとアクセスできないようになるわけです。

クラス変数

クラス定義が最後まで実行されると、クラスオブジェクトが作られます。クラスオブジェクトに属する変数がクラス変数です。

class Test:
    format = "Hello, %s."

    def hello(self, name):
        print Test.format % name

クラス定義の中で変数を定義すれば、それがクラス変数になります。


クラス変数にはクラス名.変数名でアクセスします。ちなみにクラス名はクラスオブジェクトへの参照です。

print Test.format
#=> "Hello, %s."

t = Test()
t.hello("yukiue")
#=> "Hello, yukiue."


インスタンスからでもクラス変数を参照することはできますが、同名のインスタンス変数がある場合は、そちらが優先されます。

print t.format
#=> "Hello, %s."

t.format = "hoge"
print t.format
#=> "hoge"

リスト内包表記

数値のリストから偶数だけを取り出して新しいリストを作ります。

nums = range(1,10)

list1 = []
for n in nums:
    if not n % 2:
        list1.append(n * n)

print list1
#=> [4, 16, 36, 64]


同じことをリスト内包表記を使って書くと、以下のようになります。

list2 = [n * n for n in nums if not n % 2]
print list2
#=> [4, 16, 36, 64]


数値のリストのリストから同じインデックスの要素を取り出して、その合計値のリストを作ります。

nums = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

list3 = []
for i in range(0, len(nums[0])):
    l = []
    for n in nums:
        l.append(n[i])
    list3.append(sum(l))
print list3
#=> [12, 15, 18]


同じことをリスト内包表記を使ってやってみます。まず、同じインデックスの要素から成るリストのリストを作ります。

list4 = [[n[i] for n in nums]
            for i in range(0, len(nums[0]))]
#=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

そして、それぞれのリストの合計値のリストを作ります。

list5 = [sum(l) for l in list4]
#=> [12, 15, 18]


さらに、二つをまとめてみます。

list6 = [sum(l) for l in 
            [[n[i] for n in nums] 
                for i in range(0, len(nums[0]))]]
print list6
#=> [12, 15, 18]

リスト内包表記は入れ子にできますが、その分複雑になります。今回の場合も一度 for ループで書いてからでないと、リスト内包表記で書けませんでした。for ループよりリスト内包表記を使った方が速いそうですが、使いすぎると分かりにくくなりそうなので、ほどほどにしておいた方が良さそうです。


ちなみにzip() と map() を使うのが一番楽です。

list7 = map(sum, zip(*nums))
print list7
#=> [12, 15, 18]

zip() は複数のリストを引数に取り、同じインデックスの要素から成るリストを作ります。map() はリストの各要素に指定した関数を適用します。

これはひどい、と思う記事を見つけたら

とりあえずブクマをチェックします。そして「これはひどい」というタグがたくさん付けられていたら、ホッとしてしまいます。自分は普通なんだ。自分は多数派なんだ。そう思って安心してしまいます。はてブの利用者なんて社会全体から見れば少数派であるにもかかわらず、それでも多数派であることに安心感を覚えてしまいます。そんなことに、他者との繋がりを感じてしまうのです。

引数の受け取り方

仮引数にデフォルト値を設定する

def test1(a, b, c=3):
    print a * b * c
 
test(1, 2, 6)
#=> 12

test(1, 2)
#=> 6

a と b は必須、c はデフォルト値が設定されているので省略可能です。


デフォルト値が設定されるのは1回目の関数呼び出しの時だけです。つまり、デフォルト値というのは引数を初期化する値ではなく、引数を省略した場合に使われる共通の値ということです。ですから、リストや辞書をデフォルト値に指定した場合、関数内で引数を変化させると、関数を呼び出すたびにデフォルト値が変わってしまいます。

def test2(a, l=[]):
    l.append(a)
    print l

test2(1)
#=> [1]

test2(2)
#=> [1, 2]

test2(3, [])
#=> [3]

test2(4)
#=> [1, 2, 4]


デフォルト値を空のリストで固定したい場合は、以下のようにします。

def test3(a, l=None):
    if not l:
        l = []
    l.append(a)

    print l

test3(1)
#=> [1]

test3(2)
#=> [2]

任意の個数の引数を受け取る

*付きの仮引数があると、仮引数に対応していない全ての値だけの引数をタプルとして受け取ります。

def test4(format, *l):
   print format % l

test4("%04d-%02d", 2008, 10)
#=> 2008-10

test4("%04d-%02d-%02d", 2008, 10, 6)
#=> 2008-10-06

任意の個数のキーワード引数を受け取る

**付きの仮引数があると、仮引数に対応していない全てのキーワード引数を辞書として受け取ります。

def test5(format, **d):
    print format % d

test5("%(year)04d-%(month)02d", year=2008, month=10)
#=> 2008-10

test5("%(year)04d-%(month)02d-%(day)02d", year=2008, month=10, day=6)
#=> 2008-10-06

仮引数を指定する順番

仮引数は以下の順で指定しなければなりません。また、*付き仮引数と **付き仮引数は一つずつしか指定できません。

  • デフォルト値なしの仮引数
  • デフォルト値ありの仮引数
  • *付き仮引数
  • **付き仮引数