2013-05-05 87 views
7

我正在编写针对版本3.2和更高版本的Python。它看起来像使用内置函数可调用是最直接和最有效的方式来做到这一点。我已经看到了关于hasattr(x, "__call__"),collections.Callable(x)的建议,并且只是在尝试呼叫周围使用try/except使用callable(x)与hasattr(x,“__call__”)

我测试过可调用的项目(类和函数),使用timeit进行100,000次迭代;在这两种情况下使用可调用只需要检查属性的大约75%的时间。当不可调用的项目(一个整数和一个字符串)使用callable保持与类或函数相同的成本时,检查该属性比类或函数贵大约2.3倍。我没有想到这种差异,但它也有利于清晰简洁的方法。

但是我对Python比较陌生,没有专家,所以有没有原因我不知道我应该使用hasattr方法或其他方法?

FWIW,各种时间表的结果如下。第一个字符只是timeit的第二个字符,第二个字符表示正在测试的对象的类型(c = class,f = function,i = integer,s = string),其余的表示方法(attr - check属性, call - use callable,try - use try/except)。

 
tcattr 0.03665385400199739 
tccall 0.026238360142997408 
tctry 0.09736267629614304 
tfattr 0.03624538065832894 
tfcall 0.026362861895904643 
tftry 0.032501874250556284 
tiattr 0.08297350149314298 
ticall 0.025826044152381655 
titry 0.10657657453430147 
tsattr 0.0840187013927789 
tscall 0.02585409547373274 
tstry 0.10742772077628615 
+1

旁注:可调用[已在python3.0中移除](http://www.python.org/dev/peps/pep-3100/#built-in-namespace)并且 - 在注意到[这是一个糟糕的选择](http://bugs.python.org/issue10518) - [带回3.2](http://docs.python.org/3.2/library/functions.html?highlight=callable#callable) – mata 2013-05-05 20:22:23

回答

6

hasattr()将返回更多的假阳性比callable

>>> class X(object): 
...  def __getattr__(self, name): 
...   return name 
... 
>>> i = X() 
>>> from collections import Callable 
>>> isinstance(i, Callable) 
False 
>>> callable(i) 
False 
>>> hasattr(i, '__call__') 
True 
>>> i() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'X' object is not callable 

我不知道你是哪个callable测试,但两者看起来比hasattr更好,处理更多的案件,所以我会使用它们hasattr()的地方。

+1

[可调用的]](http://docs.python.org/3/library/functions.html#callable)也可以返回[false positives](http://ideone.com/PexRWg)。 – jfs 2013-05-05 21:24:22

+1

@ J.F.Sebastian - 确实如此,但只有一个疯狂的人会用一个不可调用的'__call__'定义一个类。这只是真实代码中的一个错误。 – 2013-05-05 22:04:58

+0

谢谢你的观点。为了完整起见,我使用了内置的可调用函数,而不是collections.Callable。 – bmacnaughton 2013-05-06 01:07:44

4

callable不仅是最快的,但Zen提供了四个比较重要的原因,用它代替其他两个玩意儿:

美丽的比丑好。
显式优于隐式。
简单胜过复杂。
可读性计数。

4

伟大的问题!我想说你应该使用callable。除了速度问题,还有几点:

  1. 它明确,简单,清晰,简短,整齐。
  2. 这是一个内置的Python,所以任何不知道它做什么的人都可以轻松找到它。
  3. try... except TypeError有一个问题:TypeError有时可以通过其他的东西引发。例如,如果您成功调用一个函数,该函数在其正文中引发TypeError,则except将错误地捕获该函数,并假定该对象不可调用。
  4. 一些常见的自定义设置(如__getattr__)可能导致hasattr出错。
  5. collections.abc.Callable对于如此简单的东西来说,它似乎是相当重的机器。毕竟,callable做同样的工作。

脚注:该try块是在Python very common pattern对于这样的事情,所以你可以看到其他人的代码不少呢。但是,正如我上面所概述的那样,这是一种不太合适的情况。