如何检测Python变量是否为函数?


684

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

我曾希望我可以做些类似的事情:

>>> isinstance(x, function)

但这给了我:

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

我之所以选择,是因为

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

37
通过寻找一些调用属性或可调用函数,解决问题的答案数量让我感到沮丧。一种干净的方法是关于@ryan建议的type(a)== types.functionType
AsTeR 2013年

44
@AsTeR检查鸭子类型对象属性的正确方法是询问它们是否发出嘎嘎声,而不是查看它们是否适合鸭子大小的容器。“直接比较”方法将对许多功能(例如内置函数)给出错误的答案。
John Feminella 2014年

3
@JohnFeminella虽然我原则上同意你的看法。OP并没有询问它是否可以调用,只是它是否是一个函数。也许可以说他需要在功能和类之间进行区分?
麦凯

3
出于我的目的,我之所以来到这里是因为我想insepct.getsource在各种对象上使用它,实际上,与该对象是否可调用无关,而与它是否具有“功能”有关type(obj)。自从google将我带到这里以来,我想说AsTeR的评论是最有用的答案(对我而言)。互联网上还有许多其他地方供人们发现__call__或游览callable
tsbertalan

4
@AsTeR这是types.FunctionType,大写F.
本·马雷什

Answers:


891

如果这是用于Python 2.x或Python 3.2+,则也可以使用callable()。它曾经不推荐使用,但是现在不推荐使用,因此您可以再次使用它。您可以在此处阅读讨论内容:http : //bugs.python.org/issue10518。您可以执行以下操作:

callable(obj)

如果这是针对Python 3.x但在3.2之前的版本,请检查对象是否具有__call__属性。您可以执行以下操作:

hasattr(obj, '__call__')

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

>>> isinstance(open, types.FunctionType)
False

>>> callable(open)
True

检查鸭子型物体属性的正确方法是询问它们是否发出嘎嘎声,而不是查看它们是否适合鸭子大小的容器。types.FunctionType除非您对功能是什么有一个非常具体的了解,否则不要使用。


73
这也不会告诉您它是否是一个函数-只是可以调用它。
克里斯·B

23
区分是否重要取决于应用程序;我怀疑您是对的,不是针对最初的问题,但这还不确定。
克里斯·B,2009年

5
类可以附加一个调用函数。因此,这绝对不是区分的好方法。瑞安的方法更好。
Brian Bruggeman

43
“鸭子打字”的概念使这个问题更好地解决,例如“只要它像一个函数一样起作用,这有什么关系?”
jcomeau_ictx 2012年

8
在某些用例中,可调用函数与函数之间的区别至关重要,例如在编写装饰器时(请参阅我对Ryan答案的评论)。
Turion

266

内置名称空间中没有构造函数的内置类型(例如,函数,生成器,方法)位于types模块中。您可以types.FunctionTypeisinstance通话中使用:

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

请注意,这使用了非常特殊的“功能”概念,通常不是您所需要的。例如,它拒绝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


5
+1回答问题。但是,试图猜测一个对象是否是一个函数-甚至是任何可调用的对象-通常都是错误的。如果没有OP的进一步信息,就很难毫无希望地将其消除,但是...
bobince

47
实际上,对于内置函数,它将返回False,例如对于“ open”。因此,要明确地说,您将必须使用isinstance(f,(types.FunctionType,types.BuiltinFunctionType))。当然,如果您严格只需要函数,而不是可调用对象或方法,则不可以。
Lukasz Korzybski

5
@ŁukaszKorzybski,更准确地说,您还应该检查functools.partial:isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))f.func在这种情况下进行检查。
estani 2013年

3
@bobince,这个用例怎么样:我想写一个装饰器 @foo,可以同时使用as @foo和as @foo(some_parameter)。然后,它需要检查用什么来调用它,例如装饰函数(第一种情况)或参数(第二种情况,需要返回另一个装饰器)。
Turion

types.BuiltinFunctionType也是(“普通”)内置方法的类型,如果您不打算走这callable条路线,则可能不希望使用。
user2357112支持莫妮卡

92

从Python 2.1开始,您可以isfunctioninspect模块导入。

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True

3
不错,但是对于像open和的内置函数,似乎返回False hasattr
Zecc

12
@Zecc isbuiltin为此。
Paolo

13
请参阅inspect.isfunction文档字符串:“如果对象是用户定义的函数,则返回true。”
Mark Mikofski 2013年

4
请注意,“ isfunction”无法识别functool.partial函数。
ishmael 2013年

74

公认的答案是在提供该答案时被认为是正确的。事实证明,有无可替代callable(),这是背面在Python 3.2:具体地,callable()检查tp_call对象的领域被测试。没有普通的Python等效项。大多数建议的测试在大多数情况下都是正确的:

