问题
I'm writing Python that targets versions 3.2 and higher. It looks like using the built-in function callable is the most straightforward and efficient way to do this. I've seen recommendations for hasattr(x, "__call__")
, collections.Callable(x)
, and just using try/except
around an attempted call.
I've tested items that are callable (a class and a function), using timeit
with 100,000 iterations; in both cases using callable takes only about 75% of the time of checking for the attribute. When the item is not callable (an integer and a string) using callable stays at the same cost as a class or function while checking for the attribute is about 2.3 times more expensive than for a class or function. I didn't expect that difference, but it also favors the clear and concise callable(x)
approach.
But I'm relatively new to Python and no expert, so are there reasons I'm not aware of that I should use the hasattr approach or another approach?
FWIW, the results of the various timeits follow. The first character is just t for timeit, the second indicates what the type of the object being tested (c = class, f = function, i = integer, s = string), and the rest indicates the method (attr - check attribute, 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:
hasattr()
will return more false positives than 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
I'm not sure which callable
you were testing, but both look nicer than hasattr
and handle more cases, so I would use them in place of hasattr()
.
回答2:
callable
is not only the fastest, but the Zen provides four more important reasons to use it instead of the other two contraptions:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Readability counts.
回答3:
Great question! I'd say you should use callable
. Several points apart from the speed issue:
- It's explicit, simple, clear, short, and neat.
- It's a Python built-in, so anyone who doesn't already know what it does can find out easily.
try... except TypeError
has a problem:TypeError
can sometimes be raised by other things. For example, if you successfully call a function which raisesTypeError
in its body, theexcept
will erroneously catch that and assume that the object was not callable.- Some common customisations, like
__getattr__
, can causehasattr
to make mistakes. collections.abc.Callable
seems like rather heavy machinery for something so simple. After all,callable
does the same job.
Footnote: the try
block is a very common pattern in Python for this sort of thing, so you may see a lot of it in other people's code. However, as I've outlined above, this is one case where it's not quite suitable.
来源:https://stackoverflow.com/questions/16388059/using-callablex-vs-hasattrx-call