ChatGPT解决这个技术问题 Extra ChatGPT

类方法中的“self”参数是什么?

self 是指从一个类创建的特定对象实例。但是为什么每个方法都必须明确包含 self 作为参数?

class MyClass:
    def func(self, name):
        self.name = name

相反,在 Ruby 中,没有 self

class MyClass
    def func(name)
        @name = name
    end
end
您可能会发现 Guido van Rossum 的这篇文章“为什么明确的自我必须留下”很有趣:neopythonic.blogspot.com/2008/10/…
另请参阅“为什么必须在方法定义和调用中显式使用‘self’”:docs.python.org/faq/…
“我很容易理解” --- 很主观,你不觉得吗?是什么让 @nameself.name 更直观?后者,IMO,更直观。
虽然玩鬼鼓吹很容易忘记在每个方法中添加一个额外的参数,并且在忘记时会出现奇怪的行为,这对初学者来说很难。恕我直言,我宁愿具体说明静态方法之类的不寻常事物,然后是实例方法之类的正常行为。
这是函数和类方法之间的关键区别。一个函数是自由浮动的,不受阻碍的。类(实例)方法必须知道它的父类(和父属性),因此您需要向该方法传递对父类(作为自身)的引用。这只是在理解 OOP 之前必须内化的一条不那么隐含的规则。其他语言选择语法糖而不是语义简单,python不是其他语言。

T
Thomas Wouters

您需要使用 self. 的原因是 Python 不使用 @ 语法来引用实例属性。 Python 决定以一种使方法所属的实例自动传递而不是自动接收的方式来执行方法:方法的第一个参数是方法的实例被调用。这使得方法与函数完全一样,并让您自己使用实际名称(尽管 self 是约定俗成的,当您使用其他东西时人们通常会皱眉头。)self 对代码,它只是另一个对象。

Python 可以做一些其他的事情来区分普通名称和属性——像 Ruby 那样的特殊语法,或者像 C++ 和 Java 那样需要声明,或者更不同的东西——但它没有。 Python 完全是为了让事情变得明确,让事情变得显而易见,尽管它并不完全在任何地方都这样做,但它确实可以为实例属性做到这一点。这就是为什么分配给实例属性需要知道要分配给哪个实例,这就是它需要 self. 的原因。


@Georg:cls 指的是类对象,而不是实例对象
@SilentGhost:实际上,第一个参数的名称是您想要的任何名称。在类方法上,约定是使用 cls,而 self 通常用于实例方法。如果需要,我可以将 self 用于类方法,将 cls 用于实例方法。如果我喜欢,我也可以使用 bobfnord
我觉得有趣的是社区没有选择 this 而不是 selfself 在旧编程语言中是否有一些我不知道的历史?
@Julius self 来自 Modula-3 的约定,有关此选择的更多详细信息,请参阅 this answer。 (免责声明:它是我的)。
@Julius self 关键字(Smalltalk,1980)早于 this 关键字(来自 C++)。请参阅:stackoverflow.com/questions/1079983/…
A
Arjun Sreedharan

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

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

ObjectA 是此类的一个实例。

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

ClassA.methodA(ObjectA, arg1, arg2)

self 变量引用对象本身。


我阅读了所有其他答案并且有点理解,我读了这个,然后一切都变得有意义了。
不过,为什么不像 Ruby 那样把这些胆量藏在心里呢?
但是在 __init__(self) 方法中,它接受了 self,那么即使没有创建对象,它又是如何引用自身的呢?
但这并不能回答问题。 OP 询问为什么必须明确定义 self
e
empty

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

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 中编写实例方法;你写的是类方法,它必须将实例作为第一个参数。因此,您必须将实例参数明确放置在某处。


