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

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

我希望我能做一些类似的事情:

>>> isinstance(x, function)

但这给了我:

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

我选这个是因为

>>> type(x)<type 'function'>
365558 次浏览

以下应该返回一个布尔值:

callable(x)

尝试使用callable(x)

如果这适用于Python 2. x或Python 3.2+,您可以使用callable()。它曾经被弃用,但现在未弃用,因此您可以再次使用它。您可以在此处阅读讨论:http://bugs.python.org/issue10518。您可以使用:

callable(obj)

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

hasattr(obj, '__call__')

经常建议的types.FunctionTypesinspect.isfunction方法(都做完全一样的东西)带有许多警告。对于非Python函数,它返回False。例如,大多数内建函数是用C而不是Python实现的,所以它们返回False

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

所以types.FunctionType可能会给你令人惊讶的结果。检查鸭子类型对象属性的正确方法是询问它们是否嘎嘎叫,而不是查看它们是否适合鸭子大小的容器。

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

>>> 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)如果传递的对象可以在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可以很好地确定某个东西是否是函数(尝试isinstance(b, types.FunctionType));如果你真的有兴趣知道某个东西是否可以调用,你可以使用hasattr(b, '__call__')或者只是尝试一下。

test_as_func = Truetry:b()except TypeError:test_as_func = Falseexcept:pass

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

函数只是一个带有__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/除了比if hasattr(x, '__call__'): x()更Python'y…我会说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!

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

import collectionsisinstance(obj, collections.Callable)

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

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

从Python 2.1开始您可以从inspect模块导入isfunction

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

如果你想检测语法上看起来像函数的所有东西:函数、方法、内置的乐趣/meth、lambda……但是排除可调用对象(定义了__call__方法的对象),那么试试这个:

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

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

根据之前的回复,我想到了这个:

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)

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

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

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

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

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

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

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

接受的答案是在当时它被认为是正确的。因为它事实证明,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__

>>> 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中的bug。也就是说,真的没有办法可以产生比可以更准确的结果结果比使用callable()本身,因为typeobject->tp_call插槽方法无法以任何其他方式访问。

使用callable()

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

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

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

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

检查对象是否是用户定义函数(除了that之外什么都不是)的一种正确方法是使用isfunction(.)

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

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

这里有几个其他的方法:

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!

由于类也有__call__方法,我推荐另一种解决方案:

class A(object):def __init__(self):passdef __call__(self):print 'I am a Class'
MyClass = A()
def foo():pass
print hasattr(foo.__class__, 'func_name') # Returns Trueprint hasattr(A.__class__, 'func_name')   # Returns False as expected
print hasattr(foo, '__call__') # Returns Trueprint hasattr(A, '__call__')   # (!) Returns True while it is not a function

请注意,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)

如果你学过C++,你必须熟悉function objectfunctor,意味着任何可以be called as if it is a function的对象。

在C++,an ordinary function是一个函数对象,函数指针也是;更一般地说,定义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.

如果你想判断对象是否像函数一样工作,那么@John Feminella建议的callable方法就可以了。

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

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

#!/usr/bin/python3# 2017.12.10 14:25:01 CST# 2017.12.10 15:54:19 CST
import functoolsimport typesimport pprint

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

class A():def __call__(self, a,b):print(a,b)def func1(self, a, b):print("[classfunction]:", a, b)@classmethoddef func2(cls, a,b):print("[classmethod]:", a, b)@staticmethoddef func3(a,b):print("[staticmethod]:", a, b)
def func(a,b):print("[function]", a,b)

定义函子:

#(1.1) built-in functionbuiltins_func = open#(1.2) ordinary functionordinary_func = func#(1.3) lambda expressionlambda_func  = lambda a : func(a,4)#(1.4) functools.partialpartial_func = functools.partial(func, b=4)
#(2.1) callable class instanceclass_callable_instance = A()#(2.2) ordinary class functionclass_ordinary_func = A.func1#(2.3) bound class methodclass_bound_method = A.func2#(2.4) static class methodclass_static_func = A.func3

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

## list of functorsxfuncs = [builtins_func, ordinary_func, lambda_func, partial_func, class_callable_instance, class_ordinary_func, class_bound_method, class_static_func]## list of typextypes = [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 resultprint("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

以下是检查它的“repr方式”。它也适用于lambda。

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

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

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

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

8.9。类型-动态类型创建和内置类型的名称-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]: TrueIn [27]: callable(detect_function)Out[27]: True

至于内置功能

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

当再走一步检查是否内置函数或用户定义的函数时

#check inspect.isfunction and type.FunctionTypeIn [46]: inspect.isfunction(detect_function)Out[46]: TrueIn [47]: inspect.isfunction(hasattr)Out[47]: FalseIn [48]: isinstance(detect_function, types.FunctionType)Out[48]: TrueIn [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]: TrueIn [51]: isinstance(detect_function, types.BuiltinFunctionType)Out[51]: False

总结

使用callable来鸭子类型检查函数,
如果您有进一步指定的需求,请使用types.BuiltinFunctionType

这对我有用:

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

精确函数检查器

可调用是一个非常好的解决方案。然而,我想用与约翰·费米内拉相反的方式来处理这个问题。而不是像这样对待它:

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

我们将像这样对待它:

检查某物是否是鸭子的正确方法不是看它是否会嘎嘎叫,而是通过几个过滤器看看它是否真的是鸭子,而不是仅仅检查它是否从表面看起来像鸭子。

我们将如何实施它

“类型”模块有很多类来检测函数,最有用的是函数类型,但也有很多其他类,如方法类型、内置类型和lambda类型。我们还将“functools.partial”对象视为函数。

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

下面是一个表,列出了哪些类可以对哪些函数进行分类:

函数表从kinght-金上面的函数表由kinght-金

