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

我希望我能做一些像这样的事情:

>>> isinstance(x, function)

但这给了我:

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

我选这个是因为

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

当前回答

作为公认的答案,John Feminella说:

检查鸭子类型对象属性的正确方法是询问它们是否嘎嘎叫,而不是查看它们是否适合鸭子大小的容器。“直接比较”的方法会对许多函数给出错误的答案,比如内置函数。

尽管有两个库来严格区分函数,但我画了一个详尽的可比表:

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

30.13. inspect -检查活动对象- Python 3.7.0文档

#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”是一般用途的首选解决方案:

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

至于内置函数

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

当进一步检查是内置函数还是用户定义函数

#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

确定是否内置函数

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

总结

采用可调用鸭类型检查函数, 使用类型。如果您有进一步指定的需求,请使用BuiltinFunctionType。

其他回答

函数只是一个带有__call__方法的类,所以你可以这样做

hasattr(obj, '__call__')

例如:

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

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

这是“最好”的方法,但这取决于你为什么需要知道它是可调用的还是注释的,你可以把它放在try/execpt块中:

try:
    x()
except TypeError:
    print "was not callable"

如果try/except比doing if hasattr(x, '__call__'): x()更适合Python,这是有争议的。我会说hasattr更准确,因为你不会意外地捕捉到错误的TypeError,例如:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!

尝试使用callable(x)。

摘录:

如果对象参数显示为可调用,则返回True,否则返回False。

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

下面是一个“repr方法”来检查它。它也适用于。

def a():pass
type(a) #<class 'function'>
str(type(a))=="<class 'function'>" #True

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

如果你学过c++,你一定熟悉函数对象或函子,这意味着任何可以像函数一样调用的对象。

在c++中,普通函数是函数对象,函数指针也是;更一般地说,定义operator()的类的对象也是。在c++ 11及以上版本中,lambda表达式也是函子。

相似的是,在Python中,这些函子都是可调用的。一个普通的函数可以被调用,一个lambda表达式可以被调用,一个函数。Partial可以被调用,具有__call__()方法的类的实例可以被调用。


好,回到问题:我有一个变量x,我想知道它是否指向一个函数。

如果你想判断对象是否像一个函数,那么@John Feminella建议的callable方法是可以的。 如果您想判断一个对象是否只是一个普通函数(不是一个可调用的类实例或lambda表达式),那么xtypes。@Ryan建议的XXX是更好的选择。

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

#!/usr/bin/python3
# 2017.12.10 14:25:01 CST
# 2017.12.10 15:54:19 CST

import functools
import types
import pprint

定义一个类和一个普通函数。

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)

定义函子:

#(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

定义函子列表和类型列表:

## 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]

判断函数是否可调用。如您所见,它们都是可调用的。

res = [callable(xfunc)  for xfunc in xfuncs]
print("functors callable:")
print(res)

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

判断函数类型(types.XXX)。那么函子的类型就不一样了。

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>
"""

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

然后你可以选择合适的函子类型。

如:

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