Python中self参数:显式还是冗余?
本文深入探讨Python中self参数的必要性。尽管“显式优于隐式”是Python的核心哲学,但强制在方法定义中显式列出self被视为一种冗余。文章分析了方法定义与调用时的隐式传递机制,指出这导致了定义与调用逻辑的不一致,并提出了将self作为关键字的改进建议,以消除这种非Pythonic的设计矛盾。
self的作用与争议
self在区分全局变量或函数与对象成员变量或函数时具有关键作用。例如,它提供了一种清晰的作用域机制,相比Ruby中使用的@和@@符号,这种机制更为直观(尽管这可能源于个人习惯)。从概念上讲,它与C++和Java中的this关键字非常相似。
然而,self的存在始终让我感到困惑。正如我之前所提到的,我曾希望Python 3能对此进行改进,但相关讨论往往以“显式优于隐式”这一原则告终,而未能解决根本问题。
在巴西期间,我与巴西Python组织主席Luciano Ramalho进行过交流。他帮我理清了思路:真正让我困扰的并非无处不在的self本身,而是参数列表中必须显式写出self这一要求,这种做法在我看来是非Pythonic的。
self的使用机制
它是如何使用的
以下简单Python代码展示了类的基本用法。
代码如下:
def f(): pass
a = 1
class C1(object):
a = 2
def m1(self):
print a # Prints '1'
print self.a # Prints '2'
f() # The global version
self.m2() # Must scope other members
def m2(self): pass
obj = C1()
obj.m1()
首先观察f()和a,它们均可在全局作用域中访问。类C1继承自object,这是定义新类的标准方式(注:在Python 3中,这种继承方式可能变得不再那么显眼)。
注意,m1()和m2()的第一个参数均为self。在Python中,self并非关键字,但按照惯例,它代表当前对象的引用,即对象的地址通常作为第一个参数传递。
在类级别定义a是创建对象作用域的一种方式。你也可以在方法内部通过赋值给self.a来创建该属性,此时内存空间会在第一次执行该语句时分配。但必须区分这两种版本的a:若方法内部直接使用a,则引用的是全局版本;而self.a指向的是对象域(此处暂不考虑在类内部修改全局变量的情况)。
同理,对f()的非限定调用指向全局函数,而通过self.m2()的限定调用则指向成员函数(同时将当前对象地址作为self变量传递给m2())。
接下来看一个包含带参方法类的示例:
代码如下:
class C2(object):
def m2(self, a, b): pass
为了调用该方法,我们创建对象实例,并使用点表达式调用obj上的m2():
代码如下:
obj = C2()
obj.m2(1,2)
在调用过程中,obj的地址作为self变量隐式传递给m2()。这里存在一个明显的矛盾:为何在定义方法时“隐式”优于“显式”,而在调用方法时“隐式”传递又毫无问题?
这或许是由方法调用语法所决定的,但这意味着方法的定义和调用存在巨大差异,既谈不上“显式”,也不符合Pythonic风格。当调用参数数量错误的方法时,这一点尤为明显:
obj.m2(1)
结果报错如下:
Traceback (most recent call last):
File "classes.py", line 9, in <module>
obj.m2(1)
TypeError: m2() takes exactly 3 arguments (2 given)
由于方法调用期间self是隐式传递的,上述错误信息实际上暗示了正确的调用方式应为:
C2.m2(obj,1,2)
即使上述语句运行成功,它显然也不是实际的调用方式。你应该使用常规的方法调用语法,即传递两个参数:
obj.m2(1,2)
错误信息“m2() takes exactly 3 arguments (2 given)”不仅让初学者困惑,连我每次看到时也会感到不解。这既表明其非Pythonic,也直指方法定义与调用之间的逻辑矛盾。
改进建议与结论
绝望的建议
尽管历史长河中充满了无奈,但我有哪些建议呢?
在Python 3.1中将self设为关键字(这将带来一定的向后不兼容性)(或者直接使用this,以便C++和Java程序员更轻松地过渡)。同时保持所有与self相关的现有规则不变。
唯一的改变是:你无需将self放入方法参数列表中。这是唯一隐式的地方,其余部分均为显式——除了方法调用方式保持不变。
这实现了方法定义和调用的一致性。你可以定义一个参数数量与调用时相同的方法。当调用方法传递的参数数量错误时,错误信息会提示方法实际应有的参数个数,而不是多出一个。
显式 vs.冗余
在我再次听到“显式优于隐式”之前,需要明确:使某事清晰与使其冗余是有区别的。我们有一种语言,起初看似良好,但随后问题越来越多,这种语言叫做Java。
如果想让每件事都变得显式,我们可以使用C或汇编等能够精确展示机器内部运行细节的语言。
强制程序员将self放入方法参数列表与“显式”毫无关系,它只是强制造成冗余,并未增加编程的表达力(既然已知是方法,何必还要在参数列表中通过self来提醒我们呢?)。我认为这种做法是死板的,也是非Pythonic的。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