>>> 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__从类中删除,我们可以为此投入很多精力。只是为了让事情变得更加令人兴奋,请__call__为实例添加伪造品!

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

注意,这确实是不可调用的:

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

callable() 返回正确的结果:

>>> callable(can_o_spam)
False

但是hasattr错误的

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

can_o_spam确实具有那个属性;只是在调用实例时不使用它。

更微妙的是,isinstance()也会出错:

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

因为我们之前使用了此检查,后来又删除了该方法,abc.ABCMeta 所以将结果缓存。可以说这是一个错误abc.ABCMeta。就是说,与使用结果本身相比,它实际上不可能产生比结果更准确的结果callable(),因为typeobject->tp_call 槽的方法是不以任何其他方式使用。

只需使用 callable()


5
令人难以置信的说明了方法的陷阱hasattr(o, '__call__')以及为什么callable()(如果可以)更好的原因。
MestreLion

39

以下应返回布尔值:

callable(x)

1
这就解决了他的问题,但是他仍然创造了一个谜:如果x在buildin模块中属于'function'类,并且help(x .__ class__)描述了“ class function”,为什么“ function”显然是“ undefined”?

1
“功能”不是关键字或内置类型。函数的类型在“类型”模块中定义为“ types.FunctionType”
克里斯·B。2009年


19

callable(x) 如果可以在Python中调用传递的对象,但该函数返回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<type function> True作为输出。

isinstance可以很好地确定某物是否是一个函数(try isinstance(b, types.FunctionType));如果您真的想知道是否可以调用某些东西,可以使用hasattr(b, '__call__')也可以尝试一下。

test_as_func = True
try:
    b()
except TypeError:
    test_as_func = False
except:
    pass

当然,这不会告诉您它是否可以调用,但是TypeError在执行时会引发一个,还是一开始就不可调用。这可能对您来说并不重要。


8
称之为一个坏主意。如果它有副作用,或者实际上做了一些却花费很长时间,该怎么办?
asmeurer

@asmeurer-如果不调用它,为什么还需要知道它是否是一个函数?
2013年

1
@detly:对于调试,我通常希望将所有变量打印在一个对象中,这些方法通常对我没有用,所以我不想执行它们。最后,我只列出了每个具有相应值的不可调用属性:)
Wolph 2013年

2
仅仅因为没有调用它并不意味着它没有被调用。也许您正在调度。
asmeurer 2013年

4
使用异常来知道它是否可调用是一个很大的问题。如果有什么可调用的,但调用它会引发你正在寻找一个例外?你们都会默默地忽略一个错误,并且误诊是否可调用。当您使用EAFP时,您确实想避免尝试过多,但是在这种用例中没有办法做到这一点。

15

如果要检测语法上看起来像函数的所有内容:函数,方法,内置fun / meth,lambda ...,但要排除可调用对象(__call__定义了方法的对象),请尝试以下方法:

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

我将其与模块中的is*()检查代码进行了比较,inspect并且上面的表达式更加完整,尤其是当您的目标是过滤掉任何功能或检测对象的常规属性时。


感谢您指出我该types模块。我正在测试一个make_stemmer()工厂,该工厂有时会返回一个函数,有时会返回一个可调用的Stemmer实例,并且我需要检测差异。
滚刀


6

如果您已学习C++,则必须熟悉function objectfunctor,表示可以be called as if it is a function

在C ++中, an ordinary function是一个函数对象,一个函数指针也是如此;更一般而言,define的类的对象也是如此operator()。在C ++ 11和更高版本中the lambda expression也是functor如此。

相似,在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.

如果要判断天气,对象的作用就像一个函数,则callable建议的方法@John Feminella还可以。

如果要judge whether a object is just an ordinary function or not(不是可调用的类实例或lambda表达式),则xtypes.XXX建议使用by @Ryan是更好的选择。

然后,我使用这些代码进行实验:

#!/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

6

作为公认的答案,约翰·费米内拉说:

检查鸭子型物体属性的正确方法是询问它们是否发出嘎嘎声,而不是查看它们是否适合鸭子大小的容器。“直接比较”方法将对许多功能(例如内置函数)给出错误的答案。

即使有两个库严格区分功能,我还是绘制了一个详尽的可比较表:

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',
]

“鸭式打字”是通用的首选解决方案:

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

确定是否 builtin function

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

摘要

采用callable鸭式检查功能,如果您有进一步指定的需求,
请使用types.BuiltinFunctionType


5

函数只是带有__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比使用Python更有意义,那是有争议的if hasattr(x, '__call__'): x()。我想说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!

仅使用异常处理来防止意外行为,而不要使用逻辑流,这绝对不是Pythonic。
gotgenes

好吧,hasattr基本上在try / except块中进行getattr(尽管在C中)。blog.jancewicz.net/2007/10/reflection-hasattr.html
DBR

@dbr:但是hasattr更美观。
Nikhil Chelliah,2009年

