`self`参数的作用是什么?为什么需要它?

1322
考虑这个例子:
class MyClass:
    def func(self, name):
        self.name = name

我知道self指的是MyClass的具体实例。但为什么func必须显式地将self作为参数包含在内?为什么我们需要在方法的代码中使用self?其他一些语言会将其隐式处理,或者使用特殊的语法。


如需考虑与语言无关的设计决策,请参阅什么是强制显式self指针的优势?

为了关闭调试问题,其中OP省略了方法的self参数并获得了TypeError,请改用TypeError:method()需要1个位置参数,但提供了2个。如果OP在方法体中省略了self.并获得了NameError,请考虑如何在类内调用函数?


128
你可能会对Guido van Rossum所写的这篇文章“为什么显示self必须保留”感兴趣:http://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html - unutbu
15
请参考 "为什么方法定义和调用中必须显式使用 'self'":http://docs.python.org/faq/design.html#why-must-self-be-used-explicitly-in-method-definitions-and-calls - unutbu
39
我觉得这很主观,你不觉得吗?为什么@nameself.name更直观?在我看来,后者更加直观。而且我也很容易理解。 - Santa
18
这是函数和类方法之间的关键区别。一个函数是自由浮动的,没有任何限制。而一个类(实例)方法必须意识到它的父类(和父类属性),所以你需要向该方法传递一个指向父类的引用(作为 self)。这只是在理解面向对象编程之前需要内化的一个少了的隐式规则。其他语言选择语法糖而不是语义简单性,但Python不同于其他语言。 - Evan Plaice
3
呸,self。为什么编译器不直接处理并将self作为关键字?既然它已经在许多编程语言中出现了,如'this'或类似的东西,那就没什么大不了的了。在我的所有经验中,太多的东西对你来说都是不好的。太多糖果,太多水,还有太多显式调用。当显式更方便时,显式才比隐式更好。如果我们要使self显式,为什么不使整个语言显式,并让用户自己编写和编译呢? - user1881400
显示剩余8条评论
26个回答

790
使用self.的原因是Python没有使用特殊语法来引用实例属性。Python决定以一种使方法所属的实例被自动传递但不自动接收的方式来进行方法:方法的第一个参数是调用该方法的实例。这使得方法完全与函数相同,并将要使用的实际名称留给您(尽管self是惯例,而且当您使用其他名称时人们通常会皱眉头)。self对代码并不特殊,它只是另一个对象。
Python可以使用其他方式来区分常规名称和属性-例如Ruby具有的特殊语法,或像C++和Java那样需要声明,或者可能是更不同的东西-但它没有这样做。 Python非常注重使事情变得明确,让人们清楚地知道什么是什么,尽管它并非在任何地方都这样做,但它确实对实例属性这样做。这就是为什么分配给实例属性需要知道要分配给哪个实例,并且这就是为什么它需要self。

26
@Georg说的cls指的是类对象,而不是实例对象。 - SilentGhost
22
@SilentGhost:实际上,第一个参数的名称可以随意指定。在类方法中,通常使用cls,而在实例方法中通常使用self。如果我愿意,我也可以将self用于类方法,将cls用于实例方法。我还可以使用bobfnord等其他名称。 - SingleNegationElimination
75
我觉得很有趣的是社区没有选择使用“this”而是使用了“self”。在早期的编程语言中,“self”是否有一些历史背景我不知道? - Jules G.M.
29
@Julius,“self”这个概念是来自于Modula-3的惯例,有关这个选择的更多详细信息,请参阅此答案(免责声明:该答案为本人所撰写)。 - Bakuriu
12
@Julius,“self”关键字(Smalltalk,1980)早于“this”关键字(来自C ++)。参见:https://dev59.com/rXNA5IYBdhLWcg3wIqLr#1080192#1080192 - Wes Turner
显示剩余10条评论

618
假设您有一个类ClassA,其中包含定义为methodA的方法:

class ClassA:
    def methodA(self, arg1, arg2):
        ... # do something

