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

bool/not not

Python


http://enbug.tdiary.net/20071227.html#p01
http://d.hatena.ne.jp/odz/20071228/1198861783


自分としては、boolのような関数の呼び出しコストが高いという点でまったくその通りかなと思いつつも、こういったこと意識した場合のイディオムとしては、上記のURLで紹介されているx and True or Falseという手法ではなく、

not not value

のようにnot notを使うのが常套手段だったと思っていたので驚きました。
実際に下記のようなテストを試した見たのですが、

# test.py
from timeit import Timer

VALUE = 1

def bool_test():
    return bool(VALUE)

def not_not_test():
    return not not VALUE

def and_or_test():
    return VALUE and True or False

if __name__ == '__main__':
    print 'bool', Timer('bool_test()', 'from __main__ import bool_test').timeit()
    print 'not_not', Timer('not_not_test()', 'from __main__ import not_not_test').timeit()
    print 'and_or', Timer('and_or_test()', 'from __main__ import and_or_test').timeit()

何度やっても、not notの方がよい結果が出ました。(試したのは、Python2.5だけですが・・・)

bool 0.75896191597
not_not 0.444339036942
and_or 0.497380018234


バイトコードは上記URLで比較されているものとは違うとは思いますが、「CALL_FUNCTIONを呼ぶのと呼ばないのはどちらが速いか」、「JUMP_*と他ではどちらが速いか」では大差がないと考えます。

>>> import dis
>>> import test
>>> dis.dis(test.bool_test)
  6           0 LOAD_GLOBAL              0 (bool)
              3 LOAD_GLOBAL              1 (VALUE)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE
>>> dis.dis(test.not_not_test)
  9           0 LOAD_GLOBAL              0 (VALUE)
              3 UNARY_NOT
              4 UNARY_NOT
              5 RETURN_VALUE
>>> dis.dis(test.and_or_test)
 12           0 LOAD_GLOBAL              0 (VALUE)
              3 JUMP_IF_FALSE            7 (to 13)
              6 POP_TOP
              7 LOAD_GLOBAL              1 (True)
             10 JUMP_IF_TRUE             4 (to 17)
        >>   13 POP_TOP
             14 LOAD_GLOBAL              2 (False)
        >>   17 RETURN_VALUE


全くの感想として僕個人の意見を述べるならば、アプリケーションのコードならば、x and Ture or Falseという記述や、not not xという記述ではなく、bool(x)という記述を喜んで使います