我有一个变量x,我想知道它是否指向一个函数。
我希望我能做一些像这样的事情:
>>> isinstance(x, function)
但这给了我:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'function' is not defined
我选这个是因为
>>> type(x)
<type 'function'>
我有一个变量x,我想知道它是否指向一个函数。
我希望我能做一些像这样的事情:
>>> isinstance(x, function)
但这给了我:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'function' is not defined
我选这个是因为
>>> type(x)
<type 'function'>
当前回答
在内置命名空间中没有构造函数的内置类型(例如函数、生成器、方法)在types模块中。你可以使用类型。isinstance调用中的函数类型:
>>> 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)
和随机的。Shuffle(技术上是一种隐藏随机的方法。随机实例):
>>> type(random.shuffle), isinstance(random.shuffle, types.FunctionType)
(<class 'method'>, False)
如果你在做一些特定类型的事情。FunctionType实例,如反编译字节码或检查闭包变量,使用类型。FunctionType,但如果你只是需要一个对象像函数一样可调用,请使用callable。
其他回答
由于类也有__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
结果
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 | √ | √ | × | × | × | × | × | √ | × | × | × | × | × | √ |
<lambda> | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
generator | √ | √ | √ | × | √ | × | × | √ | × | × | √ | × | √ | × |
async_func | √ | √ | √ | × | × | √ | × | √ | × | × | √ | × | √ | × |
async_generator | √ | √ | √ | × | × | × | √ | √ | × | × | √ | × | √ | × |
A | √ | √ | × | × | × | × | × | √ | × | × | × | × | × | × |
meth | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
classmeth | √ | √ | × | √ | × | × | × | √ | × | × | × | √ | × | × |
staticmeth | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
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 meth(self, a, b):
pass
@classmethod
def classmeth(cls, a, b):
pass
@staticmethod
def 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)
Time
选择三种最常见的方法:
可调用的(x) hasattr (x, __call__) isinstance (x, typing.Callable)
time/s | |
---|---|
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()的替代品,这又回到了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中的一个bug。也就是说, 没有比这更准确的结果了 结果比使用callable()本身,因为typeobject->tp_call 槽方法不能以任何其他方式访问。
只需使用callable()
作为公认的答案,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。
根据之前的回复,我想出了这个:
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)