而且 objectA 是这个类的一个实例。

现在当调用 objectA.methodA(arg1, arg2) 时,Python会在内部为您进行转换:

ClassA.methodA(objectA, arg1, arg2)

self变量指的是对象本身。


148
我阅读了所有其他答案并有点理解,我读了这个之后一切都变得清晰明了。 - Seth
6
为什么不像Ruby一样将那些内脏保持在内部呢? - Cees Timmerman
但是在__init__(self)方法中,它接受self参数,那么即使没有创建对象,它如何引用自身? - saurav
2
这并没有回答问题。OP问的是为什么必须明确定义self - Rain
为什么每个classA实例都会生成一个新函数?从这种内部转换来看,更合适的说法似乎是methodA是类的方法而不是实例的方法。 - ado sar
1
@saurav __init__ 不会创建对象,它通过确定其初始状态来初始化对象。当您调用 MyClass() 时,背后有几个步骤。在正常情况下:查找 MyClass.__call__ 并在 type(类的通常元类)中找到;调用该函数,该函数将调用 MyClass.__new__,然后将结果作为 self 传递给 MyClass.__init__。同时,MyClass.__new__ 通常默认为 object.__new__,它会创建指定类的新对象(即设置其 __class__ 并给它一个空的 __dict__ 来保存属性)。 - Karl Knechtel

440

让我们来看一个简单的向量类:

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方法,那么我们可以这样做:

Vector.length_new = length_global
v = Vector(3, 4)
print(v.length_new()) # 5.0

这段代码的工作原理是,length_global 的第一个参数可以被重新用作 length_newself 参数。如果没有显式的 self,这将不可能实现。


另一种理解需要显式 self 的方法是查看 Python 添加一些语法糖的情况。当你记住基本上像下面这样调用:

v_instance.length()

被内部转换为

Vector.length(v_instance)

很容易看出self的作用。在Python中,您实际上不会编写实例方法;您编写的是类方法,其必须将实例作为第一个参数。因此,您需要明确地将实例参数放在某个位置。


4
Vector.length_new = length_global... 我实际上已经开始在我的类声明中使用这样的语法。每当我只想从另一个类中继承一些方法时,我就会显式地复制方法的引用。 - Jeeyoung Kim
2
Python的“实例方法”是否只是将静态全局方法(如Java或C++中)与实例对象一起传递以封装多个属性的语法糖?这种说法有一半是正确的,因为在多态性中,“this”(在Java中)或“self”最重要的目的是提供正确的方法实现。Python确实有这个功能。因此,在Python中调用myobj.someMethod()等同于TheClassOfMyObj.someMethod(myobj)。请注意,“TheClassOfMyObj”由Python自动从“self”中找出,否则您需要自己查找。 - teddy teddy
4
实际上,实例方法只是类方法,而方法只是类的成员函数,就像 Vector.length_new = length_global 所示。 - RussW
1
这段代码之所以能够正常工作,是因为length_global的第一个参数可以被重用作length_new中的self参数。如果没有显式的self,这是不可能实现的。如果使用隐式的self,它仍然可以正常工作。而第二个例子则是循环推理 - 你必须显式地放置self,因为Python需要显式的self。 - Karoly Horvath
1
@KarolyHorvath:当然,也可以使用一种模型来定义语言,其中内部定义的方法不需要显式的self,而外部定义的方法则需要。但我认为在这两种情况下都要求显式的self是有一定的一致性的,这使得这种方式成为合理的选择。其他语言可能会选择不同的方法。 - Debilski
显示剩余6条评论

250
当对象被实例化时,对象本身会传递到 `self` 参数中。

enter image description here

因此,对象的数据与对象绑定在一起。以下是一个示例,展示了每个对象的数据可能是什么样子。请注意,self被替换为对象的名称。我并不是说下面这个示意图完全准确,但它有助于让人们想象如何使用self

enter image description here

对象被传递到self参数中,以便对象可以保持其自己的数据。

