python:self的目的是什么?

Python中self字的目的是什么?我理解它指的是由该类创建的特定对象,但我不明白为什么需要显式地将它作为参数添加到每个函数中。为了说明这一点,在Ruby中我可以这样做:

1
2
3
4
5
class myClass
    def myFunc(name)
        @name = name
    end
end

我很容易理解。但是在Python中,我需要包含self:

1
2
3
class myClass:
    def myFunc(self, name):
        self.name = name

有人能跟我说说吗?在我有限的经验中,我从未遇到过这种情况。


需要使用self.的原因是Python不使用@语法来引用实例属性。Python决定采用一种方法,使方法所属的实例自动传递,而不是自动接收:方法的第一个参数是方法所调用的实例。这使得方法与函数完全相同,并将实际的名称留给您自己使用(尽管self是惯例,当您使用其他东西时,人们通常会对您皱眉头)。

Python本可以做一些其他的事情来区分普通名称和属性——像Ruby那样的特殊语法,或者需要像c++和Java那样的声明,或者做一些更不同的事情——但是它没有这样做。Python的全部功能是使事情显式化,使什么是什么一目了然,尽管它并不是在所有地方都这样做,但它确实在实例属性上这样做。这就是为什么赋值给实例属性需要知道赋值给哪个实例,这就是为什么它需要self.


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

1
2
3
4
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

我们想要一个计算长度的方法。如果我们想在类中定义它会是什么样子?

1
2
    def length(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)

当我们将它定义为一个全局方法/函数时,它应该是什么样的呢?

1
2
def length_global(vector):
    return math.sqrt(vector.x ** 2 + vector.y ** 2)

所以整个结构保持不变。我怎么才能利用这个呢?如果我们假设我们没有为我们的Vector类编写一个length方法,我们可以这样做:

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

这是因为length_global的第一个参数可以作为length_new中的self参数重用。如果没有显式的self,这是不可能的。

理解显式self的需要的另一种方法是查看Python在哪里添加了一些语法糖。当你记在心里的时候,那基本上就是一个电话

1
v_instance.length()

内部转化为

1
Vector.length(v_instance)

很容易看出self的位置。你不用Python写实例方法;您所编写的是类方法,它必须以实例作为第一个参数。因此,您必须显式地将实例参数放在某个位置。


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

1
2
def methodA(self, arg1, arg2):
    # do something

ObjectA是这个类的一个实例。

现在当调用ObjectA.methodA(arg1, arg2)时,python内部将它转换为:

1
ClassA.methodA(ObjectA, arg1, arg2)

self变量引用对象本身。


当对象被实例化时,对象本身被传递给self参数。

enter image description here

因此,对象的数据被绑定到对象。下面是一个示例,演示如何可视化每个对象的数据。注意self是如何被对象名称替换的。我并不是说下面的这个例子图是完全准确的,但希望它能对可视化self的使用有帮助。

enter image description here

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

虽然这可能不是完全准确的,但是可以这样考虑实例化一个对象的过程:当一个对象被创建时,它将该类用作它自己的数据和方法的模板。如果不将它自己的名称传递给self参数,类中的属性和方法将保留为通用模板,并且不会引用(属于)对象。因此,通过将对象的名称传递给self参数,这意味着如果从一个类实例化100个对象,它们都可以跟踪自己的数据和方法。

见下图:

enter image description here


我喜欢这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
    foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: [5]

class A:
    def __init__(self):
        self.foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: []


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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类机制来演示上面的代码,以展示它们是如何基本相同的。

1
2
3
4
5
6
7
8
9
10
11
12
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 )

[把我的答案从重复的封闭式问题中移走]


以下摘录自Python关于self的文档:

As in Modula-3, there are no shorthands [in Python] for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call.

Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.

有关更多信息,请参阅有关类的Python文档教程。


和前面提到的所有其他原因一样,它允许更容易地访问被覆盖的方法;您可以调用Class.some_method(inst)

举个有用的例子:

1
2
3
4
5
6
7
8
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
1
2
3
>>> C2()
"C2 init"
"C1 init"

它的使用类似于在Java中使用this关键字,即给当前对象一个引用。


与Java或c++不同,Python不是为面向对象编程而构建的语言。

在Python中调用静态方法时,只需编写一个包含常规参数的方法。

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

然而,object方法需要创建一个变量(在本例中是动物),它需要self参数

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

self方法还用于引用类中的变量字段。

1
2
3
4
5
6
7
8
class Animal():
    #animalName made in constructor
    def Animal(self):
        self.animalName ="";


    def getAnimalName(self):
        return self.animalName

在本例中,self引用了整个类的animalName变量。记住:如果方法中有一个变量,self将不起作用。该变量仅在方法运行时存在。要定义字段(整个类的变量),必须在类方法之外定义它们。

