如何在Python中确定一个变量是否为函数?

11

在Python中,由于函数是一种值,我该如何确定变量是否为函数呢?

例如:

boda = len   # boda is the length function now
if ?is_var_function(boda)?:
  print "Boda is a function!"
else:
  print "Boda is not a function!"

这里假设的?is_var_function(x)?应该在x是可调用函数时返回true,在x不是可调用函数时返回false。


你想让它对类也返回true吗?因为它们也是可调用的。 - Javier
1
什么阻止你阅读代码? - S.Lott
3
是的,但有时确实需要使用反射 - 例如,MVC框架可能需要加载一个类并检查其成员。但这绝对是日常代码应该避免的。 - Justin Ethier
感谢您提供的精彩答案!我实际上是想确定这个东西是否可调用,而不仅仅是一个函数。很抱歉我没有准确到每个字,我还是 Python 新手。 - bodacydo
8个回答

18

其他答案提到的callable内置函数并不能回答你的问题,因为它不仅对函数返回True,也会对定义了__call__方法的方法、类和类的实例对象返回True。如果你的问题标题和内容有误,而你只关心一个对象是否可以被调用,那么可以使用该内置函数。但是,对于你所提出的问题,最好的答案是:导入Python标准库中的inspect模块,并使用inspect.isfunction函数。(虽然还有其他更低级别的方式,但始终建议在可能使用inspect模块进行内省时使用,以优先使用更高级的方法:inspect有助于保持代码简洁、清晰、健壮和未来可扩展性)。


1
又有一个错误答案被接受在SO上了。我应该开始列一个清单。这对于内置函数和类方法是失败的,正如我在其他几个答案中提到的那样。你还需要检查inspect.isbuiltin()和inspect.ismethod(),而且拥有那么多特殊情况并不能保证“未来的可靠性”。 - Glenn Maynard
为什么不使用 type(a) == types.functionType 呢? - AsTeR

14
您可以使用inspect.isfunction(object)。参见:docs.python.org 话虽如此,在您的日常代码中应避免使用这种技术。有时您确实需要使用反射-例如,MVC框架可能需要加载一个类并检查其成员。但通常您应该返回/传递/处理具有相同“接口”的对象。例如,不要返回可能是整数或函数的对象-始终返回相同类型的对象,以使您的代码保持一致。

1
它提供了如何做一些通常不好的事情的建议,但没有提供如何构建良好、强大、符合习惯用法的Python代码的上下文。 - Mike Graham
3
这也恰好回答了问题,这是本网站的目的。 - harto
2
帮助人们、提供良好建议和为更好的程序做出贡献,都是比回答问题更高尚的目标。 - Mike Graham
2
我希望该网站的目的不是盲目回答问题,即使很可能该人提出的问题是错误的。无论如何回答问题都可以(他确实可能想要他所说的东西,并且提出一个问题并且每个人都拒绝回答它会让人发疯——这确实会发生),但是一个好的答案应该跟进解释为什么这可能不是他想要的。 - Glenn Maynard
2
网站哲学撇开不谈,这个答案是错误的。inspect.isfunction(sys.exit)和inspect.isfunction((1.0).hex)都为false(内置函数和绑定方法)。 - Glenn Maynard
显示剩余2条评论

9

Python中有一个可调用函数callable

 if callable(boda):

一个函数/方法是每个实现了__call__方法的对象。callable()是测试这种调用方法是否存在的好方法。 - tux21b
但是callable也会对类返回true,不是吗?不清楚OP是否也想要这个。 - Javier
1
我怀疑 OP 不知道区别,实际上想要任何可调用的东西。如果你不知道如何在 Python 中检查某个东西是否为函数,那么很有可能你实际上并不想这样做。 - Glenn Maynard

4

使用callable(boda)来判断boda是否可调用。

这里的“可调用”指的是函数、方法或者类。但是,如果你只想区分变量和函数,那么这个方法就可以很好地发挥作用。

你的代码将会是这样的:

boda = len   # boda is the length function now
if callable(boda):
  print "Boda is a function!"
else:
  print "Boda is not a function!"

2
>>> import types
>>> def f(): pass
...
>>> x = 0
>>> type(f) == types.FunctionType
True
>>> type(x) == types.FunctionType
False

这将检查它是否为函数。 callable() 将检查它是否可调用(也就是说,它具有一个 __call__ 方法),因此它将在类、函数或方法上返回 true。


通常情况下,isinstance 用于类型检查。对于检查单例,通常优先使用 is 而不是 ==。要求一个函数而不是像函数一样的其他东西是一个不好的策略。 - Mike Graham
1
type(sys.exit) == types.FunctionTypetype((1.0).hex) == types.FunctionType 都是错误的。(即使使用 isinstance 仍然失败;我不知道为什么 BuiltinFunctionType 和 MethodType 不是 FunctionType 的子类。) - Glenn Maynard

1

完美的解决方案当然是没有任何东西。 - Mike Graham

0
Python程序中如何使用不同对象的哲学被称为“鸭子类型”——如果它看起来像一只鸭子,叫起来像一只鸭子,走起来像一只鸭子,那么它就是一只鸭子。对象不是按其类型分组,而是按其能够做什么来分组,这甚至还包括函数。编写Python程序时,您应该始终知道所有对象的功能并在使用它们时不进行检查。
例如,我可以定义一个函数:
def add_three(a, b c):
    return a + b + c

并且意味着它可以与三个浮点数一起使用。但是通过不进行检查,我有一个更加有用的函数——我可以将其与整数、decimal.Decimals或fractions.Fractions一起使用,例如。

同样适用于拥有一个函数。如果我知道我有一个函数并想要调用它,我应该直接调用它。也许我拥有的是一个函数,也许我拥有一个不同的可调用对象(比如绑定方法或定义了__call__的任意类的实例),它可能同样好用。通过不进行任何检查,我的代码能够处理各种我事先甚至没有考虑到的情况。

对于可调用对象,我可以相当可靠地确定是否拥有一个,但出于代码简洁性的考虑,我不应该这么做。如果有人传入的不是可调用对象,在调用时会出现错误。如果我正在编写接受一个可能是可调用对象的参数,并根据它执行不同操作的代码,那么我应该通过定义两个函数来改进我的API,以执行这两个不同的操作。

如果您有一种方式来处理调用方传递的非函数式内容(而这不仅仅是疯狂API的结果),正确的解决方案是捕获TypeError,当您尝试调用无法调用的内容时引发。通常,最好尝试做某事,并在失败时恢复,而不是事先进行检查。(想起陈词滥调:“请求宽恕比请求许可容易。”)提前检查可能会基于逻辑中微妙错误导致意外问题,还可能会导致竞态条件。

您为什么认为需要类型检查?


0

对于一个像值属性一样访问但实际上调用函数的属性,它应该返回什么呢?你这个看似简单的问题实际上在Python中引发了一系列的问题。这意味着像callableisfunction这样的东西对于你的学习和内省是有好处的,但在与其他代码进行接口时可能不是你想依赖的东西。

顺带一提:关于Python的构建方式,请参考Turtles All The Way Down演示。


1
一个属性是一个奇怪的例子。属性的行为方式与函数不太相似,更重要的是,在正常情况下,您不会访问实际的属性对象,而是访问它为您获取的对象。 - Mike Graham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接