Vector.length_new = length_global... 我实际上开始在我的类声明中使用这样的语法。每当我只想从另一个类继承一些方法时,我只是显式地复制对方法的引用。
公平地说,python 的“实例方法”只是静态全局方法(如在 Java 或 C++ 中)的语法糖,其中传递了一个实例对象以封装多个属性? --- 好吧,这有点半真半假,因为在多态中,“this”(如在 java 中)或“self”的更重要目的是为您提供正确的方法实现。 Python确实有这个。所以调用 myobj.someMethod() 等于 Python 中的 TheClassOfMyObj.someMethod(myobj) 。请注意,“TheClassOfMyObj”是由 python 从“self”中自动计算出来的,否则你必须找到它。
事实上,不仅实例方法只是类方法,而且方法也只是作为类成员的函数,如 Vector.length_new = length_global 所示。
“这是可行的,因为 length_global 的第一个参数可以重新用作 length_new 中的 self 参数。如果没有明确的 self,这是不可能的。” - 它的工作原理是一样的。它将被重新用于隐式自我......第二个例子是循环推理 - 你必须明确地将自我放置在那里,因为python需要显式自我。
@KarolyHorvath:当然,也有可能拥有一种具有模型的语言,其中内部定义的方法不需要显式的自我,但外部定义的方法需要。但我想说的是,在这两种情况下都需要明确的 self 有一定的一致性,这使得这样做是一个合理的理由。其他语言可能会选择不同的方法。
s
sw123456

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

https://i.stack.imgur.com/whCZm.png

因此,对象的数据被绑定到对象。下面是一个示例,说明您可能希望如何可视化每个对象的数据可能看起来的样子。请注意“self”是如何替换为对象名称的。我并不是说下面的这个示例图是完全准确的,但它希望能够用于可视化 self 的使用。

https://i.stack.imgur.com/5daR6.png

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

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

请参见下图:

https://i.stack.imgur.com/oZZe5.png


嘿,当通过“bob.name()”访问 Bob 的属性时,您实际上是从“init”访问 bob().self.name 的,对吗?
当您在上面的注释中编写 bob.name() 时,您是在暗示 bob 有一个名为 name() 的方法,因为您在 name 后添加了括号。然而,在这个例子中没有这样的方法。 “bob.name”(没有括号)直接从 init(构造函数)方法访问名为 name 的属性。当 bob 的 speak 方法被调用时,它是访问 name 属性并在 print 语句中返回它的方法。希望这可以帮助。
不,你得到 self.name 的值,对于 bob 对象,它实际上是 bob.name,因为对象的名称在创建(实例化)时传递给 self 参数。再次,希望这会有所帮助。如果有,请随意对主要帖子进行投票。
在实例化时将名称分配给 self.name。创建对象后,属于该对象的所有变量都是以“self”为前缀的变量。请记住,当从类创建对象时,self 被替换为对象的名称。
这就是你解释东西的方式!不错的工作 :)
k
kame

我喜欢这个例子:

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: []

所以没有 self 的 vars 只是类的静态变量,就像在 java 中一样
泰迪泰迪,你不完全正确。行为(静态或非静态)不仅取决于 self,还取决于变量类型。尝试用简单的整数而不是列表来做第一个例子。结果会完全不同。
实际上,我的问题是为什么你可以在第一个例子中说 a.foo 而不是 A.foo?显然 foo 属于该类...
您可以从大多数语言的对象实例中调用静态成员。为什么这令人惊讶?
@RadonRosborough 因为在第一个示例中,ab 都是 A()(类)的标签(或指针)。 a.foo 引用 A().foo 类方法。但是,在第二个示例中,a 成为对 A()instance 的引用,b 也是如此。现在它们是实例而不是类对象本身,self 允许 foo 方法对实例进行操作。
n
ninjagecko

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

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 )

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


我希望 Python 和 Ruby 一样对处理程序进行糖衣。
C
Community

以下摘录来自 Python documentation about self

与在 Modula-3 中一样,[在 Python 中] 没有用于从其方法中引用对象成员的简写:方法函数使用表示对象的显式第一个参数声明,该参数由调用隐式提供。通常,方法的第一个参数称为 self。这只不过是一个约定:名称 self 对 Python 绝对没有特殊含义。但是请注意,如果不遵循约定,您的代码对于其他 Python 程序员的可读性可能会降低,并且还可以想象编写依赖于这种约定的类浏览器程序。

有关详细信息,请参阅 Python documentation tutorial on classes


R
Ry-

除了已经说明的所有其他原因之外,它还允许更轻松地访问被覆盖的方法;您可以致电 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"

C
ChickenFeet

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


类 myClass: def myFunc(this, name): this.name = name
r
rassa45

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

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

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

