如何检测python变量是否是函数?

How do I detect whether a Python variable is a function?

我有一个变量,x,我想知道它是否指向一个函数。

我曾希望我能做点什么,比如:

1
>>> isinstance(x, function)

但这给了我:

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

我选择的原因是

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


如果这是针对python 2.x或python 3.2+,您也可以使用callable()。它以前是不推荐使用的,但现在是未预先准备好的,所以您可以再次使用它。您可以在这里阅读讨论:http://bugs.python.org/issue10518。您可以使用以下方法进行此操作:

1
callable(obj)

如果这是针对python 3.x的,但在3.2之前,请检查对象是否具有__call__属性。您可以使用以下方法进行此操作:

1
hasattr(obj, '__call__')

OFT建议的types.FunctionTypes方法是不正确的,因为它无法涵盖许多您可能希望它通过的情况,例如内置的:

1
2
3
4
5
>>> isinstance(open, types.FunctionType)
False

>>> callable(open)
True

检查鸭子类型物体属性的正确方法是询问它们是否嘎嘎叫,而不是看它们是否适合鸭子大小的容器。不要使用types.FunctionType,除非您对函数有一个非常具体的概念。


内置命名空间中没有构造函数的内置类型(例如函数、生成器、方法)位于types模块中。您可以在isInstance调用中使用types.FunctionType

1
2
3
4
5
6
7
8
9
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


由于python 2.1,您可以从inspect模块导入isfunction

1
2
3
4
5
6
>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True


被接受的答案在提出时被认为是正确的。作为它事实证明,没有替代callable(),它回到了Python中。3.2:具体来说,callable()检查被测对象的tp_call字段。测试。没有对应的纯Python。大多数建议的测试是大部分时间都是正确的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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__

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

注意,这是不可调用的:

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

callable()返回正确的结果:

1
2
>>> callable(can_o_spam)
False

hasattr是错误的:

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

can_o_spam毕竟有这个属性,只是在调用实例。

更微妙的是,isinstance()也犯了这样的错误:

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

因为我们之前和之后都使用了这种检查方法,所以删除了abc.ABCMeta。缓存结果。可以说这是abc.ABCMeta中的一个bug。这就是说,真的没有比这更精确的方法了结果比使用callable()本身,因为typeobject->tp_call。槽方法不能以任何其他方式访问。

只需使用EDOCX1[0]


以下应返回布尔值:

1
callable(x)


python的2to3工具(http://docs.python.org/dev/library/2to3.html)建议:

1
2
import collections
isinstance(obj, collections.Callable)

由于http://bugs.python.org/issue7006,似乎选择了这个方法而不是hasattr(x, '__call__')方法。


如果传递的对象可以在python中调用,但该函数在python 3.0中不存在,则callable(x)将返回true,正确地说,不会区分:

1
2
3
4
5
6
7
8
9
10
11
12
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)

你将得到 True True作为输出。