如果你不明白我说的任何一个字,那么谷歌"面向对象编程"。一旦你理解了这一点,你甚至不需要问这个问题:)。


它遵循Python的禅"显式优于隐式"。它确实是对类对象的引用。例如,在Java和PHP中,它被称为this

如果user_type_name是模型上的字段,则由self.user_type_name访问它。


self是对象本身的对象引用,因此它们是相同的。Python方法不会在对象本身的上下文中调用。self在Python中可以用来处理自定义对象模型或其他东西。


首先,self是一个传统的名字,你可以用其他任何东西来代替它。

它引用对象本身,所以当您使用它时,您声明.name和.age是要创建的学生对象的属性(注意,不是学生类的属性)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Student:
    #called each time you create a new Student instance
    def __init__(self,name,age): #special method to initialize
        self.name=name
        self.age=age

    def __str__(self): #special method called for example when you use print
        return"Student %s is %s years old" %(self.name,self.age)

    def call(self, msg): #silly example for custom method
        return ("Hey, %s!"+msg) %self.name

#initializing two instances of the student class
bob=Student("Bob",20)
alice=Student("Alice",19)

#using them
print bob.name
print bob.age
print alice #this one only works if you define the __str__ method
print alice.call("Come here!") #notice you don't put a value for self

#you can modify attributes, like when alice ages
alice.age=20
print alice

代码在这里


我很惊讶没有人提到Lua。Lua还使用"self"变量,不过可以省略它,但仍然使用它。c++对"this"也做了同样的处理。我不认为有任何理由必须在每个函数中声明'self',但您应该仍然能够像使用lua和c++一样使用它。对于一种以简洁而自豪的语言来说,它要求声明self变量是很奇怪的。


这个参数的使用,传统上称为self并不难理解,但是为什么它是必要的呢?或者为什么要明确提到它?我想,对于大多数查询这个问题的用户来说,这是一个更大的问题,如果不是,那么当他们继续学习python时,肯定会遇到同样的问题。我建议他们阅读以下几个博客:

1:使用自我解释

注意,它不是一个关键字。

The first argument of every class method, including init, is always a reference to the current instance of the class. By convention, this argument is always named self. In the init method, self refers to the newly created object; in other class methods, it refers to the instance whose method was called. For example the below code is the same as the above code.

第2集:为什么我们要这样做,为什么我们不能像Java那样把它作为一个参数删除,而用一个关键字来代替

我想添加的另一件事是,一个可选的self参数允许我在类中声明静态方法,而不需要编写self

代码示例:

1
2
3
4
5
6
class MyClass():
    def staticMethod():
        print"This is a static method"

    def objectMethod(self):
        print"This is an object method which needs an instance of a class, and that is what self refers to"

PS:这只适用于Python 3.x。

在以前的版本中,必须显式地添加@staticmethod装饰器,否则必须使用self参数。


请看下面的示例,它清楚地解释了self的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Restaurant(object):  
    bankrupt = False

    def open_branch(self):
        if not self.bankrupt:
           print("branch opened")

#create instance1
>>> x = Restaurant()
>>> x.bankrupt
False

#create instance2
>>> y = Restaurant()
>>> y.bankrupt = True  
>>> y.bankrupt
True

>>> x.bankrupt
False

self用于/需要区分实例。


因为按照python的设计方式,这些替代方案很难奏效。Python旨在允许在隐式this (a-la Java/ c++)或显式@ (a-la ruby)都不能工作的上下文中定义方法或函数。让我们举一个python约定显式方法的例子:

1
2
3
4
5
def fubar(x):
    self.x = x

class C:
    frob = fubar

现在fubar函数无法工作,因为它假定self是一个全局变量(在frob中也是)。另一种方法是使用替换的全局范围(其中self是对象)执行方法。

隐式方法是

1
2
3
4
5
def fubar(x)
    myX = x

class C:
    frob = fubar

这将意味着myX将被解释为fubar(以及frob中的一个局部变量)。这里的另一种选择是使用替换的局部作用域来执行方法,该局部作用域在调用之间保留,但是这将删除方法局部变量的可能性。

然而,目前的情况很好:

1
2
3
4
5
 def fubar(self, x)
     self.x = x

 class C:
     frob = fubar

在这里,当frob作为方法调用时,它将接收通过self参数调用它的对象,并且fubar仍然可以使用对象作为参数调用,并且工作方式相同(我认为它与C.frob相同)。


__init__方法中,self引用新创建的对象;在其他类方法中,它引用其方法被调用的实例。

self,作为一个名字,只是一个约定,你想怎么叫就怎么叫!但是,当使用它时,例如删除对象时,您必须使用相同的名称:__del__(var),其中var__init__(var,[...])中使用。

您也应该查看一下cls,以获得更全面的信息。这篇文章可能会有帮助。


它是对类实例对象的显式引用。