Как определить, является ли переменная функцией?
У меня есть переменная 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?
'pip' не распознан как командa внутреннего или внешнего формата
Почему statistics.mean() работает так медленно?
Преобразование строки даты JSON в datetime в Python
Есть ли разница между поднятием экземпляра класса Exception и самого класса Exception?