一、面向过程程序设计与面向对象程序设计
面向过程的程序设计:核心是过程,过程就解决问题的步骤,基于该思想设计程序就像是在设计一条流水线,是一种机械式的思维方式
优点:复杂的问题的简单化,流程化 缺点:扩展性差面向对象的程序设计:核心是对象,对象是特征(变量)与技能(函数)的结合体,是一种上帝式的思维方式
优点:解决了程序的扩展性
缺点:可控性差
二、类和对象
以游戏举例,基于面向对象设计一个款游戏:英雄联盟,每个玩家选一个英雄,每个英雄都有自己的特征和和技能,特征即数据属性,技能即方法属性,特征与技能的结合体就一个对象。
类:从一组对象中提取相似的部分就是类,类所有对象都具有的特征和技能的结合体
在python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体
在现实世界中:对象--(共同的特征与技能)-->类
在程序中:先定义类----(实例化)----->对象1、类的定义
定义类的语法
class 类名: '''注释''' 类体#定义一个学生类:类名通常首字母大写表示class Student: school = 'oldboy' def __init__(self,name,age): #只用来初始化的,并且一定不能有返回值, self.name=name self.age=age def study(self): print('is studying') def fly(self,x): print(x) print('%s is flying' %self.name) def foo(self): print('===========》')
2、类的作用
类的的用法一:实例化产生对象
类的的用法二:属性引用3、对象的作用:
对象/实例只有一种作用:属性引用
#类的作用一:实例化对象s1=Student('egon1',84)s2=Student('egon2',84)#类的作用二:类的属性引用print(Student.school) #引用类的数据属性print(Student.study) #引用类的函数属性#对象的用法:属性引用print(s1.name)print(s1.age)
对象/实例本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样
4、类的名称空间与对象名称空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
类有两种属性:数据属性和函数属性,
其中类的数据属性是共享给所有对象的
>>> id(s1.school) #本质就是在引用类的Student属性,二者id一样4315241024>>> id(s2.school)4315241024
而类的函数属性是绑定到所有对象的:
>>> id(s1.study) 4302501512>>> id(s2.study)4315244200
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看dir(类名):查出的是一个名字列表类名.__dict__:查出的是一个字典,key为属性名,value为属性值二:特殊的类属性类名.__name__# 类的名字(字符串)类名.__doc__# 类的文档字符串类名.__base__# 类的第一个父类(在讲继承时会讲)类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)类名.__dict__# 类的字典属性类名.__module__# 类定义所在的模块类名.__class__# 实例对应的类(仅新式类中)
5、类的成员:
类的成员可以分为三大类:字段、方法和属性
字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段属于对象
- 静态字段属于类
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name# 直接访问普通字段obj = Province('河北省')print obj.name# 直接访问静态字段Province.country字段的定义和使用
方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print self.name print '普通方法' @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print '类方法' @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print '静态方法'# 调用普通方法f = Foo()f.ord_func()# 调用类方法Foo.class_func()# 调用静态方法Foo.static_func()方法的定义和使用
属性
如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。
对于属性,有以下三个知识点:
- 属性的基本使用
- 属性的两种定义方式
# ############### 定义 ###############class Foo: def func(self): pass # 定义属性 @property def prop(self): pass# ############### 调用 ###############foo_obj = Foo()foo_obj.func()foo_obj.prop #调用属性属性的定义和使用
三、继承与派生
1、继承的概念:继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 passclass ParentClass2: #定义父类 passclass SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass passclass SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类(,)>>> SubClass2.__bases__( , )
注:在python3中所有类默认继承object,
1、只要是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中的类都是新式类)
2、没有继承object类的子类称为经典类(在python2中,没有集成object的类,以及它的子类,都是经典类)
''继承+派生'''class People: def __init__(self, name, age,sex): self.name = name self.age = age self.sex=sex def walk(self): print('%s is walking' % self.name) def foo(self): print('from father %s' %self.name)class Teacher(People): school = '偶的博爱' #__init__(t,'egon',18,'male',10,3000) def __init__(self, name, age,sex,level,salary): People.__init__(self,name,age,sex) self.level=level self.salary=salary def teach(self): print('%s is teaching' %self.name) def foo(self): People.foo(self) print('from teacher')class Student(People): def __init__(self, name, age,sex,group): People.__init__(self, name, age, sex) self.group=group def study(self): print('%s is studying' %self.name)t=Teacher('egon',18,'male',10,3000)
四、组合与重用性
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
1.继承的方式
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师
>>> class Teacher:... def __init__(self,name,gender):... self.name=name... self.gender=gender... def teach(self):... print('teaching')... >>> >>> class Professor(Teacher):... pass... >>> p1=Professor('egon','male')>>> p1.teach()teaching
2.组合的方式
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=dayclass Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=periodclass Teacher: def __init__(self,name,gender): self.name=name self.gender=gender def teach(self): print('teaching')class Professor(Teacher): def __init__(self,name,gender,birth,course): Teacher.__init__(self,name,gender) self.birth=birth self.course=coursep1=Professor('egon','male', BirthDate('1995','1','27'), Couse('python','28000','4 months'))print(p1.birth.year,p1.birth.month,p1.birth.day)print(p1.course.name,p1.course.price,p1.course.period)'''运行结果:1 27python 28000 4 months'''
继承+派生+组合应用举例:
class People: def __init__(self, name, age, year, mon, day): self.name = name self.age = age self.birth = Date(year, mon, day) def walk(self): print('%s is walking' % self.name)class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell_birth(self): print('出生于<%s>年 <%s>月 <%s>日'%(self.year,self.mon,self.day))class Teacher(People): def __init__(self, name, age, year, mon, day,level,salary): People.__init__(self,name,age,year,mon,day) self.level=level self.salary=salary def teach(self): print('%s is teaching' %self.name)class Student(People): def __init__(self, name, age, year, mon, day,group): People.__init__(self,name,age,year,mon,day) self.group=group def study(self): print('%s is studying' %self.name)
总结:当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
五、接口
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。 def read(self): #定接口函数read pass def write(self): #定义接口函数write passclass Txt(Interface): #文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法')class Sata(Interface): #磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法')class Process(Interface): def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法')
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
接口的好处:
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
'''(抽象类): #父类要限制 #1;子类必须要有父类的方法 #2:子类实现的方法必须跟父类的方法的名字一样'''import abcclass File(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): passclass Txt(File): #文本,具体实现read和write def read(self): pass def write(self): passt=Txt()