面向对象
Posted 森林326
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象相关的知识,希望对你有一定的参考价值。
目录
一、面向过程与面向对象
二、类的定义
三、类的基本使用
四、对象的使用
五、对象属性与类属性
六、对象属性的查找顺序
七、对象访问共有部分,即访问类的属性,属性查找与绑定方法
八、小练习1
九、类型即类
十、小练习2
十一、面向对象的三大特性
十二、子类重用父类的方法
十三、组合
十四、接口与归一化设计
十五、多继承
十六、多态与多态性
十七、封装
十八、绑定方法与非绑定方法
十九、.isinstance()与issubclass()
二十、反射
二十一、内置方法
1.面向过程与面向对象的对比
#面向过程:核心"过程"二字,过程即解决问题的步骤,就是先干什么再干什么 #基于该思想写程序就好比在设计一条流水线,是一种机械式的思维方式 #优点:复杂的过程流程化,进而简单化 #缺点:扩展性差 #面向对象:核心“对象”二字,对象指特征与技能的结合体 #基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成,是一种“上帝式”的思维方式 #优点:可扩展性强 #缺点:编程复杂高,容易出现过度设计
注:面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计知识用来解决扩展性。如下图所示:
2.类的定义
#对象是特征与技能的结合体,类就是一系列对象相似的特征与技能的结合体 问:是先有对象还是先有类? 答:在现实世界中,一定是先有一个个具体存在的对象,后总结出的类 在程序中,一定保证先定义类,后产生对象
3.类的基本使用
#类体代码在类的定义阶段就会立刻执行 # 如school=\'北大’会在内存中开辟空间存放北大并将内存地址赋值给变量school class Student: school = \'北大\' #类中的特征定义 #类中的功能(技能)定义 def learn(self): #为什会有这么一个位置形参 print (\'is learning\') def choose_course(self): print (\'choose course\') #查看类属性和函数 print (Student.school) #数据属性,得到school的值 print (Student.learn) #函数属性,得到learn的内存地址 #属性存在即修改,属性不存在即创建增加 Student.country =\'China\' print (Student.country) #删除类属性 del Student.country print (Student.country) #报错,类中没有该属性 #类功能使用 print (Student.learn(\'xxx\')) #需要有实参xxx对应形参self,否则报错,
#但是类如果实例化后,就可以无需传实参给对应的self形参,见知识点7
4.对象的使用
class Student: school=\'北大\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def learn(self): print (\'is learning\') def choose_course(self): print (\'choose course\') #调用类的过程又称为实例化 #1.得到一个返回值,即对象,该对象是一个空对象 #2.触发类Student.__init__(stu1,\'lisl\',\'男\',18) stu1=Student(\'lisl\',\'男\',18) print (stu1.__dict__) #打印出__init__里的属性 print (stu1.Name) #打印名字
5.对象属性与类属性
#对象属性:只有数据属性 #类属性:有数据属性(类的特征)和函数属性(类的功能) class Student: school=\'北大\' def __init__(self,name,sex,age): #__init__也是函数可以执行其他判断语句,不能有返回值return self.Name=name self.Sex=sex self.Age=age def learn(self): print (\'is learning\') def choose_course(self): print (\'choose course\') stu1=Student(\'lisl\',\'男\',18) print (stu1.__dict__) #打印出__init__里的属性,只有数据属性 print (Student.__dict__) #有数据属性和函数属性
6.对象属性的查找顺序
class Student: school=\'北大\' Name = \'类里名字\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def learn(self): print (\'is learning\') def choose_course(self): print (\'choose course\') stu1=Student(\'lisl\',\'男\',18) print (stu1.Name) #打印对象属性里的名字=lisl print (Student.Name) #打印类数据属性里的名字‘类里名字’ #将self.Name=name修改成==》self.Name2=name print (stu1.Name) #打印对象属性里的名字=‘类里名字’ print (Student.Name) #打印类数据属性里的名字=‘类里名字’ #注:如果对象属性里没有Name,类属性里也没有Name,而全局有Name,Student.Name是无法使用全局的Name的,会报错 #总论:对象属性查找:先从对象自己的名称空间(即__dict__)找---》再到类的名称空间(即__dict__)里找,不会再全局找
7.对象访问共有部分,即访问类的属性,属性查找与绑定方法
class Student: school=\'北大\' # Name = \'类里名字\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def learn(self): print (\'%s is learning\'%self.Name) def choose_course(self): print (\'choose course\') #实例化对象 stu1 =Student(\'lisl\',\'boy\',18) stu2 =Student(\'chenhong\',\'girl\',28) stu3 =Student(\'wangdong\',\'boy\',17) #值相同,id也相同,说明都是去访问类属性或类方法 print (Student.school,id(Student.school)) print (stu1.school,id(stu1.school,)) print (stu2.school,id(stu2.school,)) print (stu3.school,id(stu3.school,)) #修改某个对象的属性,类似里在对象里创建了该属性,而非在类属性里修改,如 stu1.school=\'xxx\' #如果要修改所有对象的共有部分,则需要对类属性进行修改,如 Student.school=\'xxx\' #总结2:类的数据属性是所有对象共享,所有对象都指向同一个内存地址 print (Student.learn) #结果<function Student.learn at 0x00000000010E1C80> print (stu1.learn) <bound method Student.learn of <__main__.Student object at 0x00000000010FB240>> print (stu2.learn) <bound method Student.learn of <__main__.Student object at 0x00000000010FB278>> print (stu3.learn) #总结3:类中定义的函数(即类的函数属性)是绑定给对象使用: ##1.不同对象就是不同绑定方法 ##2.绑定给谁,就应该由谁来调用就会把谁当做第一个参数传给对应的函数,如: stu1.learn() #绑定给stu1,就将stu1当做实参传递给第一个形参self 结果:lisl is learning
8.小练习:每个对象想统计类被调用了多少次
class Teacher: school=\'北大\' count = 0 def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age Teacher.count +=1 #不能self.count这样就是统计对象自己,类的属性变动才能影响到所有对象 def teacher(self): print (\'%s is teaching\'%self.name) #实例化对象 teacher1=Teacher(\'lisl\',\'boy\',\'18\') teacher2=Teacher(\'chenhong\',\'girl\',\'21\') teacher3=Teacher(\'wangdong\',\'boy\',\'12\') # 每个对象都知道这个类总共被几个对象调用了 print (teacher1.count) print (teacher2.count) print (teacher3.count)
9.类型即类
#类型即类 l1=[2,3,4,5] l1.append(5) #等于list.append(l1,5),即l1是list类的对象化
10.小练习:对象交互,模拟英雄联盟盖伦攻击瑞文掉血场景
class Galenus: #类是盖伦 camp = \'demoria\' #阵营是德玛西亚 def __init__(self,name,life,damage): #名字,生命值,攻击力 self.Name=name self.Life=life self.Damage=damage def attack(self,target_obj): #攻击对象 target_obj.Life -=self.Damage #被攻击对象的生命值等于扣除攻击者的攻击力的剩余生命值 class Raven: #类是瑞文 camp = \'North Texas\' #阵营是诺克萨斯 def __init__(self,name,life,damage): #名字,生命值,攻击力 self.Name=name self.Life=life self.Damage=damage def attack(self,target_obj): #攻击对象 target_obj.Life -=self.Damage #被攻击对象的生命值等于扣除攻击者的攻击力的剩余生命值 #创造俩个人物 g1=Galenus(\'草丛猥琐男\',1000,100) r1=Raven(\'奔跑瑞文\',200,500) print (r1.Life) #瑞文被攻击前生命值 g1.attack(r1)#盖伦g1攻击瑞文r1 print (r1.Life)#瑞文被攻击后生命值
11.面向对象的三大特性
继承、封装、多态(见16知识点)
11.1 继承
\'\'\' 1 什么是继承 是一种新建类的方式,新建的类成为子类,子类会遗传父类的属性,可以减少代码冗余 在python中,子类(派生类)可以继承一个或者多个父类(基类,超类) \'\'\' #简单写法: class Parent1: pass class Parent2: pass class Sub1(Parent1): #单继承 pass class Sub2(Parent1,Parent2): #多继承 pass #查看子类继承了什么父类,已元组形式返回 print (Sub1.__bases__) #结果:(<class \'__main__.Parent1\'>,) print (Sub2.__bases__) #结果:(<class \'__main__.Parent1\'>, <class \'__main__.Parent2\'>) print (Parent1.__bases__) #结果:(<class \'object\'>,),父类默认继承object类 #在python2中类分为两种: #1.经典类,指的就没有继承object类的类,以及该类的子类 #2.新式类,指的就是继承object类的类,以及该类的子类 #在python3中统一都为新式类
继承举例:
class People: school=\'北大\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def tell_info(self): print (\'<姓名:%s,性别:%s,年龄:%s>\'%(self.Name,self.Sex,self.Age)) class Student(People): def learn(self): print (\'%s is learning\'%self.Name) def tell_info(self): #父类中的tell_info函数属性不满足子类,可以自行创建,会优先使用子类的 print (\'我是学生:\',end=\'\') print (\'<姓名:%s,性别:%s,年龄:%s>\'%(self.Name,self.Sex,self.Age)) #下面有更简洁的办法,重用父类代码 ,方法:People.tell_info(self) class Teacher(People): def teach(self): print (\'%s is teaching\'%self.Name) stu1 = Student(\'lisl\',\'boy\',18) print (stu1.school) #查找顺序:对象属性---》子类,Student属性---父类,People属性 stu1.tell_info()
小练习,看你混淆不混淆
class Foo: def f1(self): print (\'Foo.f1\') def f2(self): #self=obj print (\'Foo.f2\') self.f1() #obj.f1()
class Bar(Foo): def f1(self): print (\'Bar.f1\') obj = Bar() obj.f2() #结果: Foo.f2 Bar.f1
12.子类重用父类的方法
方法一:指明道姓,即父类名.父类方法()
#子类想扩展父类某个函数功能,如何避免代码重用 class People: school=\'北大\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def tell_info(self): print (\'<姓名:%s,性别:%s,年龄:%s>\'%(self.Name,self.Sex,self.Age)) class Student(People): def learn(self): print (\'%s is learning\'%self.Name) def tell_info(self): print (\'我是学生:\',end=\'\') People.tell_info(self) #调用People类中的tell_info函数属性,使用代码重用,此处不是继承关系,此处的self=stu1 stu1 = Student(\'lisl\',\'boy\',18) print (stu1.school) stu1.tell_info()
方法二:super()
#子类想扩展父类某个函数功能,如何避免代码重用 #方法二: class People: school=\'北大\' def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age def tell_info(self): print (\'<姓名:%s,性别:%s,年龄:%s>\'%(self.Name,self.Sex,self.Age)) class Student(People): def __init__(self,name,sex,age,course): super().__init__(name,sex,age) #super()是一个特殊的对象,既是对象调用方法,默认第一个形参对应的实参就不用写 self.Course=course def learn(self): print (\'%s is learning\'%self.Name) def tell_info(self): print (\'我是学生:\',end=\'\') super().tell_info() #python2的格式是super(Student,self).tell_info(),下面会额外讲解 stu1 = Student(\'lisl\',\'boy\',18,\'python\') stu1.tell_info()
12.2 关于子类重用父类方法的方法二中,python2与python3中的不同点
#python2中格式: #super(子类,self).父类方法 #如上面的方法二的例子: super(Student,self).tell_info() #python3中格式: #super().父类方法 #如上面的方法二的例子: supper().tell_info()
12.3 关于子类重用父类方法的方法一与方法二区别
#1.方法一,指明道姓,用的是类而不是对象,因此没有所谓继承关系 #2.方法二,super()是特殊的对象,有继承关系。 当你使用super()函数时,Python会在MRO列表(继承顺序,见15.3)上继续搜索下一个类。只要每个重定义的方法统一使用super() 并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只能被调用一次(ps:使用super调用的所有属性,都是从MRO 列表当前的位置(即super()所在类在MRO列表里往后查找,不在本类查找)往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
12.4 super()在MRO列表查找例子
class Foo: def f1(self): print (\'FOO.f1\') super().f2() #super()所处在Mro列表里的FOO,继续往后查找下一个类,看是否有f2()方法 class Bar: def f2(self): print (\'Bar.f2\') class Sub(Foo,Bar): pass print (Sub.mro()) #[<class \'__main__.Sub\'>, <class \'__main__.Foo\'>, <class \'__main__.Bar\'>, <class \'object\'>] s= Sub() s.f1()
13.组合
\'\'\' 减少代码冗余除了使用继承,还可以使用组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合 组合与继承的区别: 1.继承的方式 通过继承建立了派生类与基类之间的关系,它是一种\'是\'的关系,比如白马是马,人是动物。 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人 2.组合的方式 用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课 程,教授有学生s1,s2,s3...。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好 \'\'\' class People: def __init__(self,name,sex,age): self.Name=name self.Sex=sex self.Age=age class Course: def __init__(self,name,period,price): self.Name=name self.Period=period self.Price=price def tell_info(self): print (\'<%s %s %s>\'%(self.Name,self.Period,self.Price)) class Teacher(People): def __init__(self,name,sex,age,job_title): People.__init__(self,name,sex,age) self.Job_title=job_title self.Course=[] self.Students=[] class Student(People): def __init__(self,name,sex,age): People.__init__(self,name,sex,age) self.Course=[] #实例化对象 lisl = Teacher(\'lisl\',\'boy\',18,\'python讲师\') wangdong = Student(\'王东\',\'boy\',22) python = Course(\'python\',\'3mons\',3000.0) linux = Course(\'linux\',\'3mons\',2000.0) #为老师lisl和学生wangdong添加课程,即组合 lisl.Course.append(python) lisl.Course.append(linux) wangdong.Course.append(python) #为老师lisl添加学生s1 lisl.Students.append(wangdong) #使用:打印所教的课程信息 for obj in lisl.Course: obj.tell_info()
14.接口与归一化设计
14.1 什么是接口
提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数,这么做的意义,在于归一化。
14.2什么是归一化?
只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样
14.3归一化的好处:
1.让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度
2.归一化使用高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
2.1就好像linux的泛文件概念一样,所有的东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出‘字符设备’和‘块设备’,然后做出针对性的设计)
2.2再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后有本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
14.4 用python模拟出类似jave interface的功能
需求:让子类严格按照父类来定义,实现类似java interface的功能 import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod #定义一个abc的抽象方法 def eat(self): pass @abc.abstractmethod def run(self): pass class People(Animal): #子类一定要有eat方法和run方法,否则报错。 def eat(self): print (\'people is eating\') def run(self): print (\'people is running\') class Pig(Animal): def eat(self): print (\'pig is eating\') def run(self): print (\'pig is running\')
15.多继承
15.1 多继承的顺序
在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
如果继承关系为非菱形结构(没有闭环),则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直
到我们想要的属性
如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:(经典类)深度优先和(新式类)广度优先,如下图所示
15.2 演示代码
class A(object): def test(self): print(\'from A\') class B(A): def test(self): print(\'from B\') class C(A): def test(self): print(\'from C\') class D(B): def test(self): print(\'from D\') class E(C): def test(self): print(\'from E\') class面向面试编程代码片段之GC