关于python:Python-为什么要保留显式的-self

5次阅读

共计 2826 个字符,预计需要花费 8 分钟才能阅读完成。

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 或者尝试实现它。)
我能够持续聊很多,但这是一个阳光明媚的周日晚上,而我还有其它的打算 … :-)

以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈,发送“J”即可收费获取,每日干货分享

正文完
 0