Python中self用法详解

在介绍Python的self用法之前,先来介绍下Python中的类和实例…… 
我们知道,面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,比如学生这个抽象的事物,可以用一个Student类来表示。而实例是根据类创建出来的一个个具体的“对象”,每一个对象都从类中继承有相同的方法,但各自的数据可能不同。 
1、以Student类为例,在Python中,定义类如下:

class Student(object):pass
  • 1
  • 2

(Object)表示该类从哪个类继承下来的,Object类是所有类都会继承的类。

2、实例:定义好了类,就可以通过Student类创建出Student的实例,创建实例是通过类名+()实现:

student = Student() #创建一个实例
  • 1

3、由于类起到模板的作用,因此,可以在创建实例的时候,把我们认为必须绑定的属性强制填写进去。这里就用到Python当中的一个内置方法__init__方法,例如在Student类时,把name、score等属性绑上去:

class Student(object):def __init__(self, name, score):self.name = nameself.score = score
  • 1
  • 2
  • 3
  • 4

这里注意:

(1)、__init__方法的第一参数永远是self,表示创建的类实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

(2)、有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器会自己把实例变量传进去:

>>>student = Student("Hugh", 99)
>>>student.name
"Hugh"
>>>student.score
99
  • 1
  • 2
  • 3
  • 4
  • 5

另外,这里self就是指类本身,self.name就是Student类的属性变量,是Student类所有。而name是外部传来的参数,不是Student类所自带的。故,self.name = name的意思就是把外部传来的参数name的值赋值给Student类自己的属性变量self.name

4、和普通数相比,在类中定义函数只有一点不同,就是第一参数永远是类的本身实例变量self,并且调用时,不用传递该参数。除此之外,类的方法(函数)和普通函数没啥区别,你既可以用默认参数、可变参数或者关键字参数*args是可变参数,args接收的是一个tuple**kw是关键字参数,kw接收的是一个dict)。

5、既然Student类实例本身就拥有这些数据,那么要访问这些数据,就没必要从外面的函数去访问,而可以直接在Student类的内部定义访问数据的函数(方法),这样,就可以把”数据”封装起来。这些封装数据的函数是和Student类本身是关联起来的,称之为类的方法:

class Student(obiect):def __init__(self, name, score):self.name = nameself.score = scoredef print_score(self):print "%s: %s" % (self.name, self.score)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
>>>student = Student("Hugh", 99)
>>>student.print_score
Hugh: 99
  • 1
  • 2
  • 3

这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score。而如何打印,都是在Student类的内部定义的,这些数据和逻辑被封装起来了,调用很容易,但却不知道内部实现的细节。

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:

class Student(object):def __init__(self, name, score):self.__name = nameself.__score = scoredef print_score(self):print "%s: %s" %(self.__name,self.__score)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__score了:

>>> student = Student('Hugh', 99)
>>> student.__name
Traceback (most recent call last):File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
  • 1
  • 2
  • 3
  • 4
  • 5

这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name和get_score这样的方法:

class Student(object):...def get_name(self):return self.__namedef get_score(self):return self.__score
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果又要允许外部代码修改score怎么办?可以给Student类增加set_score方法:

class Student(object):...def set_score(self, score):self.__score = score
  • 1
  • 2
  • 3
  • 4
  • 5

需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name____score__这样的变量名。

有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

封装的另一个好处是可以随时给Student类增加新的方法,比如:get_grade:

class Student(object):...def get_grade(self):if self.score >= 90:return 'A'elif self.score >= 60:return 'B'else:return 'C'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

同样的,get_grade方法可以直接在实例变量上调用,不需要知道内部实现细节:

>>> student.get_grade()
'A'
  • 1
  • 2

6、self的仔细用法 
(1)、self代表类的实例,而非类。

class Test:def ppr(self):print(self)print(self.__class__)t = Test()
t.ppr()
执行结果:
<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

从上面的例子中可以很明显的看出,self代表的是类的实例。而self.__class__则指向类。 
注意:把self换成this,结果也一样,但Python中最好用约定俗成的self。 
(2)、self可以不写吗? 
在Python解释器的内部,当我们调用t.ppr()时,实际上Python解释成Test.ppr(t),也就是把self替换成了类的实例。

class Test:def ppr():print(self)t = Test()
t.ppr()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行结果如下:

Traceback (most recent call last):File "cl.py", line 6, in <module>t.ppr()
TypeError: ppr() takes 0 positional arguments but 1 was given
  • 1
  • 2
  • 3
  • 4

