`self`参数的目的是什么?为什么需要它?
考虑这个例子:
class MyClass:
def func(self, name):
self.name = name
我知道 self
是指 MyClass
的具体实例。但是,为什么 func
必须明确地把 self
作为一个参数呢?我们在方法的代码中为什么需要使用 self
?有些其他语言会自动处理这个,或者使用特别的语法来代替。
如果想了解这个设计决策的语言无关的考虑,可以查看 为什么这个/self指针必须明确指定的好处是什么?.
如果想解决 调试问题,比如用户在方法中省略了 self
参数而导致了 TypeError
,可以参考 TypeError: method() takes 1 positional argument but 2 were given。如果用户在方法体内省略了 self.
而导致了 NameError
,可以考虑 如何在类中调用函数?.
26 个回答
让我们来看一个简单的向量类:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
我们想要有一个方法来计算长度。如果我们想把这个方法定义在类里面,它会是什么样子呢?
def length(self):
return math.sqrt(self.x ** 2 + self.y ** 2)
如果我们把它定义成一个全局的方法/函数,那又会是什么样子呢?
def length_global(vector):
return math.sqrt(vector.x ** 2 + vector.y ** 2)
整体结构保持不变。我们怎么利用这个呢?假设我们暂时没有为我们的 Vector
类写一个 length
方法,我们可以这样做:
Vector.length_new = length_global
v = Vector(3, 4)
print(v.length_new()) # 5.0
这样做是可行的,因为 length_global
的第一个参数可以在 length_new
中作为 self
参数重复使用。如果没有明确的 self
,这是不可能的。
理解为什么需要明确的 self
还有另一种方式,就是看看 Python 是如何简化语法的。当你记住,基本上像这样的调用:
v_instance.length()
在内部会被转换成:
Vector.length(v_instance)
这样就很容易看出 self
的位置了。你实际上并不是在 Python 中写实例方法;你写的是类方法,这些方法必须把实例作为第一个参数。因此,你必须在某个地方明确地放置这个实例参数。
假设你有一个类叫做 ClassA
,里面有一个方法叫 methodA
,定义如下:
class ClassA:
def methodA(self, arg1, arg2):
... # do something
而 objectA
是这个类的一个实例。
现在,当你调用 objectA.methodA(arg1, arg2)
的时候,Python 会在内部帮你把它转换成:
ClassA.methodA(objectA, arg1, arg2)
这里的 self
变量指的就是这个对象本身。
你需要使用 self.
的原因是因为 Python 没有特别的语法来指代实例属性。Python 选择了一种方法,让调用方法的实例自动传递给方法,但并不是自动接收:方法的第一个参数就是调用这个方法的实例。这使得方法和函数完全一样,具体用什么名字由你自己决定(虽然 self
是一个约定,大家通常会对你用其他名字表示不满)。self
对代码来说并没有特别的意义,它只是另一个对象。
Python 本可以用其他方式来区分普通名字和属性,比如像 Ruby 那样使用特殊语法,或者像 C++ 和 Java 那样要求声明,甚至可能有更不同的方式,但它没有这么做。Python 更倾向于让事情变得明确,清楚地表明每样东西是什么,虽然它并不是在所有地方都做到这一点,但对于实例属性来说,它确实做到了。这就是为什么给实例属性赋值时需要知道要赋值给哪个实例,也就是为什么需要 self.
。