读懂python中的self
Posted 江枫对愁眠-DY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读懂python中的self相关的知识,希望对你有一定的参考价值。
在Python类中规定,函数的第一个参数是实例对象本身,无论是显式创建类的构造方法,还是向类中添加实例方法,都要求将 self 参数作为方法的第一个参数,并且约定俗成,把其名字写为self。例如定义一个Chinese类:
class Chinese:
country = \'China\'
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print(self, \'is talking Chinese\')
if __name__ == \'__main__\':
DY = Chinese("DY", "18")
DY.talk()
但Python中并没有规定该参数的具体名称,之所以将其命名为 self,只是程序员之间约定俗成的一种习惯,遵守这个约定,可以使我们编写的代码具有更好的可读性(大家一看到 self,就知道它的作用)。
1.类的实例方法中self存在必要性
从实例与类关系来说
self 参数的具体作用是什么呢?打个比方,如果把类比作造房子的图纸,那么类实例化后的对象是真正可以住的房子。根据一张图纸(类),我们可以设计出成千上万的房子(类对象),每个房子长相都是类似的(都有相同的类变量和类方法),但它们都有各自的主人,那么如何对它们进行区分呢?
当然是通过 self 参数,它就相当于每个房子的门钥匙,可以保证每个房子的主人仅能进入自己的房子(每个类对象只能调用自己的类变量和类方法)。
也就是说同一个类可以产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法,换句话说,Python 会自动绑定类方法的第一个参数指向调用该方法的对象。如此,Python解释器就能知道到底要操作哪个对象的方法。
因此得出结论:无论是创建类的构造方法还是实例方法,最少要包含一个参数self。
通过实例的self参数与对象进行绑定,程序在调用实例方法和构造方法时,也不需要手动为第一个参数传值。所以我们在调用成员方法时可以写成:
class Chinese: country = \'China\' def __init__(self, name, age): self.name = name self.age = age def talk(self): print(self, \'is talking Chinese\') if __name__ == \'__main__\': DY = Chinese("DY", "18") DY.talk()
从实例方法存储内存空间来讲
对象在实例化的时候,构造函数会开辟一块内存空间,把属性存到DY那个对象那里,实例方法还是存储在类的方法区,对象直接去引用就行,原因在于copy出来函数执行代码效果是不变的。实例化出来的对象如果想调用类的方法,就需要到Chinese类中去取。
但有个问题,如果不告诉类是谁在调用实例方法,那么调用方法产生的结果不知道返回给哪个对象。因此我们在调用Chinese. talk(self)的时候应该告诉类是哪个对象在调用,所以最简单的办法是把对象传进去。那么调用实例方法时还可以写成:
class Chinese: country = \'China\' def __init__(self, name, age): self.name = name self.age = age def talk(self): print(self, \'is talking Chinese\') if __name__ == \'__main__\': DY = Chinese("DY", "18") Chinese.talk(DY)
所以在调用实例方法时,都需要把self代表的对象传进去,因此这个self就是谁调用这个方法,表示的就是哪个对象。
2. self是谁:指实际调用该方法的对象
上面说到类的实例方法(函数)中一定要有传入一个参数self,把实例方法与调用该方法的对象进行绑定,那么怎么证明self指的就是该对象呢?
class Chinese: country = \'China\' def __init__(self, name, age): self.name = name self.age = age def talk(self): print(self, \'is talking Chinese\') if __name__ == \'__main__\': DY = Chinese("DY", "18") # 把一个类变成一个具体对象的过程叫类的实例化 zhang = Chinese("zhang", "18") print(DY) # 打印对象,返回对象所在的内存地址 print(zhang) DY.talk() # 打印对象的实例方法,获取self的值 zhang.talk() 执行结果: <__main__.Chinese object at 0x000001C718A1DA88> <__main__.Chinese object at 0x000001C718A1FF08> <__main__.Chinese object at 0x000001C718A1DA88> is talking Chinese <__main__.Chinese object at 0x000001C718A1FF08> is talking Chinese
通过执行结果我们可以看出:
①打印对象DY返回的结果<__main__.Chinese object at 0x00000269E594EEB8> ,0x00000269E594EEB8指的是该对象所在的内存地址,与通过对象调用实例方法(DY.talk()),打印的self的值0x00000269E594EEB8一致,可以看出他们指向的是同一内存地址,因此可得出结论:在当前实例化中self代表是对象DY。
②调用实例方法DY.talk()与zhang.talk()打印出self的内存地址分别为0x000001C718A1DA88和0x000001C718A1FF08,也就是说不同实例方法self的并不是同一个对象,DY.talk()中self指的是DY对象,zhang.talk()中self指的是zhang对象。
综合以上得出:self指的是实际调用该方法的对象。
3. self不必非写成self
上面代码self更改为this:
class Chinese: country = \'China\' def __init__(this, name, age): this.name = name this.age = age def talk(this): print(this, \'is talking Chinese\') if __name__ == \'__main__\': DY = Chinese("DY", "18") DY.talk()
改成this后,运行结果完全一样。我们只是使用self代指调用方法的对象,完成对象和实例方法的第一个参数进行绑定,至于实例方法的第一个参数写成a,b,c.....或者this完全不受影响。
当然,最好还是尊重约定俗成的习惯,使用self。
4. self可以不写吗
我们试下实例方法(函数)中不传入参数self:
class Chinese: def talk(): print("我热爱我的祖国") if __name__ == \'__main__\': DY = Chinese() DY.talk() 执行结果: Traceback (most recent call last): File "test.py", line 577, in <module> DY.talk() TypeError: talk() takes 0 positional arguments but 1 was given
执行结果的意思是:TypeError: talk()接受0个位置参数,但给出了1个。但是我们在调用实例方法的时候并没有给函数传入参数,给出的这1个参数是哪里来的?
上面我们有说到实例方法与类的静态属性(实例变量)不同,实例变量会在实例化对象时存储在对象的内存区间,但实例方法是存储在类的方法区,只有实例方法被调用的时候才会被加载,所以我们在执行DY.talk()时,Python解释器解释为Chinese.talk(DY),把self替换成类的实例,这样就解释了上面的问题,给出的这一个位置参数就是self,也就是调用方法的对象。
所以,实例方法中必须要传入一个形参,不可以不写self。
5. 实例方法中的变量什么时候前缀加self
类中的变量分为类变量、实例变量、局部变量。类变量就是定义在方法(函数)外的变量,在方法中可以使用类名进行调用,在类方法中可以使用cls.变量名进行调用,相对来说好做区分。实例变量和局部变量都是存在于方法中(函数内),那么大家肯定会有个疑问:变量前什么时候加self(实例变量),什么时候不加self(局部变量)?或者为了以防万一变量前全都加self,如下:
class TestLogin(unittest.TestCase): def test_login(self): self.url = xxx self.resp = self.session.get(self.url) self.text = self.resp.text self.status = self.resp.statuscode
但这样很明显没有意义,url/resp/text/status这些变量都是局部的,别的方法里面不需要访问这些变量,只存在于test_login函数中,别的用例也不需要使用这些变量,因此除了session属性需要共用以外,其他变量前不需要加self。
我们只需记住:前缀带self的变量,就是在整个类的代码块里面类似是作为全局变量,如果变量前面加了self,那么在任何实例方法(非staticmethod和calssmethod)就都可以访问这个变量了,如果没有加self,只有在当前函数内部才能访问这个变量。
总结
- self在定义实例方法时需要传入该参数,但是在调用时会自动传入。
- self的名字并不是规定死的,但是最好还是按照约定是用self。
- self总是指调用该方法的实例。
- 实例方法中需要在全局引用的变量需要加前缀self。
以上是关于读懂python中的self的主要内容,如果未能解决你的问题,请参考以下文章