Как определить, является ли переменная функцией?
У меня есть переменная x
, и я хочу узнать, указывает ли она на функцию или нет.
Я надеялся, что смогу сделать что-то вроде этого:
>>> isinstance(x, function)
Но в результате получаю ошибку:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'function' is not defined
Я выбрал этот подход, потому что при выполнении следующей команды:
>>> type(x)
<type 'function'>
явно видно, что x
является функцией. В чем может быть проблема? Как правильно проверить, является ли переменная функцией?
5 ответ(ов)
Встроенные типы, которые не имеют конструкторов в встроенном пространстве имен (например, функции, генераторы, методы), находятся в модуле types
. Вы можете использовать types.FunctionType
в вызове isinstance
:
>>> import types
>>> types.FunctionType
<class 'function'>
>>> def f(): pass
>>> isinstance(f, types.FunctionType)
True
>>> isinstance(lambda x : None, types.FunctionType)
True
Имейте в виду, что это использует очень специфическое понимание "функции", которое обычно не является тем, что вам нужно. Например, это отклоняет zip
(технически это класс):
>>> type(zip), isinstance(zip, types.FunctionType)
(<class 'type'>, False)
open
(встроенные функции имеют другой тип):
>>> type(open), isinstance(open, types.FunctionType)
(<class 'builtin_function_or_method'>, False)
И random.shuffle
(это метод скрытого экземпляра random.Random
):
>>> type(random.shuffle), isinstance(random.shuffle, types.FunctionType)
(<class 'method'>, False)
Если вы делаете что-то специфическое для экземпляров types.FunctionType
, например, декомпилируете их байт-код или инспектируете переменные замыкания, используйте types.FunctionType
, но если вам просто нужно, чтобы объект был вызываемым, как функция, используйте callable
.
Функция callable(x)
возвращает булевое значение, указывающее, может ли объект x
быть вызван (т.е. является ли он вызываемым, например, функцией или методом). Если x
можно вызвать, функция вернет True
, в противном случае — False
.
Пример использования:
def my_function():
pass
print(callable(my_function)) # Выведет: True
print(callable(5)) # Выведет: False
В этом примере my_function
можно вызвать, поэтому callable(my_function)
возвращает True
. Число 5
не является вызываемым объектом, поэтому callable(5)
возвращает False
.
В Python 2to3 инструмент предлагает использовать следующий код:
import collections
isinstance(obj, collections.Callable)
Это решение было выбрано вместо использования метода hasattr(x, '__call__')
из-за проблемы, описанной в баг-репорте #7006. Основная причина в том, что isinstance
позволяет более точно определить, является ли объект вызываемым, учитывая наследование и другие тонкости, в то время как hasattr
может не всегда корректно отразить это свойство в некоторых случаях.
Таким образом, использование collections.Callable
является более предпочтительным способом проверки вызываемости объектов в данном контексте.
В Python callable(x)
вернет True
, если переданный объект можно вызывать, однако стоит отметить, что функция не существует в 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
и <class '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
, если он не может быть вызван, либо не является вызываемым изначально. Это может быть не так важно в вашей ситуации.
Если вы хотите обнаружить всё, что синтаксически выглядит как функция: функция, метод, встроенная функция/метод, лямбда... но исключить вызывные объекты (объекты с определённым методом __call__
), то попробуйте следующий код:
import types
isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType, types.UnboundMethodType))
Я сравнил это с проверками в коде модуля inspect
, и приведённое выражение гораздо более полно, особенно если ваша цель — отфильтровать любые функции или обнаружить обычные свойства объекта.
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Преобразование списка словарей в DataFrame pandas
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе
Как отменить последнюю миграцию?
Ошибка: "'dict' объект не имеет метода 'iteritems'"