在编程语言中有两个很基础的概念,即方法(method)和函数(function)。如果达到了编程初级/入门级水平,那么你肯定在心中已有了初步的答案。

也许在你心中已有答案了

除去入参、返回值、匿名函数之类的正确的形式内容之外,你也许会说“函数就是定义在类外面的,而方法就是定义在类里面的,跟类绑定的”。

这种说法有没有问题呢?当然有!不然我就不会专门写这篇文章了,本文主要会来厘清这个问题。

在标准库inspect 中,它提供了两个自省的函数,即 ismethod() 和 isfunction(),可以用来判断什么是方法,什么是函数。

因此,本文想要先来研究一下这两个函数,看看 Python 在处理方法/函数的概念时,是怎么做的?

关于它们的用法,先看一个最简单的例子:

运行的结果分别是“True”和“False”,表明我们所定义的 test() 是一个函数,而不是一个方法。

这两个函数也可以用来检测自身,不难验证出它们都是一种函数:

那么,接下来的问题是:inspect 库的两个函数是什么工作原理呢?

先来看看 inspect 中的实现代码:

在源码中,我们看到了 isinstance() 函数,它主要用于判断一个对象(object)是否是某个类(class)的实例(instance)。

我们还看到了 types.FunctionTypetypes.MethodType ,它们指的就是目标类。继续点进去看源码:

# 摘自 types.py
def _f(): pass
FunctionType = type(_f) class _C:
def _m(self): pass
MethodType = type(_C()._m)

这里只是定义了两个空的 _f() 和 _m(),然后就使用了内置的 type() 函数。所以,我们完全可以把它们摘出来,看看庐山真面目:

梳理它们的关系,可以得到:

经过简化处理后,我们发现最关键的是两个问题:type() 函数如何判断出一个对象是 function 或 method 类?instance() 函数如何判断出一个对象是某个类的实例?

这两个内置函数都是用 C 语言实现的,这里我就不打算继续深究了……

但是,让我们再回头看看 inspect 中的注释,就会注意到一些端倪:

  • isfunction() 判断出的是用户定义的函数(user-defined function), 它拥有__doc__、__name__ 等等属性
  • ismethod() 判断出的是实例方法(instance method), 它拥有函数的一些属性,最特别的是还有一个 __self__ 属性

还是注释更管用啊,由此我们能得到如下的推论:

1、非用户定义的函数,即内置函数,在 isfunction() 眼里并不是“函数”(FunctionType)!

下面验证一下 len()、dir() 和 range():

事实上,它们有专属的类别(BuiltinFunctionType、BuiltinMethodType):

特别需要注意的是,内置函数都是builtin_function_or_method 类型,但是 range()、type()、list() 等看起来像是函数的,其实不然:

(PS:关于这点,我这篇文章 曾提到过,就不再展开了。)

2、一个类的静态方法,在 ismethod() 眼里并不是方法(MethodType)!

创建了类的实例后,再看看:

可以看出,除了 classmethod 之外,只有类实例的实例方法,才会被 ismethod() 判定为真!而静态方法,不管绑定在类还是实例上,都不算是“方法”!

有没有觉得很不可思议(或者有点理不清了)?

好了,回到本文开头的问题,我们最后来小结一下吧。

若以 inspect 库的两个函数为判断依据,则 Python 中的“方法与函数”具有一定的狭义性。在判断什么是函数时,它们并不把内置函数计算在内。同时,在判断什么是方法时,并非定义在类内部的都算,而是只有类方法及绑定了实例的实例方法才算是“方法”。

也许你会说,inspect 的两个判断函数并不足信,内置函数也应该算是“函数”,类里面的所有方法都应该算是“方法”。

我承认这种说法在广义上是可接受的,毕竟我们一直叫的就是“XX函数”、“XX方法”嘛。

但是,理论和广义概念只是方便人们的沟通理解,而代码实现才是本质的区别。也就是说,Python 在实际区别“方法与函数”时,并不是文中开头的简单说法,还有更多的细节值得关注。

看完本文,你有什么想法呢?欢迎一起交流。

