京公网安备 11010802034615号
经营许可证编号:京B2-20210330
作者:豌豆花下猫
来源:Python猫
花下猫语:前两天,我偶然在一个知识星球(刘欣老师的“码农翻身”)里看到一篇主题,刘老师表示 python 的类方法非要带个 self,而不像其它语言那样隐藏起来,这让人很不爽。我对此也有同感。在经过群聊讨论后,我获知 Guido 曾经专门撰文解释过这个问题。这篇文章并不好懂,我抽空先翻译出来了,看看能收到什么回应。如果可能的话,后续再另写文章分析。
布鲁斯·埃克尔(Bruce Eckel)发了篇博文[1],提议从类方法的形参列表中删除“self”。我将解释为什么这个提议不能通过。(译注:Bruce 是《Thinking in Java》、《Thinking in C++》等多本书籍的作者,也是个 Python 开发者。他的文章总结了当年在巴西 Pycon 上的一次讨论,主要观点是在定义类方法时,形参中的“self”是多余的,而且由它引发的报错信息具有一定的误导性。)
Bruce 的提议
Bruce 知道,我们需要一种方法来区分对实例变量的引用和对其它变量的引用,因此他建议将“self”设为关键字。
考虑一种典型的类,它有一个方法,例如:
class C:
def meth(self, arg):
self.val = arg
return self.val
跟据Bruce 的提议,这将变为:
class C:
def meth(arg): # Look ma, no self!
self.val = arg
return self.val
这样每个方法会节省 6 个字符。但我不觉得 Bruce 提出这个建议是为了减少打字。
我认为他真正关心的是程序员(可能来自其它语言)所浪费的时间,有时候似乎不需要指定“self”参数,而且他们偶尔忘记了要加(即使他们十分清楚——习惯是一种强大的力量)。确实,与忘记在实例变量或方法引用之前键入“self.”相比,从参数列表中省略“self”,往往会导致很模糊的错误消息。
也许更糟糕的是(如 Bruce 所述),当正确地声明了方法,但是在调用时的参数数量不对,这时收到的错误消息。如 Bruce 给出的以下示例:
Traceback (most recent call last): File "classes.py", line 9, in obj.m2(1) TypeError: m2() takes exactly 3 arguments (2 given)
我赞同它是令人困惑的,但是我宁愿去解决此错误消息,而不是修改语言。
为什么 Bruce 的提议不可行
首先,让我提出一些与 Bruce 的提议相反的典型论点。
这有一个很好的论据可以证明,在参数列表中使用显式的“self”,可以增强以下两种调用方法在理论上的等效性。假设“ foo”是“C”的一个实例:
foo.meth(arg) == C.meth(foo, arg)
(译注:说实话,我没有理解这个例子的意思。以下仅是个人看法。在类的内部定义方法时,可能会产生几种不同的方法:实例方法、类方法和 静态方法。它们的作用和行为是不同的,那么在定义和调用时怎么做区分呢?Python 约定了一种方式,即在定义时用第一个参数作区分:self 表示实例方法、cls或其它符号 表示类方法……三种方法都可以被类的实例调用,而且看起来一模一样,如上例的等号左侧那样。这时候就要靠定义时赋予的参数来区分了,像上例等号右侧,第一个参数是实例对象,表明此处是个实例方法。)
另一个论据是,在参数列表中使用显式的“self”,将一个函数插入一个类,获得动态地修改一个类的能力,创建出相应的一个类方法。
例如,我们可以创建一个与上面的“C”完全等效的类,如下所示:
# Define an empty class: class C: pass # Define a global function: def meth(myself, arg): myself.val = arg return myself.val # Poke the method into the class: C.meth = meth
请注意,我将“self”参数重命名为“myself”,以强调(在语法上)我们不是在此处定义一个方法(译注:类外部的是函数,即 function,类内部的是方法,即 method)。
这样之后,C 的实例就具有了一个“meth”方法,该方法有一个参数,且功能跟之前的完全一样。对于在把方法插入类之前就创建的那些 C 的实例,它甚至也适用。
我想 Bruce 并不特别在意前述的等效性。我同意这只是理论上的重要。我能想到的唯一例外是旧式的调用超级方法的习语(idiom)。但是,这个习语很容易出错(正是由于需要显式地传递"self"的原因),这就是为什么在 Python 3000中,我建议在所有情况下都使用"super()"的原因。
Bruce 可能会想到一种使第二个等效例子起作用的方法——在某些情况下,这种等效性真的很重要。我不知道 Bruce 花了多少时间思考如何实现他的提议,但是我想他正在考虑将一个名为“self”的额外形参自动地添加到直接地在类内部定义的所有方法的思路(我必须说是“直接地”,以便那些嵌套在方法内部的函数,能免于这种自动操作)。这样,可以使第一个等效例子保持等效。
但是,有一种情况我认为 Bruce 不能在不向编译器中添加某种 ESP 的情况下解决:装饰器。我相信这是 Bruce 的提议的最终败笔。
当装饰一个方法时,我们不知道是否要自动地给它加一个“self”参数:装饰器可以将函数变成一个静态方法(没有“self”)或一个类方法(有一个有趣的 self,它指向一个类而不是一个实例),或者可以做一些完全不同的事情(用纯 Python 实现“ @classmethod”或“ @staticmethod”的装饰器是繁琐的)。除非知道装饰器的用途,否则没有其它办法来确定是否要赋予正在定义的方法一个隐式的“self”参数。
我拒绝诸如特殊包装的“@classmethod”和“@staticmethod”之类的黑科技。我也认为除了自检外,自动地确定某个方法是类方法(class method)、实例方法(instance method)还是静态方法(static method),这不是一个好主意(就像在 Bruce 的文章的评论中,有人建议的那样):这使得很难仅仅根据方法前的“def”,来决定应该怎样调用该方法。
(译注:对于一个方法,在当前的添加了相应参数的情况下,可以简单地加装饰器,区分它是哪种方法,调用时也容易区分调用;但是,如果没有加参数,即使可以用神奇的自动机制来区分出它是哪种方法,但在调用时,你不好确定该怎么调用)。
在评论中,我看到了一些非常极端的对 Bruce 的提议的附和,但通常的代价是使得规则难以遵循,或者要求对语言进行更深层的修改,这令我们极其难以接受它,特别是合入 Python 3.1。顺便说一句,对于 3.1,再次声明我们的规则,新特性只有在保持向后兼容的情况下才是可接受的。
有一个似乎可行的建议(可以使它向后兼容)是把类中的
def foo(self, arg): ...
改成这样的语法糖:
def self.foo(arg): ...
但我不认同它把“self”变为保留字(reserved word),或者要求前缀必须是“self”。如果这样做了,那对于类方法,很容易也出现这种情况:
@classmethod def cls.foo(arg): ...
好了,相比于现状,我并没有更喜欢这个。但是相比于 Bruce 的提议或在他的博客评论区中提出的更极端的说法,我认为这个要好得多,而且它具有向后兼容的巨大优势,并且不需要很费力,就可以写成带有参考实现的 PEP。(我想 Bruce 应该会发现自己提案中的缺陷,如果他真的付出努力尝试编写可靠的 PEP 或者尝试实现它。)
我可以继续聊很多,但这是一个阳光明媚的周日早晨,而我还有其它的计划... :-)
作者:Guido van Rossum,写于:2008.10.26
参考资料
[1] Bruce博文:http://www.artima.com/weblogs/viewpost.jsp?thread=239003
[2] Guido原文: https://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html
——热门课程推荐:
想学习PYTHON数据分析与金融数字化转型精英训练营,您可以点击>>>“人才转型”了解课程详情;
想从事业务型数据分析师,您可以点击>>>“数据分析师”了解课程详情;
想从事大数据分析师,您可以点击>>>“大数据就业”了解课程详情;
想成为人工智能工程师,您可以点击>>>“人工智能就业”了解课程详情;
想了解Python数据分析,您可以点击>>>“Python数据分析师”了解课程详情;
想咨询互联网运营,你可以点击>>>“互联网运营就业班”了解课程详情;
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
人力资源专员是HR体系的执行入口。招聘信息发布、简历初筛、面试安排、考勤汇总、入离职手续、劳动合同与档案管理,大多由这个岗 ...
2026-10-22人力资源经理问“必学技巧”,真正要解决的不是知识清单不够长,而是时间有限、业务催得紧,学完却用不上。更有效的路径是:先用 ...
2026-10-22内容营销岗位的核心任务,不是“把文案写得更漂亮”,而是围绕目标受众,用持续的内容供给影响认知、推动转化并沉淀复购。它至少 ...
2026-10-22理财经理培训课程很多,难的是判断“先学什么、后学什么、学完能做什么”。与其按课程名称罗列,不如先看岗位任务:客户分层与维 ...
2026-10-22理财经理的知识结构,可以先用一句话概括:合规与产品知识决定“不出错”,客户经营与资产配置决定“够不够专业”,市场研判与数 ...
2026-10-22经济学背景做数据分析,优势不在工具,而在问题意识:供需如何相互作用、成本与收益如何权衡、变量之间是相关还是因果。真正的缺 ...
2026-10-22需要,但要把“需要”理解准确。经济学本身就在处理数据:GDP、物价、就业、企业营收、问卷调研,都是经济分析的原料。真正的区 ...
2026-10-22经济学分析能力不是单一技能,它至少由三层构成:把现实问题转成可检验假设的理论框架能力、用数据与统计方法验证假设的工具能力 ...
2026-10-22金融分析师的知识提升,先补的不是更多模型,而是让结论可复核的底层能力。按任务顺序,大致是四层:财务与业务理解、指标口径管 ...
2026-10-22金融分析师的“通用技能”,指换到不同机构、不同行业组仍用得上的底层能力,而非某家公司内部系统的操作经验。按任务拆,日常要 ...
2026-10-22先给结论:数据分析证书不是金融分析师的准入条件,招聘方也不会因为一张证书就直接判断你能做分析。要不要考,取决于你当前任务 ...
2026-10-22金融分析师的核心技能,不是“会看K线”或“会讲宏观”,而是能把一家公司、一个行业或一类资产,转化为可核查的数据判断与书面 ...
2026-10-22产品经理的知识提升,不是把需求文档、原型、SQL、数据分析、项目管理都学一遍,而是先对准目标岗位的真实任务,找出自己最缺的 ...
2026-10-22产品专员提升工作知识,最有效的方式不是先囤课程,而是围绕日常交付的四件事来补:需求调研与优先级判断、原型与PRD表达、竞品 ...
2026-10-22产品运营专员提升个人能力,常见误区是先把Excel、SQL、Python列成一张学习清单,然后从第一个工具开始学。更有效的做法是先回答 ...
2026-10-22产品运营的能力提升,最怕从“课程目录”出发:今天学增长模型,明天背SQL语法,学完却说不清自己负责的功能为什么激活率低。更 ...
2026-10-22产品经理的技能和影响力不是两条分开的赛道。技能决定你能不能把问题分析清楚、把方案设计对;影响力决定你的判断能否被团队接受 ...
2026-10-22产品经理要学的数据分析能力,不是“会写SQL”或“会用BI工具”这么简单。更准确地说,是围绕需求评估、功能迭代、指标监控、实 ...
2026-10-22产品经理的业务能力,不是“会用多少工具”,而是能否把业务目标翻译成可执行的指标、用数据验证判断、再推动方案落地并复盘。全 ...
2026-10-22产品经理快速提升职场技能,关键不在多报几门课,而在于先找出当前岗位上最影响产出的一两项能力缺口,围绕“业务理解—需求与指 ...
2026-10-22