'self'参数的目的是什么?为什么需要它?

考虑这个例子:

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

我知道self指的是MyClass的特定实例。但是为什么func必须显式包含self作为参数?为什么我们需要在方法代码中使用self?其他一些语言使这一点隐式化,或使用特殊的语法代替。


有关与语言无关的设计决策的考虑,请参阅强制显式设置this/self指针的优点是什么?

要关闭调试问题,其中OP省略了方法的self参数并获得了TypeError,请使用TypeError:方法()需要1个位置参数,但给出了2个

988941 次浏览

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

你需要使用self.的原因是Python没有使用特殊的语法来引用实例属性。Python决定以一种方式执行方法,使方法所属的实例自动成为通过,但不是收到:方法的第一个参数是调用方法的实例。这使得方法与函数完全相同,并将实际名称留给你使用(尽管self是惯例,当你使用其他东西时,人们通常会对你皱眉。)self对代码来说并不特殊,它只是另一个对象。

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

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

以下摘录来自Python留档about self

与Modula-3一样,[在Python中]没有从其方法引用对象成员的简写:方法函数使用表示对象的显式第一个参数声明,该参数由调用隐式提供。

通常,方法的第一个参数称为self。这只不过是一个约定:名称self对Python绝对没有特殊含义。然而,请注意,如果不遵循约定,您的代码对其他Python程序员的可读性可能会降低,也可以想象编写依赖于这样一个约定的类浏览器程序。

有关详细信息,请参阅Python留档教程

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

我喜欢这个例子:

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

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

class Vector:def __init__(self, x, y):self.x = xself.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_globalv = 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中编写实例方法;你编写的是类方法,它必须将实例作为第一个参数。因此,你必须显式地将实例参数放在某个地方。

我将用代码演示不使用类

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 += xdef mult(self, x):self.field *= x
s = State()s.add('added')    # self is implicitly passed ins.mult(2)         # self is implicitly passed inprint( s.field )

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

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

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

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

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

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

objectA是这个类的一个实例。

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

ClassA.methodA(objectA, arg1, arg2)

self变量指的是对象本身。

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

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

你也应该看看cls,有更大的图景。这个帖子可能会有帮助。

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

class Restaurant(object):bankrupt = False
def open_branch(self):if not self.bankrupt:print("branch opened")
#create instance1>>> x = Restaurant()>>> x.bankruptFalse
#create instance2>>> y = Restaurant()>>> y.bankrupt = True>>> y.bankruptTrue
>>> x.bankruptFalse

self用于/需要区分实例。

来源:python中的self变量解释-PythonTips

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

在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 constructordef Animal(self):self.animalName = "";

def getAnimalName(self):return self.animalName

在这种情况下,self引用的是整个类的动物名称变量。记住:如果你在方法中有一个变量,self将不起作用。该变量仅在该方法运行时存在。为了定义字段(整个类的变量),你必须在类方法之外定义它们。

如果你一个字都听不懂,那就去谷歌一下“面向对象编程”。一旦你明白了这一点,你甚至不需要问那个问题:)。

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

输入图片描述

正因为如此,对象的数据被绑定到对象上。下面是一个你如何可视化每个对象的数据可能看起来的例子。请注意“self”是如何被替换为对象名称的。我并不是说下面的示例图完全准确,但希望它能在可视化self的使用方面有所帮助。

输入图片描述

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

尽管这可能不完全准确,但想想实例化一个对象的过程:当一个对象被创建时,它将该类用作自己数据和方法的模板。如果不将自己的名称传入self参数,类中的属性和方法将保持为通用模板,不会被引用(属于)该对象。因此,通过将对象的名称传入self参数,这意味着如果一个类实例化了100个对象,它们都可以跟踪自己的数据和方法。

请看下面的插图:

输入图片描述

是因为按照python的设计方式,替代方案几乎不起作用。Python旨在允许在隐式this(a-laJava/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相同)。

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

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

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

class Student:#called each time you create a new Student instancedef __init__(self,name,age): #special method to initializeself.name=nameself.age=age
def __str__(self): #special method called for example when you use printreturn "Student %s is %s years old" %(self.name,self.age)
def call(self, msg): #silly example for custom methodreturn ("Hey, %s! "+msg) %self.name
#initializing two instances of the student classbob=Student("Bob",20)alice=Student("Alice",19)
#using themprint bob.nameprint bob.ageprint alice #this one only works if you define the __str__ methodprint alice.call("Come here!") #notice you don't put a value for self
#you can modify attributes, like when alice agesalice.age=20print alice

代码在这里

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

1:使用自解释

请注意,它不是关键字。

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

2:为什么我们有这样的方式,为什么我们不能消除它作为一个参数,像Java,而有一个关键字

我想补充的另一件事是,可选的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参数是强制性的。

self是不可避免的。

只有一个问题应该self是隐式或显式的。#0解决了这个问题,说#1必须留下来

self住在哪里?

如果我们坚持函数式编程,我们就不需要self。一旦我们进入python面向对象,我们在那里找到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>  inside0x2b9d79c6cc0

所以self保存类实例的内存地址。self的目的是保存实例方法的引用,并让明确访问该引用。


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

  • 静态方法(读取:函数),
  • 类方法,
  • 实例方法(提到)。

self充当当前对象名称或类实例。

# 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()
###### OutputName of the person is : Buckyobject name: BuckyName of the person is : fordobject name: Bucky

文档

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

在此之前的相关片段,

class MyClass:"""A simple example class"""i = 12345
def f(self):return 'hello world'

x=MyClass()

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

class Person:def __init__(self, name, age):self.name = nameself.age = age
p1 = Person("John", 36)
print(p1.name)print(p1.age)

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

希望这对Python有帮助!

“self”关键字包含类的引用,你是否想使用它取决于你,但是如果你注意到,每当你在python中创建一个新方法时,python会自动为你编写self关键字。如果你做一些研发,你会注意到,如果你在一个类中创建了两个方法,并试图在另一个中调用一个,除非你添加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 scopedef 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(),因为is没有self引用,所以你可以像下面这样直接使用类名调用m2()

testA.m2()

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

我的小2美分

在Person这个类中,我们用self定义了init方法,有趣的是这里自我和实例变量p的内存位置是相同的<__main__.Person object at 0x106a78fd0>

class Person:
def __init__(self, name, age):self.name = nameself.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和实例变量都是同一个对象。

“self”这个词指的是一个类的实例

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 classself.n2 = num2def 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