学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?的更多相关文章

  1. 学点经济学:M0、M1、M2、M3,傻傻分不清?(转载)

    来源:http://t.10jqka.com.cn/pid_97006727.shtml 学点经济学:M0.M1.M2.M3,傻傻分不清? 25,508人浏览 2018-08-03 11:06 常听人 ...

  2. 2021年了,`IEnumerator`、`IEnumerable`还傻傻分不清楚?

    IEnumerator.IEnumerable这两个接口单词相近.含义相关,傻傻分不清楚. 入行多年,一直没有系统性梳理这对李逵李鬼. 最近本人在怼着why神的<其实吧,LRU也就那么回事> ...

  3. bind,apply,call,caller,callee还傻傻分不清楚?

    先介绍每个的语法: 1. bind() 语法:fn.bind(thisObj[, arg1[, arg2[, ...]]]) fn:是想要改变this指向的函数 thisObj:表示fn中this指针 ...

  4. JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文带你厘清个中曲直,给你个选择SpringDataJPA的理由!

    序言 Spring Data JPA作为Spring Data中对于关系型数据库支持的一种框架技术,属于ORM的一种,通过得当的使用,可以大大简化开发过程中对于数据操作的复杂度. 本文档隶属于< ...

  5. JS魔法堂:属性、特性,傻傻分不清楚

    一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...

  6. Java:接口和抽象类,傻傻分不清楚?

    01. 来看网络上对接口的一番解释: 接口(英文:Interface),在 Java 编程语言中是一个抽象类型,是抽象方法的集合.一个类通过继承接口的方式,从而来继承接口的抽象方法. 兄弟们,你们怎么 ...

  7. [转帖]十分钟快速理解DPI和PPI,不再傻傻分不清!

    十分钟快速理解DPI和PPI,不再傻傻分不清! https://baijiahao.baidu.com/s?id=1605834796518990333&wfr=spider&for= ...

  8. 【华为敏捷/DevOps实践】7. 敏捷,DevOps,傻傻不分清楚【华为云技术分享】

    文:姚冬(华为云DevCloud首席技术布道师,资深DevOps与精益/敏捷专家,金融解决方案技术Leader,中国DevOpsDays社区核心组织者) 前言 敏捷是什么?DevOps是什么?两者有什 ...

  9. 傻傻分不清之 Cookie、Session、Token、JWT

    傻傻分不清之 Cookie.Session.Token.JWT 什么是认证(Authentication) 通俗地讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打 ...

随机推荐

  1. Ubuntu系统下环境安装遇到依赖冲突问题

    问题场景:在ubuntu系统下使用docker拉了一个python3.6的镜像,要在该容器中安装vim结果总是报已安装某些依赖的版本不满足要求 解决方法: 1.安装aptitude apt-get i ...

  2. linux命令行界面如何安装图形化界面

    linux命令行界面如何安装图形化界面 目录 问题描述 解决方案 安装包 测试是否安装成功 如何卸载图形化界面 遭遇问题 问题描述 当我们在安装Linux系统时,我们一开始可能安装的是非图形界面的系统 ...

  3. AX2012 form displays unusually because of native resolution issues(由于本机高分辨率问题导致AX2012界面显示异常)

    Please tick the 'Disable display scaling on high DPI settings' and re-logiin AX,it will be OK. 当你遇到本 ...

  4. react-native app 屏幕适配方案(按照设计稿像素大小写就行)

    import React, { Component,PropTypes } from 'react'; import { Dimensions,PixelRatio,Platform,StatusBa ...

  5. burpsuit的安装和简单使用

    一.burpsuit的环境搭建 Burp Suite可以说是Web安全工具中的瑞士军刀,打算写几篇Blog以一个小白的角度去学习Burp Suite(简称BP),会详细地说一下的用法,说明一下每一个部 ...

  6. git常用命令学习配详细说明

    原文链接 把当前目录变成Git可以管理的仓库 git init 查看仓库当前的状态 git status 添加新/变动文件 git add <文件名> // 添加某个新文件(目录) git ...

  7. [BJDCTF 2nd]old-hack

    进入首页: 首页告诉了我们是thinkphp5的漏洞. 知道了是哪个版本的话就搜一搜喽:最后发现是thinkphp5.0.23的命令执行 payload_1:查看根目录文件,发现flag位置 http ...

  8. MS15-034漏洞复现、HTTP.SYS远程代码执行漏洞

    #每次命令都百度去找命令,直接弄到博客方便些: 漏洞描述: 在2015年4月安全补丁日,微软发布的众多安全更新中,修复了HTTP.sys中一处允许远程执行代码漏洞,编号为:CVE-2015-1635( ...

  9. Redis缓存设计与性能优化

    Redis我们一般是用作缓存,扛并发:或者用于某些特定的业务场景,比如前面说到redis各种数据类型的使用场景以及redis的哨兵和集群模式. 这里主要整理了下redis用作缓存,存在的一些问题,以及 ...

  10. 概率-拒绝采样 Rejection Sampling

    2018-12-09 16:40:30 一.使用Rand7()来生成Rand10() 问题描述: 问题求解: 这个问题字节跳动算法岗面试有问到类似的,有rand6,求rand8,我想了好久,最后给了一 ...