虽然这可能不完全准确,但是将实例化(创建和分配内部值)对象的过程视为:当对象被创建时,对象使用类作为其(对象的)自身数据和方法的模板。如果不将对象自己的变量名传递到self参数中,则类中的属性和方法将保持通用模板,并且不会引用(属于)对象。因此,通过将对象的名称传递到self参数中,这意味着如果从一个类实例化100个对象,则每个100个对象都可以跟踪其(每个对象的)自身数据和方法。

总结:类是一般(不是超级具体)的模板,新创建的对象可以将其自己的特定数据传递到其中(而不必影响可能从同一类创建的其他对象),从而允许更少的复制粘贴代码,以创建具有相同模式的多个对象。self用于指定应使用特定对象的数据,而不是其他数据。

请看下面的示意图:

enter image description here


1
嗨,当通过“bob.name()”访问Bob的属性时,实际上是从'init'中访问bob()。self.name,对吧? - udarH3
7
在上面的评论中,当你写下 bob.name() 时,由于在 name 后面添加了括号,你就暗示了 bob 有一个名为 name() 的方法。然而,在这个例子中并没有这样的方法。'bob.name'(没有括号)直接访问了 init(构造函数)方法中的名为 name 的属性。当调用 bob 的 speak 方法时,它会访问 name 属性并在打印语句中返回它。希望这可以帮到你。 - sw123456
4
不,你会得到self.name的值,对于bob对象来说实际上是bob.name,因为当对象被创建(实例化)时,它的名称被传递到self参数中。希望这可以帮助你,如果有帮助的话,请随意点赞主贴。 - sw123456
4
实例化时,将名称分配给self.name。对象创建后,属于对象的所有变量都以“self.”为前缀。请记住,当从类中创建对象时,self会被替换为对象的名称。 - sw123456
9
这是你解释事情的方式!做得很好 :) - penta
显示剩余4条评论

87

我喜欢这个例子:

class A: 
    foo = []

a, b = A(), A()
a.foo.append(5)

b.foo  # [5]

class A: 
    def __init__(self): 
        self.foo = []

a, b = A(), A()
a.foo.append(5)

b.foo  # []

21
没有 self 的变量就是类的静态变量,就像 Java 中的一样。 - teddy teddy
7
泰迪泰迪,你的说法并不完全正确。行为(静态或非静态)不仅取决于 self,也取决于变量类型。试着用简单的整数代替列表来进行第一个示例。结果会大不相同。 - Konstantin
2
实际上,我对此的问题是,为什么您可以在第一个示例中说a.foo,而不是A.foo?显然foo属于类... - Radon Rosborough
在大多数编程语言中,您可以从对象的实例中调用静态成员。这为什么会让人感到惊讶呢? - Paarth
2
因为在第一个例子中,ab都是A()类的标签(或指针)。a.foo引用了A().foo类方法。然而,在第二个例子中,a变成了A()实例的引用,b也是如此。现在它们是实例而不是类对象本身,self允许foo方法对实例进行操作。 - LegendaryDude
@LegendaryDude 这并不完全正确,在这两种情况下,ab 都是 A() 实例的引用,但是在第二种情况下,a.foob.foo 引用了它们自己内部的一个引用,而在第一种情况下,a.foob.foo 引用了类内部的一个引用。之所以可以访问类内部的引用,是为了简化操作(在 Python 中经常如此):想象一个函数 def bar(self): return self.foo;。由于我们可以引用类成员,f = a.bar; f(a); 将与 f = A.bar; f(a) 完全相同,并且在两种情况下 f 都是相同的。 - yyny

46

我将用不使用类的代码进行演示:

def state_init(state):
    state['field'] = 'init'

def state_add(state, x):
    state['field'] += x

def state_mult(state, x):
    state['field'] *= x

def state_getField(state):
    return state['field']

myself = {}
state_init(myself)
state_add(myself, 'added')
state_mult(myself, 2)

print( state_getField(myself) )
#--> 'initaddedinitadded'

