编程语言中有两个非常基础的概念,分别是方法(method)和函数(function)。如果你已经达到了初级/入门级的编程水平,那么你心里肯定有一个初步的答案。或许你心中已经有了答案。除了输入参数、返回值、匿名函数等正确的形式和内容外,你可能会说“函数定义在类外,方法定义在类内,类绑定”。这个说法有什么问题吗?当然有!否则,我不会专门写这篇文章。本文将主要阐明这个问题。在标准库inspect中,它提供了两个自省函数,分别是ismethod()和isfunction(),可以用来判断什么是方法,什么是函数。因此,本文想先研究这两个函数,看看Python是如何处理方法/函数这个概念的。关于它们的用法,我们先看一个最简单的例子:运行结果分别为“True”和“False”,说明我们定义的test()是函数,不是方法。这两个函数也可以用来检测自己,不难验证它们都是函数:那么,接下来的问题是:inspect库的两个函数是如何工作的呢?我们先来看看inspect中的实现代码:在源码中,我们看到了isinstance()函数,这个函数主要用来判断一个对象是否是某个类的实例。我们还看到了types.FunctionType和types.MethodType,它们引用了目标类。继续点进去看源码:#摘自types.pydef_f():passFunctionType=type(_f)class_C:def_m(self):passMethodType=type(_C()._m)这里只有两个空的_f分别定义了()和_m(),然后使用内置的type()函数。因此,我们可以把它们拿出来,看看庐山真面目:理清它们的关系后,我们可以得到:简化后,我们发现最关键的问题有两个:type()函数如何判断一个对象是否是函数还是方法类?instance()函数如何判断一个对象是某个类的实例呢?这两个内置函数是用C语言实现的,这里就不细说了。。。但是,让我们回头看看inspect中的注释,会发现一些端倪:isfunction()判断的是用户自定义函数(user-definedfunction),它有__doc__、__name__等属性ismethod()判断它是一个实例方法(instancemethod),它有函数的一些属性,最特别的是一个__self__属性或者注释比较有用,所以我们可以得到以下推论:1.非用户自定义函数,即内置函数,在isfunction()眼里不是“函数”(FunctionType)!我们来验证一下len()、dir()和range():其实它们都有自己的分类(BuiltinFunctionType、BuiltinMethodType):特别需要注意的是,内置函数都是builtin_function_or_method类型,而range()、type(),list()等看起来像函数,其实不是:(PS:关于这一点,我在这篇文章中有提到,就不展开了。)2.类的静态方法,inismethod()并不是眼中的方法(MethodType)!创建完类的实例后,再看:可以看出,除了classmethod,只有类实例的实例方法会被ismethod()判断为true!而静态方法,无论是绑定到类还是实例,都不是“方法”!你觉得不可思议(或者有点困惑)吗?好了,回到本文开头的问题,我们在最后做一个总结。如果以inspect库的两个函数作为判断依据的话,Python中的“方法和函数”就有了一定的狭义。在确定函数是什么时,他们不计算内置函数。同时,在判断什么是方法时,并不是所有在类内部定义的都算在内,只有类方法和实例绑定的实例方法才算是“方法”。也许你会说inspect的两个判断函数不可信,内置函数也应该算是“函数”,类中的所有方法都应该算是“方法”。我承认这种说法在广义上是可以接受的,毕竟我们一直以来所说的“XX函数”和“XX方法”。但是,理论和广义的概念只是为了方便人们的交流和理解,代码实现才是本质的区别。也就是说,Python真正区分“方法和函数”的时候,并不是文章开头的简单陈述,还有更多值得关注的细节。看完这篇文章,你有什么感想?欢迎一起交流。
