MIRACLE
メールサービス申込 ユーザー登録 パートナー情報
お問い合わせ FAQ サイトマップ
MIRACLE LINUXの特長 製品紹介 サービス案内 購入 サポート 技術フォーラム

プロフィール

日本発のリナックス企業、ミラクル・リナックスで奮闘する社員のブログです。

ミラクル関連リンク

採用情報

サイト検索

最近のトラックバック

2008年9月

  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

« ダイアグラム作成ツール dia | メイン | YAPC::Asia 2008 Thu. 5/15 (1) »

Python Code Reading 01

先日、mixi で知らない人から「お仕事の件」という件名のメールをもらった tmorimoto です。その人は IT 系の会社を起業したばかりらしく、その創業メンバーとして SE/PG を探していたそうです。丁重にお断りしたのですが、そんなメールも来るんだなと驚きました。

前回の 00 は、キックオフ的な集まりでした。記念すべき 01 のお題は python の標準モジュール  sets.py でした。sets.py は、PEP218 -邦訳- で提案されたそうです。コード量は577行で、2時間の勉強会で取り上げるのに適切な分量だったと思います。

講師は id:michisu さんが務められ、既にプレゼン資料もアップされています。最初に準備された資料で簡単に前説し、大半の時間をソースコードを読み進めながら、時には注意して詳解し、時には質問を受け、時には助言を求めながら進められました。一人だと、容易に読み進められない、初学者にも、識者にも有益な勉強会だったと思います。また、参加者は40人程度でした。

私は python を学び始めて3ヶ月程度です。私にとって、個人的にとても興味深かった内容は以下の通りです。

  • sandbox -set.py- の開発ログが意外とおもしろい
  • 特殊メソッド というキーワードとその概念
  • パフォーマンスを考慮したちょっとしたコーディングテクニック

もっと高度な話題も出ていましたが、私が理解できた内容に絞って、sets.py のソースコードを見ながら取り上げてみます。

 83 class BaseSet(object):
84     """Common base class for mutable and immutable sets."""
・・・
90     def __init__(self):
91         """This is an abstract class."""
92         # Don't call this from a concrete subclass!
93         if self.__class__ is BaseSet:
94             raise TypeError, ("BaseSet is an abstract class.  "
95                               "Use Set or ImmutableSet.")

BaseSet は ImmutableSet/Set/_TemporarilyImmutableSet の抽象クラスに相当します。しかし、python は言語として抽象クラスをサポートしていないため、コンストラクタで明示的に例外を発生させているようです。
python での抽象クラスを使用する場合の、簡単なサンプルが以下になります。

>>> class MyAbsCls:
...   def delegate(self):
...     self.action()
...
>>> class MyCls(MyAbsCls):
...   def action(self):
...     print 'in MyCls.action'
...
>>> x = MyCls()
>>> x.delegate()
in MyCls.action

x.delegate() を実行すると、サブクラス(MyCls) の属性を検索して、スーパークラス(MyAbsCls)の delegate メソッドが見つかります。そして、delegate に渡された self(MyCls のインスタンス) の属性を検索し、サブクラスの action メソッドが見つかるわけです。何か「おぉーっ」て言いたくなるような仕組みです。

さらに sandbox -set.py- の diff を見てみると、組み込み定数をローカル変数に代入するように変更していることが分かります。

285         value = True
286         for elt in ifilterfalse(otherdata.has_key, self):
287             data[elt] = value
288         return result

当然、こんな質問が出ました。「何故 value に True を入れてるの?」
その回答はこうでした。「おそらくローカル変数を使った方がパフォーマンスが良いのではないか。」