isinstance在确定某个东西是否是一个函数方面工作得很好(尝试isinstance(b, types.FunctionType);如果你真的有兴趣知道某个东西是否可以被调用,你可以使用hasattr(b, '__call__')或者直接尝试它。

1
2
3
4
5
6
7
test_as_func = True
try:
    b()
except TypeError:
    test_as_func = False
except:
    pass

当然,这不会告诉你它是可调用的,但会在执行时抛出一个TypeError,或者一开始就不可调用。那对你来说也许不重要。


如果您想检测语法上看起来像函数的所有东西:函数、方法、内置的fun/meth、lambda…但排除可调用对象(定义了__call__方法的对象),然后尝试以下方法:

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

我将它与inspect模块中的is*()检查的代码进行了比较,上面的表达式更加完整,特别是如果您的目标是过滤掉任何函数或检测对象的正则属性。


尝试使用callable(x)


以下是其他几种方法:

1
2
3
4
5
def isFunction1(f) :
    return type(f) == type(lambda x: x);

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

下面是我提出第二个问题的方法:

1
2
3
4
5
>>> type(lambda x: x);
<type 'function'>
>>> str(type(lambda x: x));
"<type 'function'>"
# Look Maa, function! ... I ACTUALLY told my mom about this!


注意,python类也可以调用。

要获取函数(按函数,我们是指标准函数和lambda),请使用:

1
2
3
4
5
6
7
8
9
10
11
12
import types

def is_func(obj):
    return isinstance(obj, (types.FunctionType, types.LambdaType))


def f(x):
    return x


assert is_func(f)
assert is_func(lambda x: x)

如果你学过C++,你必须熟悉function objectfunctor,指任何可以be called as if it is a function的物体。

在C++中,EDCOX1×25是一个函数对象,函数指针也是如此;更一般地说,定义EDCOX1×26的类的对象也是如此。在C++ 11和更大的情况下,EDCOX1×27是EDCOX1,23也是如此。

在python中,这些functors都是callablean ordinary function可以调用,a lambda expression可以调用,functional.partial可以调用,class with a __call__() method的实例可以调用。

好吧,回到问题上来:I have a variable, x, and I want to know whether it is pointing to a function or not.

If you want to judge weather the object acts like a function, then the callable method suggested by @John Feminella is ok.

If you want to judge whether a object is just an ordinary function or not( not a callable class instance, or a lambda expression), then the xtypes.XXX suggested by @Ryan is a better choice.

然后我用这些代码做了一个实验:

1
2
3
4
5
6
7
#!/usr/bin/python3
# 2017.12.10 14:25:01 CST
# 2017.12.10 15:54:19 CST

import functools
import types
import pprint

Define a class and an ordinary function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A():
    def __call__(self, a,b):
        print(a,b)
    def func1(self, a, b):
        print("[classfunction]:", a, b)
    @classmethod
    def func2(cls, a,b):
        print("[classmethod]:", a, b)
    @staticmethod
    def func3(a,b):
        print("[staticmethod]:", a, b)

def func(a,b):
    print("[function]", a,b)

Define the functors:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#(1.1) built-in function
builtins_func = open
#(1.2) ordinary function
ordinary_func = func
#(1.3) lambda expression
lambda_func  = lambda a : func(a,4)
#(1.4) functools.partial
partial_func = functools.partial(func, b=4)

#(2.1) callable class instance
class_callable_instance = A()
#(2.2) ordinary class function
class_ordinary_func = A.func1
#(2.3) bound class method
class_bound_method = A.func2
#(2.4) static class method
class_static_func = A.func3

Define the functors' list and the types' list:

1
2
3
4
## list of functors
xfuncs = [builtins_func, ordinary_func, lambda_func, partial_func, class_callable_instance, class_ordinary_func, class_bound_method, class_static_func]
## list of type
xtypes = [types.BuiltinFunctionType, types.FunctionType, types.MethodType, types.LambdaType, functools.partial]

Judge wether the functor is callable. As you can see, they all are callable.

1
2
3
4
5
6
7
8
res = [callable(xfunc)  for xfunc in xfuncs]
print("functors callable:")
print(res)

"""
functors callable:
[True, True, True, True, True, True, True, True]
"""

Judge the functor's type( types.XXX). Then the types of functors are not all the same.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
res = [[isinstance(xfunc, xtype) for xtype in xtypes] for xfunc in xfuncs]

## output the result
print("functors' types")
for (row, xfunc) in zip(res, xfuncs):
    print(row, xfunc)

"""
functors' types
[True, False, False, False, False] <built-in function open>
[False, True, False, True, False] <function func at 0x7f1b5203e048>
[False, True, False, True, False] <function <lambda> at 0x7f1b5081fd08>
[False, False, False, False, True] functools.partial(<function func at 0x7f1b5203e048>, b=4)
[False, False, False, False, False] <__main__.A object at 0x7f1b50870cc0>
[False, True, False, True, False] <function A.func1 at 0x7f1b5081fb70>
[False, False, True, False, False] <bound method A.func2 of <class '__main__.A'>>
[False, True, False, True, False] <function A.func3 at 0x7f1b5081fc80>
"""

我用这些数据绘制了一个可调用函数类型表。

enter image description here

Then you can choose the functors' types that suitable.

例如:

1
2
3
4
5
6
7
8
9
10
11
12
def func(a,b):
    print("[function]", a,b)

>>> callable(func)
True
>>> isinstance(func,  types.FunctionType)
True
>>> isinstance(func, (types.BuiltinFunctionType, types.FunctionType, functools.partial))
True
>>>
>>> isinstance(func, (types.MethodType, functools.partial))
False

函数只是一个带有__call__方法的类,因此可以

1
hasattr(obj, '__call__')

例如:

1
2
3
4
5
6
>>> hasattr(x, '__call__')
True

>>> x = 2
>>> hasattr(x, '__call__')
False

这是最好的方法,但是根据您需要知道它是可调用的还是可注释的原因,您可以将它放在一个try/execpt块中:

1
2
3
4
try:
    x()
except TypeError:
    print"was not callable"

如果Try/Except比if hasattr(x, '__call__'): x()更像是python'y,那么这是有争议的。我认为hasattr更准确,因为您不会意外地发现错误的类型错误,例如:

1
2
3
4
5
6
7
8
9
10
11
>>> def x():
...     raise TypeError
...
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print"x was not callable"
...
x was not callable # Wrong!


因为类也有__call__方法,所以我推荐另一种解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A(object):
    def __init__(self):
        pass
    def __call__(self):
        print 'I am a Class'

MyClass = A()

def foo():
    pass

print hasattr(foo.__class__, 'func_name') # Returns True
print hasattr(A.__class__, 'func_name')   # Returns False as expected

print hasattr(foo, '__call__') # Returns True
print hasattr(A, '__call__')   # (!) Returns True while it is not a function


作为公认的答案,John Feminella指出:

The proper way to check properties of duck-typed objects is to ask them if they quack, not to see if they fit in a duck-sized container. The"compare it directly" approach will give the wrong answer for many functions, like builtins.

尽管有两个libs可以严格区分函数,但我还是绘制了一个详尽的可比表:

8.9。类型-内置类型的动态类型创建和名称-python 3.7.0文档

30.13。检查-检查活动对象-python 3.7.0文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#import inspect             #import types
['isabstract',
 'isasyncgen',              'AsyncGeneratorType',
 'isasyncgenfunction',
 'isawaitable',
 'isbuiltin',               'BuiltinFunctionType',
                            'BuiltinMethodType',
 'isclass',
 'iscode',                  'CodeType',
 'iscoroutine',             'CoroutineType',
 'iscoroutinefunction',
 'isdatadescriptor',
 'isframe',                 'FrameType',
 'isfunction',              'FunctionType',
                            'LambdaType',
                            'MethodType',
 'isgenerator',             'GeneratorType',
 'isgeneratorfunction',
 'ismethod',
 'ismethoddescriptor',
 'ismodule',                'ModuleType',        
 'isroutine',            
 'istraceback',             'TracebackType'
                            'MappingProxyType',
]

"duck typing"是通用的首选解决方案:

1
2
3
4
5
6
7
def detect_function(obj):
    return hasattr(obj,"__call__")

In [26]: detect_function(detect_function)
Out[26]: True
In [27]: callable(detect_function)
Out[27]: True

至于内置功能

1
2
In [43]: callable(hasattr)
Out[43]: True

当执行另一步以检查内置函数或用户定义函数时

1
2
3
4
5
6
7
8
9
10
#check inspect.isfunction and type.FunctionType
In [46]: inspect.isfunction(detect_function)
Out[46]: True
In [47]: inspect.isfunction(hasattr)
Out[47]: False
In [48]: isinstance(detect_function, types.FunctionType)
Out[48]: True
In [49]: isinstance(getattr, types.FunctionType)
Out[49]: False
#so they both just applied to judge the user-definded

确定builtin function

1
2
3
4
In [50]: isinstance(getattr, types.BuiltinFunctionType)
Out[50]: True
In [51]: isinstance(detect_function, types.BuiltinFunctionType)
Out[51]: False

总结

使用callable来避开类型检查功能,如果您有进一步指定的需求,请使用types.BuiltinFunctionType


您可以检查用户定义的函数是否具有func_namefunc_doc等属性,而不是检查'__call__'(不排除于函数),这对方法不起作用。

1
2
3
4
>>> def x(): pass
...
>>> hasattr(x, 'func_name')
True

另一种检查方法是使用来自inspect模块的isfunction()方法。

1
2
3
>>> import inspect
>>> inspect.isfunction(x)
True

要检查对象是否为方法,请使用inspect.ismethod()


任何函数都是类,因此您可以使用实例x的类的名称并进行比较:

1
2
if(x.__class__.__name__ == 'function'):
     print"it's a function"

一些答案中提到的使用hasattr(obj, '__call__')callable(.)的解决方案有一个主要缺点:对于类和使用__call__()方法的类的实例,它们都返回True。如。

1
2
3
4
5
6
>>> import collections
>>> Test = collections.namedtuple('Test', [])
>>> callable(Test)
True
>>> hasattr(Test, '__call__')
True

检查一个对象是否是用户定义的函数的一种正确方法是使用isfunction(.)

1
2
3
4
5
6
>>> import inspect
>>> inspect.isfunction(Test)
False
>>> def t(): pass
>>> inspect.isfunction(t)
True

如果您需要检查其他类型,请查看inspect-inspect活动对象。


在python3中,我提出了type (f) == type (lambda x:x),如果f是一个函数,则生成True,如果f不是函数,则生成False。但我觉得我更喜欢isinstance (f, types.FunctionType),这感觉不那么特别。我想做type (f) is function,但这行不通。


如果代码继续执行调用,如果值是可调用的,只需执行调用并捕获TypeError

1
2
3
4
5
def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")


下面是一个"repr方法"来检查它。它也与lambda一起工作。

1
2
3
4
5
6
def a():pass
type(a) #<class 'function'>
str(type(a))=="<class 'function'>" #True

b = lambda x:x*2
str(type(b))=="<class 'function'>" #True

在之前的回复之后,我提出了这个问题:

1
2
3
4
5
6
7
8
9
from pprint import pprint

def print_callables_of(obj):
    li = []
    for name in dir(obj):
        attr = getattr(obj, name)
        if hasattr(attr, '__call__'):
            li.append(name)
    pprint(li)