2016年9月7日 星期三

Python 比較運算符 vs 數值運算符 拿來當作 True/False 比較

在 Python 中常會看到有人會拿數值當作判斷式條件,因為 Python 支援使用數值做隱喻的真假值(True/False)運算。在好奇這樣隱喻計算跟一般比較運算符的差異會在哪,因此做了點記錄跟比較。

使用在 sorting 時,可以看到使用比較運算符的速度會比隱喻計算的速度快接近一倍。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time

def numeric_compare(x, y):
    return x - y

def numeric_compare2(x, y):
    return x > y

if __name__ == '__main__':
    a = [9, 11, 2, 8, 13, 0, -13, 21, 55]
    t = 10000000
    tStart = time.time()
    for i in xrange(t):
        sorted(a, cmp=numeric_compare)

    tEnd = time.time()
    print "x-y {} cost {} sec".format(t, tEnd - tStart)
    tStart = time.time()
    for i in xrange(t):
        sorted(a, cmp=numeric_compare2)

    tEnd = time.time()
    print "x>y {} cost {} sec".format(t, tEnd - tStart)

# Running result
# python cmp.py
# x-y 10000000 cost 47.1060450077 sec
# x>y 10000000 cost 24.1215779781 sec

底下是根據官方文件我自己再重寫部分比較運算的實作,可以看到為了要取隱喻的布林值是需要先做完一般計算,之後再去呼叫 __nonzero__ 的結果。直接比較的話就少了一次計算以及產生匿名物件的成本。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class INT:
    def __init__(self, n):
        print "INIT CALLED", n
        self.__value__ = n

    def __nonzero__(self):
        print "Non-Zero Called", self.__value__, self.__value__ != 0
        return self.__value__ != 0

    def __sub__(self, other):
        print self.__value__, "sub", other.__value__
        return INT(self.__value__ - other.__value__)

    def __cmp__(self, other):
        print self.__value__, "cmp", other.__value__
        if self.__value__ < other.__value__:
            return -1
        if self.__value__ == other.__value__:
            return 0
        return 1

def foo():
    a = INT(3)
    b = INT(9)
    c = INT(3)
    if a - b:
        print "a != b"
    if b - a:
        print "b != a"
    if a - c:
        print "a = c"
    if a == c:
        print "a = c"
    if a > b:
        print "a > b"
    if b > a:
        print "b > a"
參考連結:Data model

程式碼上色