【初めての Python 第2版】によると「ある変数がローカル変数であるかどうかは、スタティックに決定され、def 内で代入ステートメントがある変数は全てローカル変数と見なされる」と記述されています。実行時に解釈していないのであれば、確かにコストは低くなりそうです。
でも、本当かな? ならば、実験です(^ ^;;
以前、pilot python Fl.1 - profile runtime - で取り上げた profile_time_var.py を用いて計測してみました。

$ cat compare_variable.py
#!/bin/env python

L = [x for x in xrange(10000)]

def local_true():
    value = True
    for i in xrange(len(L)):
        L[i] = value

def builtin_true():
    for i in xrange(len(L)):
        L[i] = True


$ python profile_time_var.py
// リスト要素1万の処理を100回呼び出した場合
Running local_true 100 times took 0.370 seconds
Running builtin_true 100 times took 0.420 seconds

// リスト要素10万の処理を100回呼び出した場合
Running local_true 100 times took 3.460 seconds
Running builtin_true 100 times took 3.780 seconds

// リスト要素100万の処理を100回呼び出した場合
Running local_true 100 times took 41.480 seconds
Running builtin_true 100 times took 48.490 seconds

確かにローカル変数を使った方が速いようです。
最後に、例外処理のちょっとした最適化です。

371             while True:
372                 try:
373                     for element in it:
374                         data[element] = value
375                     return
376                 except TypeError:
377                     transform = getattr(element, "__as_immutable__", None)
378                     if transform is None:
379                         raise # re-raise the TypeError exception we caught
380                     data[transform()] = value

371行目の while True: でループを入れ子にしなくても、373行目のfor element in it: の箇所で例外処理を行えば同じ処理になります。ここでの回答は「iterable の各要素に対して、全て例外処理の判定を行うとコストが高くなるから。」でした。前述のローカル変数同様に実験してみました。

$ cat except_loop.py
#!/bin/env python

import random

L = [x for x in xrange(100000)]
RandomError = "RandomError"
raise_number = 5846

def inside_except():
    random.shuffle(L)
    i = 0
    for i in xrange(len(L)):
        try:
            if L[i] == raise_number:
                raise RandomError
        except RandomError:
            pass
    return

def outside_except():
    random.shuffle(L)
    j = 0
    while True:
        i = j
        try:
            for i in xrange(i, len(L)):
                if L[i] == raise_number:
                    raise RandomError
            return
        except RandomError:
            j = i + 1


$ python profile_time_except.py
Running inside_except 100 times took 21.360 seconds
Running outside_except 100 times took 17.980 seconds

このサンプルの処理が本当に等価か?と言うと、やや怪しいですが、それでも try/except を外に出した方が速いようです。リストの要素数によって実行速度が逆転するかもしれません。もっと良い検証方法があれば、コメントにツッコミをどうぞ。

と、私のような初学者でも、とても収穫の多い勉強会でした。もちろん他にも多くのキーワードを得ることができました。キーワードさえ分かれば、詳細はインターネットや書籍でいくらでも調べることができます。キーワードが分かるというのも、勉強会の良いところの1つだと、私は思います。

トラックバック

このページのトラックバックURL:
http://www.typepad.jp/t/trackback/4447/12547318

このページへのトラックバック一覧 Python Code Reading 01:

コメント

Linuxとは関係のない趣味のネタばかりでうんざりです。そういう情報は他のサイトでも得られます。何のためのブログなんでしょうか?

コメントありがとうございます。
アジアのペンギンは、Asianux 開発チームのメンバーが共同で執筆しています。
執筆する内容は特に規定されていないので、私は、主にコミュニティ活動やユーザアプリ関連について執筆しています。直接 Linux とは関係ないと感じられるかもしれませんが、Linux もコミュニティベースで開発されているものです。(各々の)コミュニティの考え方や活動内容は、Linux の根幹に通じるものがあるかもしれないと私は考えています。
求める Linux 情報とは具体的にどのようなものでしょうか?是非、今後の参考にさせて頂きたいと思います。

あなたのオブジェクト指向の勉強や社内連絡のようなやりとりを公開することが、このブログの趣旨なのでしょうか?

このブログは会社の公式サイト上にあり、その会社は何を販売していて、どんな人がこのサイトに来るのかを考えれば、自ずと書くべき内容は決まってくるのではないでしょうか?

コメントありがとうございます。
仰る通り、社内連絡のようなやり取りは不要です。オブジェクト指向の勉強ログも、私自身、是非が分からなかったのですが、適切ではないという意見が大勢であれば、止めようと思います。弊社は、Linux Distribution を販売している会社です。OSS をビジネスとして、どう捉えるかという点が1つの課題だと、私は考えています。コミュニティ関連の話題は、Linux を始め、OSS をビジネスとして展開する上で有益な情報であると、私は思っています。
当ブログを読まれる方々にとって、それが有益かどうか、私には分かりません。コメント投稿者様(お名前/ニックネームを教えて頂きたいのですが)のような、貴重なご意見を頂けるのは嬉しく思います。今後もコメントを頂けると幸いです。

コメントを投稿

会社情報 採用情報 個人情報保護方針 商標等取り扱い事項 English
Copyright(c)2000-2006 MIRACLE LINUX CORPORATION. All Rights Reserved.