当我遇到一组奇怪的时序结果时,我正在搞乱一个小的自定义数据对象,需要可以清洗,可比较和快速。这个对象的一些比较(和散列方法)只是委托给一个属性,所以我使用的是:
def __hash__(self):
return self.foo.__hash__()
然而,经过测试,我发现hash(self.foo)
明显更快。好奇,我测试了__eq__
,__ne__
以及其他魔术比较,但发现如果我使用含糖形式(<{1}},所有的运行速度更快,==
,!=
等)。为什么是这样?我认为加糖形式必须在引擎盖下进行相同的函数调用,但也许情况并非如此?
设置:围绕控制所有比较的实例属性的薄包装。
<
我的自定义对象正在包装Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:13:51) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>>
>>> sugar_setup = '''\
... import datetime
... class Thin(object):
... def __init__(self, f):
... self._foo = f
... def __hash__(self):
... return hash(self._foo)
... def __eq__(self, other):
... return self._foo == other._foo
... def __ne__(self, other):
... return self._foo != other._foo
... def __lt__(self, other):
... return self._foo < other._foo
... def __gt__(self, other):
... return self._foo > other._foo
... '''
>>> explicit_setup = '''\
... import datetime
... class Thin(object):
... def __init__(self, f):
... self._foo = f
... def __hash__(self):
... return self._foo.__hash__()
... def __eq__(self, other):
... return self._foo.__eq__(other._foo)
... def __ne__(self, other):
... return self._foo.__ne__(other._foo)
... def __lt__(self, other):
... return self._foo.__lt__(other._foo)
... def __gt__(self, other):
... return self._foo.__gt__(other._foo)
... '''
,这就是我使用的内容,但它不应该有任何区别。是的,我在测试中创建了日期时间,所以显然有一些相关的开销,但是从一个测试到另一个测试的开销是不变的,所以它不应该有所作为。为简洁起见,我省略了datetime
和__ne__
测试,但这些结果与此处显示的结果基本相同。
__gt__
>>> test_hash = '''\
... for i in range(1, 1000):
... hash(Thin(datetime.datetime.fromordinal(i)))
... '''
>>> test_eq = '''\
... for i in range(1, 1000):
... a = Thin(datetime.datetime.fromordinal(i))
... b = Thin(datetime.datetime.fromordinal(i+1))
... a == a # True
... a == b # False
... '''
>>> test_lt = '''\
... for i in range(1, 1000):
... a = Thin(datetime.datetime.fromordinal(i))
... b = Thin(datetime.datetime.fromordinal(i+1))
... a < b # True
... b < a # False
... '''
答案 0 :(得分:12)
有两个原因:
API查找仅查看类型。他们不看self.foo.__hash__
,他们会查找type(self.foo).__hash__
。这是一本不太容易找到的词典。
C槽查找比纯Python属性查找(将使用__getattribute__
)更快;相反,查找方法对象(包括描述符绑定)完全在C中完成,绕过__getattribute__
。
因此,您必须在本地缓存type(self._foo).__hash__
查找,即使这样,调用也不会像C代码那样快。如果速度非常快,请坚持使用标准库函数。
避免直接调用魔术方法的另一个原因是比较运算符 more 而不是只调用一个魔术方法;方法也反映了版本;对于x < y
,如果未定义x.__lt__
或x.__lt__(y)
返回NotImplemented
单身,则还会咨询y.__gt__(x)
。