面向对象编程
在面向对象的编程中,有两个重要概念:类和对象;
类class:同一事物的抽象,像一个描述实例的模板,比如汽车类包含了各种具体的现实中的汽车;
对象Object:某个类的具体实例instance,是看得见摸得着的,比如我家的那辆汽车,在这里注意一下,我个人认为在python中,这个对象应当指实例,python是一切皆对象的,类本质上也是一个对象,实例也是一个对象,但为了更官方地描述,在后面的部分,我依然把实例称作对象
类和对象的关系:假设有这样一个语句a=7
,数字7实际上是int类型的一个对象,使用type()就能查看当前对象所属的类,有一句我认为很重要的话,它将在以后的例子里体现出来:类的变量可以供对象共享,对象的变量不能给其他对象共享,正所谓类是共通的,实例之间有所差异;
这里插入一个有意思的哲学问题,有点类似鸡生蛋,还是蛋生鸡:你认为是先有类还是先有实例?我们知道,类是实例的模板,这样来看确实是先有类;但是我们想想人类的思考,我们往往是先看到很多实例,然后才会想着分类,因此,个人觉得应该是先有实例后有对象;
如果有学过OWL语言,其实也会看到面向对象的思想:OWL语言分为schema层和instance层,类似类与对象的思想
下面有一些常用操作,便于我们实际体会类和对象:
1.查看对象所属的类:type([]),type({})
2.判断对像是否是类的实例:isinstance(6,int),isinstance('python',str)
3.判断类是否是子类:issubclass()
类的设计与创建
首先要知道,类有两大成员,一个是变量,一个是方法(即函数),变量又分为类变量和实例变量,方法分为实例方法,类方法,类静态方法;在方法中可能会遇到两个关键字:self和cls,self指向实例,cls指向类本身;下面设计一个学生类进行细致分析:
#学生类的设计
class Student(object):
#类成员变量
student_totalnum=0#班级总人数
student_graduated=0#毕业人数
student_namelist=[]#班级人员名单
student_graduated_namelist=[]#班级已毕业人名
#init前后各自两条下划线,代表这是一个魔法函数,是魔法函数中的初始化函数,在创建实例的同时自动调用,self是指向实例本身的对象
#self.name代表这是实例的变量,__init__则是实例的方法(在类下直接以def定义的函数)
#叫做实例方法的原因:调用必须依赖实例
def __init__(self,name,age,gender):
#实例的成员变量:实例独自拥有
self.name=name
self.age=age
self.gender=gender
self.examnum=0#学生实例的考试次数
self.totalscore=0#学生实例的总分数
#类的成员变量:同类实例共享
Student.student_totalnum=Student.student_totalnum+1
#注意append返回值为None
Student.student_namelist.append(name)
class Student(object)
代表Student类继承自基类object,直接在类下定义的变量就是类变量,正如之前那句话,类变量是实例共享的,也就是说用这个类创建的实例,它们的类变量及其值将是一样的;
在类中不用任何装饰器定义的函数都是实例函数,必须通过实例才能调用,实例函数必须携带self参数,关于__init__,可能会有些觉得奇怪,看到双下划线,证明这是魔法函数,所谓魔法函数就是一些功能比较奇特的函数,但它仍然是一个实例方法,只不过调用会在创建实例时自动执行,比如我进行实例化:
#将类实例化
baijingyi=Student('baijingyi',20,'male')
hack=Student('hack',20,'male')
#参数默认传递给魔法函数init
在实例方法下的变量都是实例变量,实例变量由self访问,类变量由cls或类名访问;
下面在类中补充一个实例方法:
#实例方法
def exam(self,score):
if score<60:
return 'fail'
else:
self.examnum=self.examnum+1
self.totalscore=self.totalscore+score
#若学生examnum>3,则列入毕业名单
if self.examnum>3 and self.name not in Student.student_graduated_namelist:
Student.student_graduated_namelist.append(self.name)
通过实例调用实例方法:
#通过实例调用实例方法
baijingyi.exam(99)
可以看出各个实例的变量是互不干扰的;
如果考试合格次数emamnum超过3次,就可以进入毕业生名单:
类中的关键字和装饰器
还是重复一次上面说过的,类中有两个重要的关键字self和cls,self指向实例(仅在实例方法参数列表中),cls指向类(仅在类方法参数列表中);
除此之外,还有两个重要装饰器:@classmethod和@staticmethod:
1.如果使用了@classmethod修饰的方法就成为类方法,参数必须包含cls;
2.如果使用了@staticmethod修饰的方法就成为类静态方法,类静态方法的参数列表中一定不能出现self和cls;
实例方法必须用实例调用,类方法和类静态方法既可以用实例调用也可以用类调用;
就在学生类的基础上,我再增加一个类方法用于返回毕业学生名单:
@classmethod
def check_graduated(cls):
return cls.student_graduated_namelist
调用该方法时就可以直接通过类调用,或者还是依旧用实例调用:
对于类静态方法,参数列表中不能存在cls和self,如果确实需要用到类成员变量,只能使用类名访问:
class PP():
string='im'
class People():
string="people"
#类静态方法
@staticmethod
def print_str():
print("python"+People.string+PP.string)
#调用可以使用类或者实例
People.print_str()
xiaoming=People()
xiaoming.print_str()
观察结果:
发现了一个问题,目前看来,python的安全性是很低的,只要想访问变量,完全没有阻拦的办法,通过一个类名就访问到了其他类的类成员变量,因此,下面讲一个扩展部分:私有成员变量,说实话,私有成员变量对我而言没多大用处,我使用python主要是用于深度学习,对安全性要求没有这么高
私有成员变量
当在成员变量前加双下划线时,该成员就成为私有成员变量,私有成员变量也有类私有成员变量和实例私有成员变量之分,其访问规则遵循之前说的self,cls,类名访问,与之前不同的是,私有变量只能通过类内部的方法才能访问;
还是那个学生类,我现在加上一个类私有变量和实例私有变量(注意双下划线):
添加一个实例方法和类方法:
同样去调用它们,结果是确实可以访问到:
但现在,当我dir()时,将找不到这两个变量:
这说明,私有变量确实隐蔽起来了;
注意到,虽然这么说是私有了,但其实还是可以访问到,只要在类名前加下划线就行:
baijingyi._Student__myscore,Student._Student__clsmyscore
#结果返回:(1000, 999)
Python完全就是全靠自觉,实际上,我只要想添加变量,不管是类变量还是实例变量,直接添加就行:
baijingyi.param='new'#为baijingyi实例添加实例变量
Student.param='new'#为Student类添加类变量