7

我正在编写针对 3.2 及更高版本的 Python。看起来使用内置函数 callable 是最直接有效的方法。我已经看到了对 , 和 的建议hasattr(x, "__call__")collections.Callable(x)并且只是try/except在尝试调用时使用。

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

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

FWIW,各种时间的结果如下。第一个字符只是 t 表示 timeit,第二个表示正在测试的对象的类型(c = 类,f = 函数,i = 整数,s = 字符串),其余表示方法(attr - 检查属性, call - 使用 callable,try - 使用 try/except)。

tcattr 0.03665385400199739
tccall 0.026238360142997408
tctry 0.09736267629614304
tfattr 0.03624538065832894
调用 0.026362861895904643
tftry 0.032501874250556284
tiattr 0.08297350149314298
0.025826044152381655
滴定度 0.10657657453430147
tsattr 0.0840187013927789
调用 0.02585409547373274
测试 0.10742772077628615
4

3 回答 3

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().

于 2013-05-05T19:17:03.763 回答
4

callable不仅是最快的,而且Zen提供了四个更重要的理由来使用它而不是其他两个装置:

美丽总比丑陋好。
显式优于隐式。
简单胜于复杂。
可读性很重要。

于 2013-05-05T19:16:05.113 回答
4

好问题!我会说你应该使用callable. 除了速度问题还有几点:

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

脚注:tryblock 是Python 中用于此类事情的一种非常常见的模式,因此您可能会在其他人的代码中看到很多它。但是,正如我上面所概述的,这是一种不太适合的情况。

于 2013-05-05T19:23:42.870 回答