Python中self参数:显式还是冗余?

一只小鹿
一只小鹿
发布时间:2026-10-13| 3人浏览过
原创

本文深入探讨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的。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。