然而,一个对象方法,它需要你创建一个变量,它是一个 Animal,在这种情况下,需要 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()
你说的不是100%真实的。那只是一个约定。您仍然可以从创建的对象调用静态方法。您将无法使用任何类成员,因为您没有声明自我。我什至可以调用 Animal.objectMethod(animalObj) 来调用非静态的。基本上这意味着静态方法只是不使用成员变量的方法。不应该有任何需要声明自我。我认为这是一个愚蠢的语言要求。 Lua 和 C++ 等语言在幕后为您提供 obj 变量。
你做了一个无用的 animalName 字符串声明和崩溃的 animalName 方法。
@ytpillai 无关紧要。不应将令人困惑和不正确的代码作为答案。
def getAnimalName 不破坏您尝试返回的字符串,并且 self 指的是类的实例,而不是其中的任何字段。
A
Akash Kandpal

首先,self 是一个传统名称,你可以用其他任何东西(连贯)来代替它。

它指的是对象本身,因此当您使用它时,您声明 .name 和 .age 是您要创建的 Student 对象(注意,不是 Student 类)的属性。

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

Code is here


M
Ming-Tang

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


R
Ry-

它遵循 Python 禅宗“显式胜于隐式”。它确实是对您的类对象的引用。例如,在 Java 和 PHP 中,它称为 this

如果 user_type_name 是您模型上的一个字段,您可以通过 self.user_type_name 访问它。


u
user441521

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


B
Bugs Buggy

使用通常称为 self 的参数并不难理解,为什么它是必要的?或者为什么明确提到它?我想,对于大多数查找这个问题的用户来说,这是一个更大的问题,或者如果不是,他们在学习 python 时肯定会有同样的问题。我建议他们阅读以下几篇博客:

1: Use of self explained

请注意,它不是关键字。

每个类方法(包括 init)的第一个参数始终是对当前类实例的引用。按照惯例,这个参数总是命名为 self。在init方法中,self指的是新创建的对象;在其他类方法中,它指的是调用其方法的实例。例如下面的代码与上面的代码相同。

2: Why do we have it this way and why can we not eliminate it as an argument, like Java, and have a keyword instead

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

代码示例:

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 参数是强制性的。


k
kmario23

看看下面的例子,它清楚地解释了 self 的用途

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 用于/需要区分实例。

来源:self variable in python explained - Pythontips


是的,我想我们知道为什么要使用 self,但问题是为什么语言会让你明确声明它。许多其他语言不需要这个,而一种以简洁而自豪的语言,你会认为它们只会在幕后为你提供变量,以便像 Lua 或 C++(this)那样使用。
@kmario23 您的回复来自这里:pythontips.com/2013/08/07/the-self-variable-in-python-explained在发布您自己的答案时,请始终承认原始作者。
s
skyking

是因为按照 python 的设计方式,替代方案几乎不起作用。 Python 旨在允许在隐式 this (a-la Java/C++) 或显式 @ (a-la ruby) 都不起作用的上下文中定义方法或函数。让我们举一个使用 python 约定的显式方法的例子:

def fubar(x):
    self.x = x

class C:
    frob = fubar

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

隐含的方法是

def fubar(x)
    myX = x

class C:
    frob = fubar

这意味着 myX 将被解释为 fubar(以及 frob)中的局部变量。这里的替代方法是使用替换的本地范围执行方法,该范围在调用之间保留,但这会消除方法局部变量的可能性。

不过目前的情况还不错:

 def fubar(self, x)
     self.x = x

 class C:
     frob = fubar

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


C
Community

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

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

您也应该看看 cls,以了解全局。此 post 可能会有所帮助。


s
sameer_nubia

self 就像当前对象名称或 class 实例一样。

# Self explanation.


 class classname(object):

    def __init__(self,name):

        self.name=name
        # Self is acting as a replacement of object name.
        #self.name=object1.name

   def display(self):
      print("Name of the person is :",self.name)
      print("object name:",object1.name)


 object1=classname("Bucky")
 object2=classname("ford")

 object1.display()
 object2.display()

###### Output 
Name of the person is : Bucky
object name: Bucky
Name of the person is : ford
object name: Bucky

p
prosti

自我是不可避免的。

只有一个 question 应该是 self 是隐式的或显式的。 Guido van Rossumself has to stay 解决了这个问题。

那么自我住在哪里呢?

如果我们只坚持函数式编程,我们就不需要 self。输入 Python OOP 后,我们会在其中找到 self