运行时提醒错误如下:ppr在定义时没有参数,但是我们运行时强行传了一个参数。

由于上面解释过了t.ppr()等同于Test.ppr(t),所以程序提醒我们多传了一个参数t。

这里实际上已经部分说明了self在定义时不可以省略。

当然,如果我们的定义和调用时均不传类实例是可以的,这就是类方法。

class Test:def ppr():print(__class__)Test.ppr()运行结果:
<class '__main__.Test'>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(3)、在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。

class Parent:def pprt(self):print(self)class Child(Parent):def cprt(self):print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行结果:

<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>
  • 1
  • 2
  • 3

解释: 
运行c.cprt()时应该没有理解问题,指的是Child类的实例。 
但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。

(4)、在描述符类中,self指的是描述符类的实例

class Desc:def __get__(self, ins, cls):print('self in Desc: %s ' % self )print(self, ins, cls)
class Test:x = Desc()def prt(self):print('self in Test: %s' % self)
t = Test()
t.prt()
t.x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果如下:

self in Test: <__main__.Test object at 0x0000000002A570B8>
self in Desc: <__main__.Desc object at 0x000000000283E208>
<__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>
  • 1
  • 2
  • 3

这里主要的疑问应该在:Desc类中定义的self不是应该是调用它的实例t吗?怎么变成了Desc类的实例了呢? 
因为这里调用的是t.x,也就是说是Test类的实例t的属性x,由于实例t中并没有定义属性x,所以找到了类属性x,而该属性是描述符属性,为Desc类的实例而已,所以此处并没有顶用Test的任何方法。

那么我们如果直接通过类来调用属性x也可以得到相同的结果。

下面是把t.x改为Test.x运行的结果。

self in Test: <__main__.Test object at 0x00000000022570B8>
self in Desc: <__main__.Desc object at 0x000000000223E208>
<__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>
---------------------------------------------------------------------------------------------------------

================================================================================================

self可以理解为自己
类似于C++中的this指针,就是对象自身的意思,在用某个对象调用该方法时,就将该对象作为第一个参数传递给self

python的类中有属性、方法;其中属性可以有私有和公有之分,方法可以对私有属性进行限定和保护,类似于C#中属性的功能。

方法也有私有和公有,__方法前面两个下划线

类,具有相似内部状态和运动规律的实体的集合(统称、抽象)
具有相同属性和行为事物的统称,类是抽象的,使用时找到类的具体存在,使用这个具体存在--对象
类是对象的模板  类:类名、属性、方法