做这件事的代码

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

from types import BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaTypefrom 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(部分),因为那是一个类,而不是函数,这正是函数,而不是类。这是一个预览供您尝试代码。

结论

可调用的是检查对象是否是函数的首选方法,如果您想通过鸭子打字而不是绝对

我们的自定义is_function,可能带有一些编辑是检查对象是否为函数的首选方法,如果您没有将任何可调用类实例计算为函数,而是仅定义内置lambdadef部分的函数。

我想一切都结束了。祝你有美好的一天!

你可以试试这个:

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')

结合@Sumukh Barve、@Katsu和@tinnick的答案,如果您的动机只是获取可供您在控制台中使用的内置函数列表,则这两个选项有效:

  1. [i for i, j in __builtin__.__dict__.items() if j.__class__.__name__ in ['function', 'builtin_function_or_method']]
  2. [i for i, j in __builtin__.__dict__.items() if str(j)[:18] == '<built-in function']

结果

可调用的(x)Hasattr(x,'__call__')inspect.isfunction(x)inspect.ismethod(x)inspect.isgeneratorfunction(x)inspect.iscoroutinefunction(x)inspect.isasyncgenfunction(x)is实例(x, typing. Callable)获取实例类型获取实例类型实例类型获取实例类型实例类型ISASIAL(x,functools.partial)
打印×××××××××
func××××××××
functools.partial××××××××××
×××××
××××××
async_func×××××××
async_generator×××××××
一个×××××××
×××××××
经典×××××××××
苯丙胺××××××××
import typesimport inspectimport functoolsimport typing

def judge(x):name = x.__name__ if hasattr(x, '__name__') else 'functools.partial'print(name)print('\ttype({})={}'.format(name, type(x)))print('\tcallable({})={}'.format(name, callable(x)))print('\thasattr({}, \'__call__\')={}'.format(name, hasattr(x, '__call__')))print()print('\tinspect.isfunction({})={}'.format(name, inspect.isfunction(x)))print('\tinspect.ismethod({})={}'.format(name, inspect.ismethod(x)))print('\tinspect.isgeneratorfunction({})={}'.format(name, inspect.isgeneratorfunction(x)))print('\tinspect.iscoroutinefunction({})={}'.format(name, inspect.iscoroutinefunction(x)))print('\tinspect.isasyncgenfunction({})={}'.format(name, inspect.isasyncgenfunction(x)))print()print('\tisinstance({}, typing.Callable)={}'.format(name, isinstance(x, typing.Callable)))print('\tisinstance({}, types.BuiltinFunctionType)={}'.format(name, isinstance(x, types.BuiltinFunctionType)))print('\tisinstance({}, types.BuiltinMethodType)={}'.format(name, isinstance(x, types.BuiltinMethodType)))print('\tisinstance({}, types.FunctionType)={}'.format(name, isinstance(x, types.FunctionType)))print('\tisinstance({}, types.MethodType)={}'.format(name, isinstance(x, types.MethodType)))print('\tisinstance({}, types.LambdaType)={}'.format(name, isinstance(x, types.LambdaType)))print('\tisinstance({}, functools.partial)={}'.format(name, isinstance(x, functools.partial)))

def func(a, b):pass

partial = functools.partial(func, a=1)
_lambda = lambda _: _

def generator():yield 1yield 2

async def async_func():pass

async def async_generator():yield 1

class A:def __call__(self, a, b):pass
def meth(self, a, b):pass
@classmethoddef classmeth(cls, a, b):pass
@staticmethoddef staticmeth(a, b):pass

for func in [print,func,partial,_lambda,generator,async_func,async_generator,A,A.meth,A.classmeth,A.staticmeth]:judge(func)

时间

选择三种最常见的方法:

时间
可调用的(x)0.86
Hasattr(x,'__call__')1.36
is实例(x, typing. Callable)12.19
import typingfrom timeit import timeit

def x():pass

def f1():return callable(x)

def f2():return hasattr(x, '__call__')

def f3():return isinstance(x, typing.Callable)

print(timeit(f1, number=10000000))print(timeit(f2, number=10000000))print(timeit(f3, number=10000000))# 0.8643081# 1.3563508# 12.193492500000001

您可以DIY一个短函数来检查输入是否不是字符串,并将输入转换为字符串将返回匹配的名称定义:

def isFunction(o):return not isinstance(o,str) and str(o)[:3]=='<fu';

我认为这段代码已经兼容所有python版本。

或者如果有变化,您可以添加额外的转换为小写并检查内容长度。我看到的函数转换的格式字符串是“

这是我的代码:

# -*- coding: utf-8 -*-import hashlibimport inspect
# calc everything to md5!!def count_md5(content):if isinstance(content, dict):return count_md5([(str(k), count_md5(content[k])) for k in sorted(content.keys())],)elif isinstance(content, (list, tuple)):content = [count_md5(k) for k in content]elif callable(content):return make_callable_hash(content)return calc_md5(str(content))

def calc_md5(content):m2 = hashlib.md5()if isinstance(content, str):m2.update(content.encode("utf8"))else:m2.update(content)return m2.hexdigest()

def make_callable_hash(content):if inspect.isclass(content):h = []for attr in [i for i in sorted(dir(content)) if not i.startswith("__")]:v = getattr(content, attr)h.append(count_md5(v))
return calc_md5("".join(h))
return calc_md5(content.__name__)

对于可调用,大多数时候我们只是想看看属性的值是否一致,所以我们可以取callable的所有属性并对其进行评估。“可调用”将返回true,如果它是一个类,所以它不是很严格

使用是实例()type(),它们都是Python中的内置函数,您可以检查它是否是一个函数,因此您不需要导入任何东西:

def test():pass
print(isinstance(test, type(test)))

输出:

True