这是使用方法 m1 的典型用例 class C

class C:
    def m1(self, arg):
        print(self, ' inside')
        pass

ci =C()
print(ci, ' outside')
ci.m1(None)
print(hex(id(ci))) # hex memory address

该程序将输出:

<__main__.C object at 0x000002B9D79C6CC0>  outside
<__main__.C object at 0x000002B9D79C6CC0>  inside
0x2b9d79c6cc0

所以 self 保存了类实例的内存地址。 self 的目的是保存实例方法的引用,并让我们显式访问该引用。

请注意,有三种不同类型的类方法:

静态方法(阅读:函数),

类方法,

实例方法(提到)。


R
Rahul Jha

“self”关键字包含类的引用,是否使用它取决于您,但如果您注意到,每当您在 python 中创建新方法时,python 都会自动为您编写 self 关键字。如果您进行一些研发,您会注意到,如果您在一个类中创建两个方法并尝试在另一个内部调用一个,除非您添加自我(类的引用),否则它不会识别方法。

class testA:
def __init__(self):
    print('ads')
def m1(self):
    print('method 1')
    self.m2()
def m2(self):
    print('method 2')

下面的代码会引发无法解决的引用错误。

class testA:
def __init__(self):
    print('ads')
def m1(self):
    print('method 1')
    m2()  #throws unresolvable reference error as class does not know if m2 exist in class scope
def m2(self):
    print('method 2')

现在让我们看看下面的例子

class testA:
def __init__(self):
    print('ads')
def m1(self):
    print('method 1')
def m2():
    print('method 2')

现在,当您创建类 testA 的对象时,您可以使用这样的类对象调用方法 m1(),因为方法 m1() 已包含 self 关键字

obj = testA()
obj.m1()

但是如果你想调用方法 m2(),因为它没有自引用,所以你可以直接使用类名调用 m2(),如下所示

testA.m2()

但是在实践中坚持使用 self 关键字,因为它还有其他好处,比如在内部创建全局变量等等。


P
PrabhavDevo

“自我”一词是指类的实例

class foo:
      def __init__(self, num1, num2):
             self.n1 = num1 #now in this it will make the perimeter num1 and num2 access across the whole class
             self.n2 = num2
      def add(self):
             return self.n1 + self.n2 # if we had not written self then if would throw an error that n1 and n2 is not defined and we have to include self in the function's perimeter to access it's variables

S
SilentGhost

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


我不认为这有助于richzilla 理解其背后的原因。
@SilentGhost:你已经搞定了。我很佩服。如果我理解正确:我确实创建了一个对象作为已定义类的实例,并且 self 参数引用了该对象?我理解 self 以隐含的方式指代班级本身,但如果您能多解释一下您的答案,那就太好了。
l
laxman

docs

方法的特殊之处在于实例对象作为函数的第一个参数传递。在我们的示例中,调用 xf() 完全等同于 MyClass.f(x)。通常,使用包含 n 个参数的列表调用方法等效于使用参数列表调用相应的函数,该参数列表是通过在第一个参数之前插入方法的实例对象而创建的。

在此之前的相关片段,

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

x = MyClass()


R
Rishi

我会说至少对于 Python,self 参数可以被认为是一个占位符。看看这个:

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

p1 = Person("John", 36)

print(p1.name)
print(p1.age)

在这种情况下,Self 和许多其他方法被用作存储名称值的方法。然而,在那之后,我们使用 p1 将它分配给我们正在使用的类。然后当我们打印它时,我们使用相同的 p1 关键字。

希望这对 Python 有所帮助!


s
saran

我的小 2 美分

在这个 Person 类中,我们用 self 定义了 init 方法,这里要注意的是 self 和实例变量 p 相同<__main__.Person object at 0x106a78fd0>

class Person:

    def __init__(self, name, age):
        self.name = name 
        self.age = age 

    def say_hi(self):
        print("the self is at:", self)
        print((f"hey there, my name is {self.name} and I am {self.age} years old"))

    def say_bye(self):
        print("the self is at:", self)
        print(f"good to see you {self.name}")

p = Person("john", 78)
print("the p is at",p)
p.say_hi()  
p.say_bye() 

所以如上所述,self 和 instance 变量都是同一个对象。