Pythonクックブック(4章)
リストのリスト
リストのかけ算をする時には気をつけなければいけない。
リストのかけ算は、リストの要素を乗数分新しく作るのではなく、同じ要素に対する参照を乗数分作るだけ。
# ベタにリストのリストを作る >>> multi1 = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # 各要素は別のリストオブジェクトを参照している >>> multi1[0] is multi1[1] False # リストのかけ算でリストのリストを作る >>> multi2 = [[0, 0, 0]] * 3 # 各要素は同じリストオブジェクトを参照している >>> multi2[0] is multi2[1] True >>> multi1[0][0] = 'oops' # 各要素は別々のオブジェクトを参照しているので引きずられない >>> multi1 [['oops', 0, 0], [0, 0, 0], [0, 0, 0]] >>> multi2[0][0] = 'oops' # 各要素が同じオブジェクトを参照しているので引きずられてしまう >>> multi2 [['oops', 0, 0], ['oops', 0, 0], ['oops', 0, 0]] >>>
リストのリストを作りたい時はかけ算ではなくリスト内包表記を使う。
>>> multi3 = [[0 for column in range(3)] for row in range(3)] >>> multi3[0] is multi3[1] False >>> multi3[0][0] = 'oops' >>> multi3 [['oops', 0, 0], [0, 0, 0], [0, 0, 0]] >>>
シーケンスの展開
本文中では文字列についてはそんな場面が少ないので省略されてた。
せっかくなのでどんな感じになるのか書いてみる。
・・・が、1文字でも文字列として扱われることに気付かず、スタックオーバーフローだらけ。
「なんで?」と悩む。。。
気付いてしまえばなんてことはないことだから、いかにこういうこと状態を無くせるかだなぁ。
それから、例外を使う方法は遅いということで実際に測ってみたが、確かに遅い。
あと、文字列も展開するとやっぱり遅い。再帰の回数が増えるから、その分遅くなってしまうのだろう。
[kobakoba0723@fedora13-intel64 ~]$ cat loop_sequence.py import timeit def list_or_tuple(obj): return isinstance(obj, (list, tuple)) def list_or_tuple_or_string(obj): # # basestringで判定すると、'a'などの1文字でもTrueと判定され、無限ループ → スタックオーバーフロー # # return isinstance(obj, (list, tuple, basestring)) if isinstance(obj, (list, tuple)): return True else: if isinstance(obj, basestring): if len(obj) == 1: return False else: return True else: return False def nostring_iterable(obj): try: iter(obj) except TypeError: return False else: return not isinstance(obj, basestring) def flatten(sequence, to_expand=list_or_tuple): for item in sequence: if to_expand(item): for subitem in flatten(item, to_expand): yield subitem else: yield item if __name__ == '__main__': sequence = [1, 2, [3, [], 4, [5, 6], 7, [8, ], ], 9, ('a', 'b'), (('c', 'd'), 10, 11), 'egg', 'spam'] print "===== use list_or_tuple ======" for x in flatten(sequence): print x, print print "===== use list_or_tuple_or_string ======" for x in flatten(sequence, to_expand=list_or_tuple_or_string): print x, print print "===== use nonstring_iterable =====" for x in flatten(sequence, to_expand=nostring_iterable): print x, print print "---- measure time -----" t = timeit.Timer(""" for x in flatten(seq): pass """, "from __main__ import flatten; seq=%s" % sequence) print "list_or_tuple: %f" % t.timeit(number=1000) t = timeit.Timer(""" for x in flatten(seq, to_expand=list_or_tuple_or_string): pass """, "from __main__ import flatten, list_or_tuple_or_string; seq=%s" % sequence) print "list_or_tuple_or_string: %f" % t.timeit(number=1000) t = timeit.Timer(""" for x in flatten(seq, to_expand=nostring_iterable): pass """, "from __main__ import flatten, nostring_iterable; seq=%s" % sequence) print "nostring_iterable: %f" % t.timeit(number=1000) [kobakoba0723@fedora13-intel64 ~]$ python loop_sequence.py ===== use list_or_tuple ====== 1 2 3 4 5 6 7 8 9 a b c d 10 11 egg spam ===== use list_or_tuple_or_string ====== 1 2 3 4 5 6 7 8 9 a b c d 10 11 e g g s p a m ===== use nonstring_iterable ===== 1 2 3 4 5 6 7 8 9 a b c d 10 11 egg spam ---- measure time ----- list_or_tuple: 0.033982 list_or_tuple_or_string: 0.059997 nostring_iterable: 0.071849
参考サイト
リストのかけ算@atsuoishimotoの日記
timeit - 小さなPythonコードの実行時間を計る@Python Module of The Week
- 作者: Alex Martelli,Anna Martelli Ravenscroft,David Ascher,鴨澤眞夫,當山仁健,吉田聡,吉宗貞紀
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/06/26
- メディア: 大型本
- 購入: 11人 クリック: 423回
- この商品を含むブログ (85件) を見る