花猫语:前两天偶然在知识星球看到一个话题(Mr.不像其他语言那么隐蔽,很烦人。我对此也有同感。在群里讨论后,得知Guido曾经写过一篇文章来解释这个问题,这篇文章不太好理解,我先抽空翻译了一下,看看能收到什么回复,如果可以的话,以后再写一篇分析。-------------以下为翻译----------------布鲁斯埃克尔(BruceEckel)发表博文,提出类方法Remove的形式参数列表中的“self”。我会解释为什么这个提议不能通过。(译注:Bruce是《Thinking in Java》、《Thinking in C++》等多本书的作者,也是一名Python开发者。他的文章总结了当年PyconBrazil的一次讨论,主要是在定义类方法时,形式参数中的“self”是多余的,导致的错误信息有些误导。)Bruce的建议Bruce知道我们需要一种方法来区分对实例变量的引用和对其他变量的引用,因此他建议将“self”作为关键字。考虑一个典型的类,它有一个方法,例如:classC:defmeth(self,arg):self.val=argreturnself.val按照Bruce的建议,这将变成:classC:defmeth(arg):#Look妈妈,没有自我!self.val=argreturnself.val这为每个方法节省了6个字符。但我不认为Bruce提出这个建议是为了减少打字。我认为他真正关心的是程序员(可能来自其他语言)所浪费的时间,他们有时似乎不需要指定“self”参数,并且偶尔会忘记添加它(即使他们完全知道-习惯是强大的力量)。事实上,从参数列表中省略“self”往往会导致非常模糊的错误消息,而不是忘记键入“self”。在实例变量或方法引用之前。也许更糟糕的是(正如Bruce提到的)当一个方法被正确声明但使用错误数量的参数调用时你得到的错误消息。正如Bruce给出的以下示例:Traceback(mostrecentcalllast):File"classes.py",line9,inobj.m2(1)TypeError:m2()takesexactly3arguments(2given)我同意这很令人困惑,但我宁愿修复此错误消息也不愿修改语言。为什么Bruce的提议行不通首先,让我提出一些反对Bruce提议的典型论据。有一个很好的论据,即在参数列表中使用显式“self”可以增强以下两种调用方法的理论上的等效性。假设“foo”是“C”的实例:foo.meth(arg)==C.meth(foo,arg)在定义方法内部时,可能有几种不同的方法:实例方法、类方法和静态方法.它们的功能和行为都不一样,那么如何区分定义和调用呢?Python约定了一种方式,就是在定义的时候用第一个参数来区分:self代表一个实例方法,cls或者其他符号代表一个类方法……这三个方法都可以被一个类的实例调用,并且它们看起来完全一样,如上例中等号左侧所示那样。这时通过定义时给出的参数来区分。如上例等号右边,第一个参数是实例对象,说明这是一个实例方法。)另一个说法是在参数列表中使用Explicit“self”,插入一个函数到一个类中,获得动态修改类的能力,并创建相应的类方法。例如,我们可以创建一个完全等同于上面“C”的类,如下所示:#定义一个空类:classC:pass#定义一个全局函数:defmeth(myself,arg):myself.val=argreturnmyself.val#把方法戳进类中:C.meth=meth注意我把“self”参数重命名为“myself”是为了强调(语法上)我们不是在这里定义方法(译注:类的外部是函数,即函数,类内部是方法,即方法)。然后C的实例有一个“meth”方法,它接受一个参数并且功能与以前完全一样。它甚至适用于在将方法插入类之前创建的C实例。我想Bruce并不特别关心上述等价性。我同意这只是理论上重要的。我能想到的唯一例外是调用超级方法的老式习语(idiom)。然而,这个习惯用法很容易出错(正是由于需要显式传递“self”),这就是为什么在Python3000中,我建议在所有情况下都使用“super()”。Bruce可能会想出一种方法来使第二个等价示例起作用——在某些情况下,这种等价确实很重要。我不知道Bruce花了多少时间思考如何实现他的提议,但我认为他正在考虑自动为类内部直接定义的所有方法添加一个名为“self”的额外参数的想法(我有说“直接”以便嵌套在方法中的函数免于此自动操作)。这样,第一个等效示例可以保持等效。但是,如果不向编译器添加某种ESP,我认为Bruce无法解决一种情况:装饰器。我相信这是Bruce提议的最终失败。在装饰方法时,我们不知道是否自动给它一个“self”参数:装饰器可以把一个函数变成静态方法(没有“self”)或类方法(有一个有趣的self,指向一个类而不是一个实例),或者可以做一些完全不同的事情(在纯Python中实现“@classmethod”或“@staticmethod”装饰器是乏味的)。除非知道装饰器的目的,否则没有其他方法可以确定是否将隐式“self”参数分配给正在定义的方法。我拒绝像特别包装的“@classmethod”和“@staticmethod”这样的黑科技。我也不认为除了自我测试之外自动确定一个方法是类方法、实例方法还是静态方法是一个好主意(正如有人建议的那样,在Bruce的文章的评论中):这很难仅根据它前面的“def”来决定应该如何调用一个方法。(译注:对于一个方法,在当前添加了相应参数的情况下,可以简单的添加一个装饰器来区分是哪个方法,调用时很容易区分调用;但是,如果不添加参数,即使你可以使用magic的自动机制来区分它是哪个方法,但是在调用的时候,你不确定如何调用它)。在评论中,我看到了Bruce提议的一些非常极端的回应,但通常是以让规则更难遵循为代价的,或者需要对语言进行更深层次的改变,这让我们极难接受,尤其是结合Python3.1.顺便说一句,对于3.1,再次声明我们的规则,新功能只有在保持向后兼容性的情况下才可以接受。一个似乎可行的建议(这将使它向后兼容)是将类中的deffoo(self,arg):...更改为这个语法糖:defself.foo(arg):...但我不同意把“self”变成保留字(reservedword),或者要求前缀为“self”。如果你这样做了,对类方法也很容易做到这一点:@classmethoddefcls.foo(arg):...好吧,我不喜欢这个比现状更好。但我认为这个比Bruce的提议或者他博客评论部分的更极端的主张要好得多,而且它具有向后兼容的巨大优势,可以用PEP编写以供参考实现。(我认为Bruce应该看到他的建议中的缺陷,如果他真的努力尝试编写可靠的PEP或尝试实施它的话。)我可以继续下去,但这是一个阳光明媚的星期天早晨,我仍然在那里是其他计划...:-)作者:GuidovanRossum,撰写时间:2008.10.26英语:https://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay。html作者简介:GuidovanRossum,Python的创造者,在2018年7月12日退位之前是一位“终生仁慈的独裁者”。目前,他是新的最高决策层级的五名成员之一,并且仍然活跃在社区中。译者简介:猫下豌豆花,出生于广东,毕业于武汉大学,现为苏飘程序员。他有一些极客思维,有一些人文情怀,有一些温暖,有一些态度。公众号:“蟒猫”(python_cat)。公众号【Python猫】,本号连载系列精品文章,包括喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等,欢迎收看注意。