类只是一种避免在所有时间传递这个“状态”东西的方法(以及其他好东西,比如初始化、类组合、很少需要的元类以及支持自定义方法来覆盖运算符)。

现在让我们使用内置的Python类机制演示上述代码,以展示它基本上是相同的东西。

class State(object):
    def __init__(self):
        self.field = 'init'
    def add(self, x):
        self.field += x
    def mult(self, x):
        self.field *= x

s = State()
s.add('added')    # self is implicitly passed in
s.mult(2)         # self is implicitly passed in
print( s.field )

[从重复关闭的问题迁移了我的答案]


1
我希望Python能像Ruby一样把处理程序包装得更好。 - Cees Timmerman
如果我不使用self,而是直接使用field += x会怎样? - skan

23
以下摘自Python关于self的文档
如同Modula-3一样,在Python中没有简写方法来引用对象的成员:方法函数声明时需要一个显式的第一个参数表示对象,而这个参数在调用时会被隐式提供。
通常,一个方法的第一个参数称为self。这只是一种约定:self这个名称对Python来说并没有任何特殊含义。但请注意,不遵循这种约定可能会降低你的代码可读性,其他Python程序员也可能无法理解你的代码,同时也有可能会写一些依赖该约定的类浏览器程序。
更多信息请参见Python类教程

22

除了已经提到的其它原因外,它还可以更轻松地访问被覆盖的方法;你可以调用Class.some_method(inst)

以下是一个使用它很有用的示例:

class C1(object):
    def __init__(self):
         print "C1 init"

class C2(C1):
    def __init__(self): #overrides C1.__init__
        print "C2 init"
        C1.__init__(self) #but we still want C1 to init the class too
>>> C2()
"C2 init"
"C1 init"

18

它的用法类似于Java中this关键字的用法,即用来引用当前对象。


4
类 myClass: def myFunc(this, name): this.name = name - LEMUEL ADANE

13

Python不像Java或C++那样是为面向对象编程而构建的语言。

首先,方法属于整个类(静态方法)或类的对象(实例)(对象方法)。

在Python中调用静态方法时,只需在其中编写带有常规参数的方法即可。

class Animal():
    def staticMethod():
        print "This is a static method"

然而,一个对象(即实例)方法需要你创建一个对象(即实例,在这种情况下是一个动物),需要使用 self 参数。

class Animal():
    def objectMethod(self):
        print "This is an object method which needs an instance of a class"

self方法也用于引用类内的变量字段。

class Animal():
    #animalName made in constructor
    def Animal(self):
        self.animalName = "";

    def getAnimalName(self):
        return self.animalName

在这种情况下,self 是指整个类的 animalName 变量。记住:如果您在方法中创建了一个新变量(称为局部变量),则 self 将无法使用。该变量仅在该方法运行时存在。对于定义字段(整个类的变量),您必须在类方法之外定义它们。
如果您不理解我所说的任何一个词,那么请谷歌“面向对象编程”。一旦您理解了这一点,您甚至都不需要问那个问题 :)。

+1 是因为 staticMethod()objectMethod(self) 之间的区别。我想补充一点,要调用第一个方法,你需要输入 Animal.staticMethod(),而 objectMethod() 需要一个实例:a = Animal(); a.objectMethod() - András Aszódi
3
你声明了一个无用的animalName字符串并导致animalName方法崩溃。 - Cees Timmerman
对于@user441521,Python适用于脚本编写而不是面向对象编程,它在面向对象编程标准方面表现不佳。对于timmerman,这是为教育目的而编写的,这只是一个小错误,只有那些简单地复制粘贴而不理解的人会遇到问题,这是SO所认为的不道德行为。 - rassa45
6
不相关。混淆和不正确的代码不应被提供为答案。 - Cees Timmerman
3
为了避免覆盖你要返回的字符串,请使用 def getAnimalName,而 self 指的是类的实例,而不是其中的任何字段。 - Cees Timmerman
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接