9

Как определить, является ли переменная функцией?

9

У меня есть переменная 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 ответ(ов)

3

Встроенные типы, которые не имеют конструкторов в встроенном пространстве имен (например, функции, генераторы, методы), находятся в модуле 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.

0

Функция 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.

0

В Python 2to3 инструмент предлагает использовать следующий код:

import collections
isinstance(obj, collections.Callable)

Это решение было выбрано вместо использования метода hasattr(x, '__call__') из-за проблемы, описанной в баг-репорте #7006. Основная причина в том, что isinstance позволяет более точно определить, является ли объект вызываемым, учитывая наследование и другие тонкости, в то время как hasattr может не всегда корректно отразить это свойство в некоторых случаях.

Таким образом, использование collections.Callable является более предпочтительным способом проверки вызываемости объектов в данном контексте.

0

В 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, если он не может быть вызван, либо не является вызываемым изначально. Это может быть не так важно в вашей ситуации.

0

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

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

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь