我有一个变量 x
,我想知道它是否指向一个函数。
我曾希望我可以做类似的事情:
>>> isinstance(x, function)
但这给了我:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'function' is not defined
我选择它的原因是因为
>>> type(x)
<type 'function'>
insepct.getsource
,实际上,对象是否可调用并不重要,而是它是否可以为 type(obj)
提供“功能”。由于谷歌把我带到了这里,我想说 AsTeR 的评论是最有用的答案(对我来说)。互联网上还有很多其他地方可供人们发现 __call__
或 callable
。
如果这是针对 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
或 inspect.isfunction
方法(两者都做 the exact same thing)带有许多警告。它为非 Python 函数返回 False
。例如,大多数 builtin functions 是用 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
。
isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))
或检查 f.func
。
@foo
,我可以将它用作 @foo
和 @foo(some_parameter)
。然后它需要检查它被调用的是什么,例如要装饰的函数(第一种情况)或参数(第二种情况,它需要返回一个进一步的装饰器)。
types.BuiltinFunctionType
也是(“正常”)内置方法 的类型,如果您不采用 callable
路线,您可能不希望允许这种类型。
Since Python 2.1 您可以从 inspect
模块导入 isfunction
。
>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
open
和 hasattr
等内置函数,它似乎返回 False。
inspect.isfunction
文档字符串:“如果对象是用户定义的函数,则返回 true。”
isfunction
的 entire source code 是 return isinstance(object, types.FunctionType)
,因此它带有 @Ryan's answer 中指出的注意事项。
接受的答案是在被提供时被认为是正确的。事实证明,没有替代可以替代 Python 3.2 中的 callable()
:具体来说,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()
hasattr(o, '__call__')
方法的缺陷以及为什么 callable()
(如果可用)更胜一筹的惊人说明。
以下应返回一个布尔值:
callable(x)
Python 的 2to3 工具 (http://docs.python.org/dev/library/2to3.html) 建议:
import collections
isinstance(obj, collections.Callable)
似乎因为 http://bugs.python.org/issue7006 而选择了此方法而不是 hasattr(x, '__call__')
方法。
callable()
:bugs.python.org/issue10518#msg122309
结果
callable(x) hasattr(x, '__call__') inspect.isfunction(x) inspect.ismethod(x) inspect.isgeneratorfunction(x) inspect.iscoroutinefunction(x) inspect.isasyncgenfunction(x) isinstance(x, typing.Callable) isinstance(x, types.BuiltinFunctionType) isinstance(x, types.BuiltinMethodType) isinstance(x, types.FunctionType) isinstance(x, types.MethodType) isinstance(x, types.LambdaType) isinstance(x, functools.partial) 打印 √ √ × × × × × √ √ √ × × × × func √ √ √ × × × × √ × × √ × √ × functools.partial √ √ × × × × × √ × × × × √
import types
import inspect
import functools
import 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 1
yield 2
async def async_func():
pass
async def async_generator():
yield 1
class A:
def __call__(self, a, b):
pass
def func1(self, a, b):
pass
@classmethod
def func2(cls, a, b):
pass
@staticmethod
def func3(a, b):
pass
for func in [print,
func,
partial,
_lambda,
generator,
async_func,
async_generator,
A,
A.func1,
A.func2,
A.func3]:
judge(func)
时间
挑选最常用的三种方法:
可调用(x)
hasattr(x, '__call__')
isinstance(x,打字。可调用)
时间/秒 callable(x) 0.86 hasattr(x, '__call__') 1.36 isinstance(x, typing.Callable) 12.19
import typing
from 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
callable(x)
如果传递的对象可以在 Python 中调用,但函数在 Python 3.0 中不存在,则 将 返回 true,并且正确地说不会区分:
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 = True
try:
b()
except TypeError:
test_as_func = False
except:
pass
当然,这不会告诉您它是否可调用但在执行时会抛出 TypeError
,或者一开始就不可调用。这对你来说可能无关紧要。
如果您想检测语法上看起来像函数的所有内容:函数、方法、内置 fun/meth、lambda ... 但 排除 可调用对象(定义了 __call__
方法的对象),然后试试这个:
import types
isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType, types.UnboundMethodType))
我将此与 inspect
模块中的 is*()
检查代码进行了比较,上面的表达式更加完整,特别是如果您的目标是过滤掉任何函数或检测对象的常规属性。
types
模块。我正在测试一个 make_stemmer()
工厂,它有时会返回一个函数,有时会返回一个可调用的 Stemmer
实例,我需要检测其中的差异。
尝试使用 callable(x)
。
如果您学过 C++
,则必须熟悉 function object
或 functor
,即任何可以 be called as if it is a function
的对象。
在 C++ 中,an ordinary function
是函数对象,函数指针也是;更一般地说,定义 operator()
的类的对象也是如此。在 C++11 及更高版本中,the lambda expression
也是 functor
。
同样,在 Python 中,那些 functors
都是 callable
。 An 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 建议的可调用方法就可以了。如果要判断一个对象是否只是一个普通函数(不是可调用的类实例,也不是 lambda 表达式),那么@Ryan 建议的 xtypes.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>
"""
我使用数据绘制了一张可调用函子类型的表格。
https://i.stack.imgur.com/W3Hvi.png
然后你可以选择合适的函子类型。
如:
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
作为公认的答案,John Feminella 表示:
检查鸭子类型对象属性的正确方法是询问它们是否嘎嘎,而不是查看它们是否适合鸭子大小的容器。 “直接比较”的方法会为许多函数给出错误的答案,比如内置函数。
尽管有两个库可以严格区分函数,但我还是绘制了一个详尽的可比较表:
8.9. types — Dynamic type creation and names for built-in types — Python 3.7.0 documentation
30.13. inspect — Inspect live objects — Python 3.7.0 documentation
#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
。
以下是其他几种方法:
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!
精确的功能检查器
callable 是一个很好的解决方案。然而,我想以与 John Feminella 相反的方式来对待这个问题。而不是这样对待它:
检查鸭子类型对象属性的正确方法是询问它们是否嘎嘎,而不是查看它们是否适合鸭子大小的容器。 “直接比较”的方法会为许多函数给出错误的答案,比如内置函数。
我们会这样对待它:
判断一个东西是不是鸭子的正确方法不是看它会不会嘎嘎叫,而是通过几个过滤器来看看它是否真的是鸭子,而不是仅仅从表面上看它是否像鸭子。
我们将如何实现它
'types' 模块有很多类来检测函数,最有用的是 types.FunctionType,但也有很多其他的,比如方法类型、内置类型和 lambda 类型。我们还将把 'functools.partial' 对象视为一个函数。
我们检查它是否是一个函数的简单方法是对所有这些类型使用 isinstance 条件。以前,我想创建一个从上述所有类继承的基类,但我无法做到,因为 Python 不允许我们从上述某些类继承。
下表列出了哪些类可以对哪些功能进行分类:
https://i.stack.imgur.com/W3Hvi.png
执行它的代码
现在,这是完成我们上面描述的所有工作的代码。
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),因为那是一个类,而不是一个函数,而这正是函数,而不是类。这里有一个 preview 供您试用代码。
结论
callable(obj) 是检查对象是否为函数的首选方法,如果您想通过鸭式键入绝对值。
我们的自定义 is_function(obj),如果您不将可调用类实例计数为函数,但仅将函数定义为内置或使用 lambda、def ,或部分。
我认为这一切都结束了。祝你有美好的一天!
函数只是具有 __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 比 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!
您可以检查用户定义的函数是否具有属性 func_name
、func_doc
等,而不是检查 '__call__'
(这不是函数独有的)。这不适用于方法。
>>> def x(): pass
...
>>> hasattr(x, 'func_name')
True
另一种检查方法是使用 inspect
模块中的 isfunction()
方法。
>>> import inspect
>>> inspect.isfunction(x)
True
要检查对象是否为方法,请使用 inspect.ismethod()
由于类也有 __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
hasattr(obj, '__call__')
含糊不清。
请注意,Python 类也是可调用的。
要获取函数(我们所说的函数是指标准函数和 lambdas),请使用:
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)
无论函数是类,因此您可以获取实例 x 的类的名称并进行比较:
if(x.__class__.__name__ == 'function'):
print "it's a function"
一些答案中提到的使用 hasattr(obj, '__call__')
和 callable(.)
的解决方案有一个主要缺点:对于具有 __call__()
方法的类和类的实例,两者都返回 True
。例如。
>>> 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
如果您需要检查其他类型,请查看 inspect — Inspect live objects。
在 Python3 中,我提出了 type (f) == type (lambda x:x)
,如果 f
是函数,则产生 True
,如果不是,则产生 False
。但我认为我更喜欢 isinstance (f, types.FunctionType)
,它感觉不那么特别。我想做type (f) is function
,但这不起作用。
你可以试试这个:
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 的答案,如果您的动机只是为了在控制台中获取可供您使用的内置函数列表,那么这两个选项有效:
[i for i, j in __builtin__.__dict__.items() if j.__class__.__name__ in ['function', 'builtin_function_or_method']] [i for i, j in __builtin__.__dict__.items() if str(j) [:18] == '<内置函数']
根据之前的回复,我想出了这个:
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)
如果值是可调用的,代码将继续执行调用,则只需执行调用并捕获 TypeError
。
def myfunc(x):
try:
x()
except TypeError:
raise Exception("Not callable")
x
有什么副作用。
以下是检查它的“repr方式”。它也适用于 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
这对我有用:
str(type(a))=="<class 'function'>"
"<type 'function'>"
,对于一个整数,我得到 "<type 'int'>"
,所以我看不出它是如何为你工作的:/
open
是否应该被视为一个函数? str(type(open))
在 Python 3 中给出 <class 'builtin_function_or_method'>
。
您可以DIY一个简短的函数来检查输入是否不是字符串并将输入转换为字符串将返回匹配的名称定义:
def isFunction(o):return not isinstance(o,str) and str(o)[:3]=='<fu';
我认为这段代码已经兼容所有 python 版本。
或者,如果发生变化,您可以添加额外的小写转换并检查内容长度。我看到的函数的格式字符串是“
不定期副业成功案例分享