5

这是另外两种方式:

def isFunction1(f) :
    return type(f) == type(lambda x: x);

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

这是我想到的第二种方法:

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

太好了!应该适用于所有版本的python2.x和python3.x!
Saurav Kumar,

4

'__call__'您可以检查用户定义的函数是否具有属性,等等func_name,而不是进行检查(这不是函数所独有的)func_doc。这不适用于方法。

>>> def x(): pass
... 
>>> hasattr(x, 'func_name')
True

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

>>> import inspect
>>> inspect.isfunction(x)
True

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


4

由于类也具有__call__方法,因此我建议另一个解决方案:

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

1
同意您的回答,约翰·费米内拉的回答hasattr(obj, '__call__')模棱两可。
GoingMyWay'9

4

请注意,Python类也是可调用的。

要获取功能(按功能,我们指的是标准功能和lambda),请使用:

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)

2

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


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

2

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

>>> import collections
>>> Test = collections.namedtuple('Test', [])
>>> callable(Test)
True
>>> hasattr(Test, '__call__')
True

检查对象是否是用户定义的函数(除了那个以外的任何东西)的一种正确方法是使用isfunction(.)

>>> import inspect
>>> inspect.isfunction(Test)
False
>>> def t(): pass
>>> inspect.isfunction(t)
True

如果需要检查其他类型,请查看一下检查—检查活动对象


2

精确功能检查器

callable是一个非常好的解决方案。但是,我想以相反的方式对待约翰·费米内拉。而不是像这样说:

检查鸭子型物体属性的正确方法是询问它们是否发出嘎嘎声,而不是查看它们是否适合鸭子大小的容器。“直接比较”方法将对许多功能(例如内置函数)给出错误的答案。

我们将这样处理:

检查某物是否是鸭子的正确方法不是通过若干过滤器查看它是否会发出嘎嘎声,而是通过多种过滤器查看它是否真的是鸭子,而不是仅仅从表面上检查它是否看起来像鸭子。

我们将如何实施

'types'模块具有大量用于检测功能的类,其中最有用的是type.FunctionType,但是还有很多其他类,例如方法类型,内置类型和lambda类型。我们还将“ functools.partial”对象视为函数。

我们检查所有类型是否简单的简单方法是使用isinstance条件。以前,我想创建一个继承自上述所有类的基类,但我无法做到这一点,因为Python不允许我们继承上述某些类。

下表列出了哪些类别可以对哪些功能进行分类:

功能表from kinght-金 以上功能表由kinght-金

起作用的代码

现在,这是完成我们上面描述的所有工作的代码。

from types import BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType
from functools import partial

def is_function(obj):
  return isinstance(obj, (BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType, partial))

#-------------------------------------------------

def my_func():
  pass

def add_both(x, y):
  return x + y

class a:
  def b(self):
    pass

check = [

is_function(lambda x: x + x),
is_function(my_func),
is_function(a.b),
is_function(partial),
is_function(partial(add_both, 2))

]

print(check)
>>> [True, True, True, False, True]

一个错误是is_function(partial),因为它是一个类,而不是一个函数,而这恰好是函数,而不是类。这是预览版,可让您试用其中的代码。

结论

如果希望通过对绝对值的鸭式输入来检查,callable(obj)是检查对象是否为函数的首选方法

我们的自定义is_function(obj),如果您不将可调用类实例算作一个函数,而是仅定义为内置函数或使用lambdadef,则可能需要进行一些编辑才能检查对象是否为函数的首选方法或部分

而且我认为这一切都包含在内。祝你有美好的一天!


1

在Python3中,我想出了type (f) == type (lambda x:x)产生Trueif f是否为函数的结果False。但是我想我更喜欢isinstance (f, types.FunctionType),感觉不太特别。我想做type (f) is function,但这不起作用。


0

在先前的答复之后,我想到了这一点:

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)

0

您可以尝试以下方法:

if obj.__class__.__name__ in ['function', 'builtin_function_or_method']:
    print('probably a function')

甚至更怪异的东西:

if "function" in lower(obj.__class__.__name__):
    print('probably a function')

-1

如果该值可调用,代码将继续执行调用,则只需执行调用并catch即可TypeError

def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")

4
这很危险;你不知道有什么副作用x
cwallenpoole

-2

以下是检查它的“替代方法”。它也适用于lambda。

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

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

-3

这对我有用:

str(type(a))=="<class 'function'>"

1
告诉我们结果是否为空字符串又是什么?对于函数,我得到"<type 'function'>",对于整数,我得到"<type 'int'>",所以我不知道它如何为您工作:/
pawamoy19年

现在仅适用于Python 3 :)还要根据问题的初衷,这将是不完整的:应该将内建open函数视为函数吗?str(type(open))<class 'builtin_function_or_method'>在Python 3
pawamoy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.