2013-05-05 3 views
7

Я пишу Python, который нацелен на версии 3.2 и выше. Похоже, что использование встроенной функции, вызываемой, является наиболее простым и эффективным способом для этого. Я видел рекомендации для hasattr(x, "__call__"), collections.Callable(x) и просто использовал try/except вокруг попытки вызова.Использование callable (x) vs. hasattr (x, "__call__")

Я тестировал элементы, которые можно назвать (класс и функция), используя timeit с 100 000 итерациями; в обоих случаях использование вызываемого объекта занимает всего около 75% времени проверки атрибута. Когда элемент не может быть вызван (целое число и строка) с использованием вызываемых остатков с той же стоимостью, что и класс или функция, при проверке атрибута примерно в 2,3 раза дороже, чем для класса или функции. Я не ожидал этой разницы, но это также способствует четкому и кратким подходам callable(x).

Но я относительно новичок в Python и не эксперт, поэтому есть причины, по которым я не знаю, что я должен использовать подход hasattr или другой подход?

FWIW, результаты различных сроков следуют. Первый символ - это просто t для timeit, второй - тип тестируемого объекта (c = class, f = function, i = integer, s = string), а остальное указывает метод (attr - check attribute, call - use callable, try - используйте 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

sidenote: вызываемый был удален в 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

ответ

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) также может возвращать [ложные срабатывания] (http://ideone.com/PexRWg). – jfs

+1

@ J.F.Sebastian - Правда, но только сумасшедший человек будет определять класс с не вызываемым '__call__'. Это будет просто ошибкой в ​​реальном коде. –

+0

Спасибо вам за перспективу. Для полноты я использовал встроенные вызываемые, а не коллекции.Callable. – bmacnaughton

4

callable не только самый быстрый, но Zen обеспечивает еще четыре важные причины, чтобы использовать его вместо двух других хитрых:

Красивое лучше, чем уродливое.
Явный лучше, чем неявный.
Простой лучше, чем сложный.
Показатели удобочитаемости.

4

Отличный вопрос! Я бы сказал, вы должны использовать callable. Несколько вопросов, помимо скорости выпуска:

  1. Это явный, простой, понятный, короткий и аккуратный.
  2. Это встроенный Python, поэтому любой, кто еще не знает, что он делает, может легко найти.
  3. try... except TypeError имеет проблемы: TypeError иногда могут быть подняты другими вещами. Например, если вы успешно вызываете функцию, которая поднимает TypeError в ее теле, то except ошибочно поймает это и предположит, что объект не был вызван.
  4. Некоторые общие настройки, такие как __getattr__, могут привести к ошибке hasattr.
  5. collections.abc.Callable кажется довольно тяжелым механизмом для чего-то такого простого. В конце концов, callable выполняет ту же работу.

Сноска: try блока является very common pattern в Python для такого рода вещей, так что вы можете увидеть много его в коде других людей. Однако, как я изложил выше, это один случай, когда он не совсем подходит.

Смежные вопросы