`self` 参数的用途是什么?为什么需要它?
- 2024-11-15 08:37:00
- admin 原创
- 19
问题描述:
考虑这个例子:
class MyClass:
def func(self, name):
self.name = name
我知道self
指的是 的特定实例MyClass
。但为什么必须func
明确包含self
为参数?为什么我们需要self
在方法的代码中使用?其他一些语言将其隐式化,或使用特殊语法代替。
对于与语言无关的设计决策考虑,请参阅强制显式指定 this/self 指针有什么好处?。
要关闭OP 省略方法参数并得到 的调试问题,请使用TypeError:method() 需要 1 个位置参数,但实际给出了 2 个。如果 OP在方法主体中省略并得到,请考虑如何在类中调用函数?。self
`TypeErrorself.
NameError`
解决方案 1:
您需要使用的原因self.
是因为 Python 不使用特殊语法来引用实例属性。Python 决定以一种方式执行方法,使方法所属的实例自动传递,但不自动接收:方法的第一个参数是调用该方法的实例。这使得方法与函数完全相同,并将实际名称留给您使用(尽管这self
是惯例,当您使用其他名称时,人们通常会对您皱眉头。)self
对代码来说并不特殊,它只是另一个对象。
Python 本来可以做些别的事情来区分普通名称和属性——像 Ruby 那样使用特殊语法,或者像 C++ 和 Java 那样要求声明,或者可能还有其他更不同的东西——但它没有这样做。Python 的全部目的是使事物明确,使什么是什么显而易见,虽然它并不是在所有地方都这样做,但它确实为实例属性做了这件事。这就是为什么分配给实例属性需要知道要分配给哪个实例,这就是为什么它需要self.
。
解决方案 2:
假设您有一个类ClassA
,其中包含如下定义的方法methodA
:
class ClassA:
def methodA(self, arg1, arg2):
... # do something
并且objectA
是此类的一个实例。
现在,当objectA.methodA(arg1, arg2)
被调用时,python 会在内部将其转换为:
ClassA.methodA(objectA, arg1, arg2)
变量self
指的是对象本身。
解决方案 3:
让我们以一个简单的向量类为例:
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)
所以整个结构保持不变。我该如何利用这一点?如果我们假设我们没有length
为我们的Vector
类编写方法,我们可以这样做:
Vector.length_new = length_global
v = Vector(3, 4)
print(v.length_new()) # 5.0
这是可行的,因为 的第一个参数length_global
可以重新用作self
中的参数length_new
。如果没有显式的 ,这是不可能的self
。
理解显式的必要性的另一种方法self
是查看 Python 在哪里添加了一些语法糖。请记住,基本上,像这样的调用
v_instance.length()
内部转换为
Vector.length(v_instance)
很容易看出它self
适合于哪里。你实际上并没有在 Python 中编写实例方法;你编写的是类方法,它必须将实例作为第一个参数。因此,你必须将实例参数明确地放在某处。
解决方案 4:
当对象被实例化时,对象本身被传递到self
参数中。
因此,对象的数据与对象绑定。下面是一个示例,展示了如何可视化每个对象的数据。请注意,如何self
用对象的名称替换。我并不是说下面的示例图完全准确,但希望它能帮助人们直观地了解 的使用self
。
将对象传递到self
参数中,以便对象可以保留其自己的数据。
虽然这可能并不完全准确,但请想象一下实例化(创建和分配内部值)对象的过程:创建对象时,该对象将类用作其(对象)自身数据和方法的模板。如果不将对象自己的变量名称传递给参数self
,类中的属性和方法将仍然是通用模板,不会被引用(属于)对象。因此,通过将对象的名称传递给参数self
,这意味着如果从一个类实例化 100 个对象,则这 100 个对象中的每一个都可以跟踪其(每个对象)自己的数据和方法。
摘要:类是通用的(不是超特定的)模板,新创建的对象可以将其自己的特定数据传递进去(而不一定影响可以从同一个类创建的其他对象),从而减少复制粘贴代码的次数,以创建具有相同模式的许多对象。self
用于指定应使用特定对象的数据而不是其他数据。
参见下图:
解决方案 5:
我喜欢这个例子:
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 # []
解决方案 6:
我将使用不使用类的代码进行演示:
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 )
[将我的答案从重复的封闭式问题中迁移出来]
解决方案 7:
以下摘录自有关 self 的 Python 文档:
与 Modula-3 一样,[在 Python 中] 没有用于从其方法引用对象成员的简写:方法函数用表示对象的显式第一个参数声明,该参数由调用隐式提供。
通常,方法的第一个参数称为 self。这只不过是一种惯例:名称 self 对 Python 来说绝对没有任何特殊含义。但请注意,如果不遵循惯例,您的代码对其他 Python 程序员来说可能不太可读,而且可以想象,类浏览器程序可能会依赖于这种惯例来编写。
有关更多信息,请参阅有关类的 Python 文档教程。
解决方案 8:
除了已经陈述的所有其他原因之外,它允许更轻松地访问重写的方法;您可以调用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"
解决方案 9:
它的使用类似于this
Java中关键字的使用,即赋予当前对象的引用。
解决方案 10:
与 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
一个新变量(称为局部变量),则不起作用。该变量仅在该方法运行时存在。要定义字段(整个类的变量),您必须在类方法之外定义它们。
如果你对我所说的一点儿都不理解,那么就谷歌一下“面向对象编程”。一旦你理解了这一点,你就不需要问这个问题了 :)。
解决方案 11:
首先,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
代码在这里
解决方案 12:
self
是对对象本身的对象引用,因此,它们是相同的。Python 方法不是在对象本身的上下文中调用的。self
在 Python 中可能用于处理自定义对象模型或其他东西。
解决方案 13:
它的存在是为了遵循 Python 的“显式优于隐式”原则。它实际上是对类对象的引用。例如,在 Java 和 PHP 中,它被称为this
。
如果user_type_name
是模型上的一个字段,您可以通过 访问它self.user_type_name
。
解决方案 14:
我很惊讶没有人提到 Lua。Lua 也使用“self”变量,但是可以省略它但仍然可以使用。C++ 对“this”也做了同样的事情。我认为没有必要在每个函数中声明“self”,但您仍然可以像使用 lua 和 C++ 一样使用它。对于一种以简洁为荣的语言来说,要求您声明 self 变量很奇怪。
解决方案 15:
看看下面的例子,它清楚地解释了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
用于/需要区分实例。
来源:python 中 self 变量详解 - Pythontips
解决方案 16:
习惯上调用的参数的用法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
参数是强制性的。
解决方案 17:
这是因为按照 Python 的设计方式,替代方案几乎行不通。Python 的设计允许在隐式this
(Java/C++)或显式@
(ruby)都行不通的上下文中定义方法或函数。让我们举一个使用 Python 约定的显式方法的示例:
def fubar(x):
self.x = x
class C:
frob = fubar
现在该fubar
函数将无法工作,因为它会假设这self
是一个全局变量(并且frob
也是 in)。替代方法是使用替换的全局范围(self
对象所在的位置)执行方法。
隐式方法是
def fubar(x)
myX = x
class C:
frob = fubar
这意味着myX
将被解释为 in 中的局部变量fubar
(以及 in 中的frob
局部变量)。此处的替代方法是使用在调用之间保留的替换局部范围来执行方法,但这将消除方法局部变量的可能性。
但目前的情况还算顺利:
def fubar(self, x)
self.x = x
class C:
frob = fubar
这里当作为方法调用时frob
将通过参数接收调用它的对象self
,并且fubar
仍然可以使用对象作为参数进行调用并执行相同的工作(与我的想法相同)C.frob
。
解决方案 18:
在该__init__
方法中,self指的是新创建的对象;在其他类方法中,它指的是调用其方法的实例。
self 作为一个名称,只是一个约定,随心所欲地称呼它!但是在使用它时,例如删除对象,您必须使用相同的名称:__del__(var)
,其中var
在__init__(var,[...])
你也应该看一下cls
,以了解整体情况。这篇文章可能会有所帮助。
解决方案 19:
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()
###### Output
Name of the person is : Bucky
object name: Bucky
Name of the person is : ford
object name: Bucky
解决方案 20:
“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 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 关键字,因为它还有其他好处,比如在内部创建全局变量等等。
解决方案 21:
self
是不可避免的。
刚才有个问题应该self
是隐含的还是明确的。Guido van Rossum
解决了这个问题就说self
必须保留。
那么他们self
住在哪里?
如果我们只坚持函数式编程,我们就不需要self
。一旦我们进入Python OOP,我们就会发现self
。
class C
以下是该方法的典型用例m1
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
是保存实例方法的引用,并让我们能够显式访问该引用。
请注意,有三种不同类型的类方法:
静态方法(即函数),
类方法,
实例方法(已提到)。
解决方案 22:
“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 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
解决方案 23:
它是对类实例对象的明确引用。
解决方案 24:
来自文档,
方法的特殊之处在于实例对象作为函数的第一个参数传递。在我们的示例中,调用
x.f()
完全等同于MyClass.f(x)
。通常,使用 n 个参数列表调用方法等同于使用通过在第一个参数之前插入方法的实例对象而创建的参数列表调用相应函数。
在此之前的相关片段,
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
x = MyClass()
解决方案 25:
我的小小建议
在这个 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 和实例变量都是同一个对象。
解决方案 26:
使用self
是python 的设计决策,从语言设计的角度来看并非严格强制的,以明确实例对象并将实例变量与局部变量区分开来。您可以设想一种替代方案,其中self
始终隐式存在,正如 Guido 的博客文章所回应的那样:
class MyClass:
def __init__(n): # implicit self
self.n = n
def print_n():
print(self.n)
或者使用特殊语法来引用实例变量,就像在 Ruby 中一样,尽管 Python 试图将语法字符保持在最低限度。这在功能上与 implied 相同self
,只是您不会将其self
视为隐藏变量。
class MyClass:
def __init__(n):
@n = n
def print_n():
print(@n)
另一种选择是 C++ 风格的语法,使用隐式this
和显式数据类声明(相对较新):
class MyClass:
n: int
def __init__(n):
this.n = n
def print_n():
print(n) # default to instance variable
this
仅用于区分局部变量和实例变量。如果没有混淆,n
则使用“类作用域”。我认为这比self
到处使用显式更干净,但您需要跟踪什么是实例状态,什么不是实例状态。
解决方案 27:
我想说,至少对于 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 有帮助!
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件