Как определить, является ли переменная Python функцией? - PullRequest
578 голосов
/ 09 марта 2009

У меня есть переменная x, и я хочу знать, указывает ли она на функцию или нет.

Я надеялся, что смогу сделать что-то вроде:

>>> isinstance(x, function)

Но это дает мне:

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'function' is not defined

Причина, по которой я выбрал это, потому что

>>> type(x)
<type 'function'>

Ответы [ 23 ]

756 голосов
/ 09 марта 2009

Если это для Python 2.x или для Python 3.2+, вы также можете использовать callable(). Раньше он устарел, но теперь не устарел, поэтому вы можете использовать его снова. Вы можете прочитать обсуждение здесь: http://bugs.python.org/issue10518. Вы можете сделать это с помощью:

callable(obj)

Если это для Python 3.x, но до 3.2, проверьте, имеет ли объект атрибут __call__. Вы можете сделать это с:

hasattr(obj, '__call__')

Часто предлагаемый types.FunctionTypes подход не верен, потому что он не охватывает многие случаи, которые вы, вероятно, хотели бы, чтобы он прошел, как со встроенными:

>>> isinstance(open, types.FunctionType)
False

>>> callable(open)
True

Правильный способ проверить свойства объектов типа "утка" - это спросить их, если они крякают, а не посмотреть, помещаются ли они в контейнер размером с утку. Не используйте types.FunctionType, если у вас нет четкого представления о функции.

240 голосов
/ 09 марта 2009

Встроенные типы, которые не имеют конструкторов во встроенном пространстве имен (например, функции, генераторы, методы), находятся в модуле types. Вы можете использовать types.FunctionType в вызове isinstance.

In [1]: import types
In [2]: types.FunctionType
Out[2]: <type 'function'>
In [3]: def f(): pass
   ...:
In [4]: isinstance(f, types.FunctionType)
Out[4]: True
In [5]: isinstance(lambda x : None, types.FunctionType)
Out[5]: True
84 голосов
/ 29 ноября 2011

Начиная с Python 2.1 , вы можете импортировать isfunction из inspect модуля.

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
65 голосов
/ 09 сентября 2013

Принятый ответ был в то время, когда он был предложен, считался правильным. Как это Оказывается, есть без замены для callable(), который вернулся в Python 3.2: В частности, callable() проверяет поле tp_call объекта, являющегося объектом испытания. Нет простого эквивалента Python. Большинство предлагаемых тестов исправить большую часть времени:

>>> class Spam(object):
...     def __call__(self):
...         return 'OK'
>>> can_o_spam = Spam()


>>> can_o_spam()
'OK'
>>> callable(can_o_spam)
True
>>> hasattr(can_o_spam, '__call__')
True
>>> import collections
>>> isinstance(can_o_spam, collections.Callable)
True

Мы можем бросить гаечный ключ в это, удалив __call__ из учебный класс. И просто чтобы сделать вещи более захватывающими, добавьте фальшивку __call__ к экземпляру!

>>> del Spam.__call__
>>> can_o_spam.__call__ = lambda *args: 'OK?'

Обратите внимание, это действительно не вызывается:

>>> can_o_spam()
Traceback (most recent call last):
  ...
TypeError: 'Spam' object is not callable

callable() возвращает правильный результат:

>>> callable(can_o_spam)
False

Но hasattr это неправильно :

>>> hasattr(can_o_spam, '__call__')
True

can_o_spam имеет этот атрибут в конце концов; это просто не используется при звонке экземпляр.

Еще тоньше, isinstance() также ошибается:

>>> isinstance(can_o_spam, collections.Callable)
True

Поскольку мы использовали эту проверку раньше, а позже удалили метод, abc.ABCMeta кеширует результат. Возможно, это ошибка в abc.ABCMeta. Это сказало, действительно нет никакого способа, которым мог бы дать более точный результат результат, чем при использовании callable(), так как typeobject->tp_call Слот-метод недоступен другим способом.

Просто используйте callable()

35 голосов
/ 09 марта 2009

Следующее должно возвращать логическое значение:

callable(x)
24 голосов
/ 20 ноября 2010

Инструмент Python 2to3 (http://docs.python.org/dev/library/2to3.html) предлагает:

import collections
isinstance(obj, collections.Callable)

Кажется, это было выбрано вместо hasattr(x, '__call__') метода из-за http://bugs.python.org/issue7006.

19 голосов
/ 09 марта 2009

callable(x) будет возвращать true, если переданный объект может быть вызван в Python, но функция не существует в Python 3.0, и, по сути, не будет различать:

class A(object):
    def __call__(self):
        return 'Foo'

def B():
    return 'Bar'

a = A()
b = B

print type(a), callable(a)
print type(b), callable(b)

Вы получите <class 'A'> True и <type function> True в качестве вывода.

isinstance отлично работает, чтобы определить, является ли что-то функцией (попробуйте isinstance(b, types.FunctionType)); если вы действительно хотите узнать, можно ли что-то вызвать, вы можете использовать hasattr(b, '__call__') или просто попробовать.

test_as_func = True
try:
    b()
except TypeError:
    test_as_func = False
except:
    pass

Это, конечно, не скажет вам, может ли оно быть вызвано, но выдает TypeError при выполнении или не вызывается в первую очередь. Это может не иметь значения для вас.

13 голосов
/ 28 ноября 2012

Если вы хотите обнаружить все, что синтаксически выглядит как функция: функция, метод, встроенный fun / meth, lambda ... но исключают вызываемые объекты (объекты с определенным методом __call__) ), затем попробуйте это:

import types
isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType, types.UnboundMethodType))

Я сравнил это с кодом is*() проверок в модуле inspect, и приведенное выше выражение является гораздо более полным, особенно если ваша цель - отфильтровать какие-либо функции или обнаружить обычные свойства объекта.

6 голосов
/ 09 марта 2009

Попробуйте использовать callable(x).

5 голосов
/ 23 января 2014

Вот еще несколько способов:

def isFunction1(f) :
    return type(f) == type(lambda x: x);

def isFunction2(f) :
    return 'function' in str(type(f));

Вот как я придумал второе:

>>> type(lambda x: x);
<type 'function'>
>>> str(type(lambda x: x));
"<type 'function'>"
# Look Maa, function! ... I ACTUALLY told my mom about this!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...