[python] view plaincopy
  1. class Dog:  
  2.     color='red' #没有下划线,类外可以通过对象访问:公有  
  3.     age=10  
  4.     __height=10 #私有属性,这个地方前面加了下划线,代表不可以在外面使用  
  5.     def setName(self,newName):  
  6.         self.name=newName  
  7.         self.__weight=20  
  8.     def printheight(self):  
  9.         print('the height is %d'%self.__height)  
  10.         print('the weight is %d'%self.__weight)  
  11.   
  12. dog=Dog()  
  13. dog.setName('Bule'#调用这个方法后才可以访问私有__weight  
  14. print(dog.color)  
  15. dog.printheight()  #属性用来保存数据,私有属性体现了封装性  
  16. #方法中的self必须为第一个参数,用来接收对象使用  
self的作用,可以在方法中将对象替换掉self
[python] view plaincopy
  1. class Person( ):  
  2.     #构造方法,在创建对象的同时默认调用  
  3.     def __init__(self):  
  4.         self.country='Chinese'  
  5.     def __del__(self):  
  6.         print('析构方法')#当删除一个对象时,默认调用析构方法  
  7.   
  8.   
  9.     __name='Lily'  
  10.     __age=12  
  11.     """docstring for ClassName"""  
  12.   
  13.     def __getName(self):#在这个方法中可以对属性进行一些限定,类似于C#中的属性功能  
  14.         return self.__name  
  15.     def __getAge(self):#对属性进行限定和保护  
  16.         if self.__age>18:  
  17.             return self.__age  
  18.         else:  
  19.             return 18  
  20.     def getNameAge(self):  
  21.         name1=self.__getName()  
  22.         age1=self.__getAge()  
  23.         print('name is %s,age is %d'%(name1,age1))  
  24.   
  25. person=Person()  
  26. person.getNameAge()  
  27. #print(p.getAge(),p.getName())#两个方法是私有的,不可以在类外面调用  
  28. del person #调用析构方法  

方法可以返回元组def test: return(100,200,200)  a,b,c=test()

[python] view plaincopy
  1. class Teacher:  
  2.     age=30  
  3.     def __init__(self):  
  4.         print('父类的构造方法')  
  5.     def showName(self,name):  
  6.         self.name=name  
  7.         print('Name is %s'%self.name)  
  8.     def showAge(self,age):  
  9.         self.age=age  
  10.         print('Age is %d'%self.age)  
  11.   
  12. class Student(Teacher ):   
  13.     #pass#子类继承父类,父类的东西子类都包含,只写pass  
  14.     def __init__(self):  
  15.         Teacher.__init__(self)  #根据这两行的先后顺序决定先执行父类还是子类方法  
  16.         print('子类重写父类的方法 ,子类的构造方法')  
  17.           
  18.     def Special(self):  
  19.         print('Student is a child')  
  20. class Doctor():  
  21.     pass  
  22.   
  23. #class pel(Teacher,Student):#多继承,有错误Cannot create a consistent method resolution,继承太多  
  24. #   pass  
  25. class pe2(Teacher,Doctor):  
  26.     pass  
  27.   
  28. teacher=Teacher()  
  29. student=Student()  
  30.   
  31. teacher.showAge(30)  
  32. teacher.showName('Mr li')  
  33. #子类的方法名与父类的方法名相同,重写。子类的对象会调用子类的  
  34. #多态的概念应用于Java和C#这一类强类型语言中,而Python崇尚‘鸭子类型’  


  • 1
  • 2
  • 3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

siwft初学(一)

今天刚開始学习swift语言。首先须要下载xcode6 beta版本号。正式版本号然后会公布。自己学习总结一下&#xff0c;假设有误。请大家指出。 创建project的时候。language选择swift语言。 swift语言比起c&#xff0c;oc很的简洁。開始真有点不适应&#xff0c;没有main函数&#…

python简单爬虫(一)

学习python前纠结了下&#xff0c;到底是应该一个个知识点吃透&#xff0c;然后写些小程序。还是应该快速掌握基础语法&#xff0c;快速实践。思考后认为前者这么学习速度真心不高&#xff0c;于是花2天时间看了下python3的语法&#xff0c;虽然很多都不明白&#xff0c;但是带…

Github远程仓库管理

1. Github 在版本控制系统中&#xff0c;大约90%的操作都是在本地仓库中进行的&#xff1a;暂存&#xff0c;提交&#xff0c;查看状态或者历史记录等等。除此之外&#xff0c;如果仅仅只有你一个人在这个项目里工作&#xff0c;你永远没有机会需要设置一个远程仓库。 只有当…

oracle 中的trunc()函数及加一个月,一天,一小时,一分钟,一秒钟方法

返回处理后的数据&#xff0c;不同于round()&#xff08;对数值进行四舍五入处理&#xff09;&#xff0c;该函数不对指定小数前或后的数值部分进行舍入处理。 语法&#xff1a;trunc(number[,decimals]) 其中&#xff0c;number为待做处理的数值&#xff0c;decimals为需要保留…

【Halcon】Halcon与OpenCV介绍、比较

from:https://blog.csdn.net/taily_duan/article/details/514997691.MVTec HALCONMVTec HALCON 是世界上最全能的机器视觉软件.世界各地的用户从HALCON为快速开发图像分析和机器视觉程序的灵活架构获益匪浅.HALCON 提供了超过1100多种具备突出性能控制器的库,如模糊分析,形态,模…

直接拿来用!最火的Android开源项目(完结篇)

直接拿来用&#xff01;最火的Android开源项目&#xff08;完结篇&#xff09; 2014-01-06 19:59 4785人阅读 评论(1) 收藏 举报 分类&#xff1a;android 高手进阶教程&#xff08;100&#xff09; 摘要&#xff1a;截至目前&#xff0c;在GitHub“最受欢迎的开源项目”系…

ABP理论学习之Web API控制器(新增)

返回总目录 本篇目录 介绍AbpApiController基类 本地化审计日志授权工作单元其他介绍 ABP通过Abp.Web.ApiNuget包集成了 ASP.NET Web API控制器。你可以像以往创建Asp.Net Web API控制器那样创建Web API控制器。依赖注入对于有规律的ApiController&#xff08;其实就是继承自Ab…

C++类构造函数初始化列表及初始化成员变量的误区

构造函数初始化列表以一个冒号开始&#xff0c;接着是以逗号分隔的数据成员列表&#xff0c;每个数据成员后面跟一个放在括号中的初始化式。例如&#xff1a;[cpp] view plaincopyclass CExample { public: int a; float b; //构造函数初始化列表 CExampl…

将centos7打造成桌面系统

前言 以下所有操作默认在root权限下执行&#xff0c;桌面环境是kde&#xff0c;使用gnome的也可以参考一下。我收集的以下要用到的一些安装包&#xff0c;360网盘http://yunpan.cn/csMhBAp92vTgN 提取码 92e2以下要用的安装软件语法&#xff1a;通过软件源在线安装&#xff1a;…

VC2010打开资源视图时提示“指南必须指定类型”,.rc资源文件损坏(转)

VC: 打开资源视图时提示“指南必须指定类型 ”(Guideline……specify type) &#xff0c;.rc资源文件损坏 可能是TFS导致的使用记事本打开.rc文件&#xff0c;找到“DESIGNINFO”节&#xff0c;会看到如下的数据&#xff1a; BEGIN , 50 , 13798327 EN…

C++ virtual 析构函数

copy自:http://zxjgoodboy.blog.sohu.com/61482463.html 在此基础上稍作修改C中虚析构函数的作用 我们知道&#xff0c;用C开发的时候&#xff0c;用来做基类的类的析构函数一般都是虚函数。可是&#xff0c;为什么要这样做呢&#xff1f;下面用一个小例子来说明&#xff1a; …

(八)企业部分之nginx+tomcat+memcached负载均衡集群搭建

【server1】vim /usr/local/lnmp/tomcat/conf/context.xml<Context>......<Manager className"de.javakaffee.web.msm.MemcachedBackupSessionManager"memcachedNodes"n1:172.25.45.1:11211,n2:172.25.45.2:11211"failoverNodes"n1"req…

泛型算法(二十三)之排列算法

1、is_permutation(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2)&#xff1a;C11版本&#xff0c;判断两个序列是否为同一元素集的两个排列。 std::vector<int> c1 {1, 2, 3, };std::vector<int> c2 {1, 2, 3, 1, 3};//判断两个序…

C++ 虚函数经典深入解析

from&#xff1a;https://blog.csdn.net/gggg_ggg/article/details/45915505C中的虚函数的作用主要是实现了多态的机制。 关于多态&#xff0c;简而言之就是用父类型别的指针指向其子类的实例&#xff0c;然后通过父类的指针调用实际子类的成员函数。 这种技术可以让父类的指针…

21OGNL与ValueStack(VS)-静态方法访问

转自&#xff1a;https://wenku.baidu.com/view/84fa86ae360cba1aa911da02.html 在LoginAction中增加如下方法&#xff1a;public static String getSta() { return "这是LoginAction中的静态方法"; } 然后在loginSuc.jsp中增加如下代码&#xff1a; 调用Action中的静…

win7通过easyBCD引导ubuntu

我电脑配置了固态和传统双硬盘&#xff0c;SSD已经装了win7&#xff0c;然后在传统硬盘上安装ubuntu&#xff0c;结果安装完成后看不到ubuntu的入口。因为跟win7不是装在一个驱动设备上&#xff0c;所以使用easyBCD的Linux&#xff0f;BCD选项也无法正确引导。最后通过easyBCD的…

深入理解C++中的explicit关键字

深入理解C中的explicit关键字kezunhaigmail.com http://blog.csdn.net/kezunhaiC中的explicit关键字只能用于修饰只有一个参数的构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的&#xff0c; 跟它相对应的另一个关键字是implicit, 意思是隐藏的,构造函数默认情况下即声…

JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结(转)

hibernate以及mybatis都有过学习&#xff0c;在java面试中也被提及问道过&#xff0c;在项目实践中也应用过&#xff0c;现在对hibernate和mybatis做一下对比&#xff0c;便于大家更好的理解和学习&#xff0c;使自己在做项目中更加得心应手。 第一方面&#xff1a;开发速度的对…

Caffe源码解析4: Data_layer

转载请注明出处&#xff0c;楼燚(y)航的blog&#xff0c;http://home.cnblogs.com/louyihang-loves-baiyan/ data_layer应该是网络的最底层&#xff0c;主要是将数据送给blob进入到net中&#xff0c;在data_layer中存在多个跟data_layer相关的类 BaseDataLayerBasePrefetchingD…

理解C++中拷贝构造函数

拷贝构造函数的功能是用一个已有的对象来初始化一个被创建的同样对象&#xff0c;是一种特殊的构造函数&#xff0c;具有一般构造函数的所有特性&#xff0c;当创建一个新对象的时候系统会自动调用它&#xff1b;其形参是本类对象的引用&#xff0c;它的特殊功能是将参数代表的…