面向对象编程:
面向对象编程是一种能够产生结果但是这个结果是无法预测的结果,这是和面向过程的却别,如果我们按照过程传入参数,那么我们按照过程进行一行行的解读会得到一个结果,而面向对象过程相当于我们以上帝的视角去创造一个对象,我们给他任务让他完成我们要求的1到n个结果中得出一个结果,这个过程中随时在变,但是会给出一个结果,而我们只需要创造好这个对象而不是刻意的安排好每一步,这就好比你养了一只狗你会要求他永久的在6点吃饭,7点吃饭,8点吃饭,9点吃饭么?
狗也有自己的想法也会在饿了的时候去吃饭,那么你需要告诉控制住他什么时候饿了,什么时候有饿了的感觉就可以,这样他就会在自己饿了的时候才去吃东西,这就是面相对象,你去控制他的一切的阈值而不是控制他的下一步动作。
不懂得话我们做个对比和面向过程编程对比一下就好思考了
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。
而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。
功能上的统一保证了面向对象设计的可扩展性。比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。
再比如我要把这个五子棋游戏改为围棋游戏,如果你是面向过程设计,那么五子棋的规则就分布在了你的程序的每一个角落,要改动还不如重写。但是如果你当初就是面向对象的设计,那么你只用改动规则对象就可以了,五子棋和围棋的区别不就是规则吗?(当然棋盘大小好像也不一样,但是你会觉得这是一个难题吗?直接在棋盘对象中进行一番小改动就可以了。)而下棋的大致步骤从面向对象的角度来看没有任何变化。
当然,要达到改动只是局部的需要设计的人有足够的经验,使用对象不能保证你的程序就是面向对象,初学者或者很蹩脚的程序员很可能以面向对象之虚而行面向过程之实,这样设计出来的所谓面向对象的程序很难有良好的可移植性和可扩展性。
这样更容易弄懂面向对象
既然知道了面向对象我们要懂得是怎么去还分出,三类(黑白双方,棋盘系统,规则系统)
这里我们引入类和对象的概念
我们需要先说明对象在python是真是存在的实体,而类是对一个种类的共有的特点的描述,打个比方,我们再买车的时候他们都会标配四个轱辘,两个大灯,两个后视镜,一个发动机我们在想这些的时候就相当于定义了一个种类,叫车 这是我们定义的这个类是没有实体的,你可能问那车上的确是有这些东西啊我在车上看到过啊??????那么我问你 你看到的是不是个实体?????这时你看到的那辆车他就是一个对象,这个对象除了拥有想象出来的车类的该有的东西,
是不是也有他们专属的品牌啊。
这里就可以引入一个概念实例化和实例化后的专有标志(变量)概念
我们现在python中创造一个类
再创造之前我们先想几个不同型号的车,
比方说suv,轿车,跑车
他们都有四个轱辘,一个发动机,两个后视镜这些都是他们都有的数据属性,他们都能跑到120公里每小时,这是他们都有的函数属性
下面列出他们的区别,举例
suv:越野,宽大
轿车: 灵活,省油
跑车:高贵,费油
那么我们可以先定义他们的类
这样我们就定义好了车这个类基础上有什么,
但是我们还做不到如何调用他因为他只是我们构思出来的一个类,这个类知识我们想象出来的,不是真正存在的
这时我们想要定义个suv只需要
这样我们就创造了一个对象叫suv。
我们怎么看这个对象里面有什么呢?
这样调用我们就能够看到内容
但是呢他只有车类的基本属性,那么他的越野特性怎么添加进去呢?
这里我们就需要给类中添加一个功能
需要在类中增加这一段,这一段代表的是可以提供给对象独有几个数据属性,这里我只写了两个说明每个对象只能写两个多了不行少了报错
如果suv还有别的特点那么我们可以新家一个独有的变量名称再给他附上值
同样的我们定义个他特有的函数数据也是相同的方法只不过我们需要先定义一个函数在将函数导入进去就可以了
继承:
在我们定义一个类的时候,常常会出现a类定义完之后,我们再去定义一个b类结果他的属性足够包含了所有a的的所有属性,而我们的a类又不能舍弃,如果我们再写一个b类代码重复量特别多,所以我们由此引出了子类的概念,他要做的就是能够完全继承一个类的所有属性而且可以进行自由的添加其他属性的类。
注释(这里为了方便理解继承打的比方,在别的地方可能不适用,因为没有日常工作中任何两个类是完全相同的)
同时也对应了另一种关系那就是子类的属性数量大于等于父类
这也比较好了解,就好比一个资产过亿的父亲死了把这一切给了子类,而子类又特别牛逼,他就保持住了家业,同时还能够扩大自己的家业
class People: def __init__(self,name,age):#定义一个父类,这里父类拥有2个数据属性,1个函数属性 self.name=name self.age=age def walk(self): print('%s is walking' %self.name) class Teacher(People): def __init__(self,name,age,salary,level):#我们只需要把创建对象的类所有需要的数据函数都填写上 People.__init__(self,name,age)#在父类中已经有的数据函数我们可以不用进行再次添加直接调用加上就可以了 self.salary=salary #子类独有的数据函数我们可以单独添加 self.level=level def walk(self): People.walk(self)#我们在这里调用了父类的函数walk print('%s is walking T' %self.name) def pai(self): #如果我们的父类中没有这个函数数据,我们可以在子类中添加上,这个过程叫派生 print('这是一段派生') class Student(People): pass t=Teacher('egon',17,3000,10) print(t.age) print(t.name) t.walk()
再提醒一遍:这样做的好处就是,不用为创作多个类而使用多次重复的代码而犯愁了,大大的提高了效率
关于新式类和经典类:
名称空间上,类实在定义的时候,就执行一遍 ,就创建了名称空间
当我们实际使用对象的时候,是会从对象内找,找不到在上类里找,再找不到在到父类里面找
新式类和经典类
新式类:写的时候自动添加了 object 父类的
经典类python2中默认不添加object所以叫经典类
组合:
我们一会又调用这个类的时候需要其他类的某个功能这是会使用一个叫做组合的方式
class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def birth(self): print('出生于%s %s月 %s日' %(self.year,self.mon,self.day)) class Teacher: def __init__(self,name,age,year,mon,day): self.name=name self.age=age self.birth=Date(year,mon,day).birth#这里我们直接调用DATE的一个函数属性,两个类之间是平行,所以当两个不想管的函数之间进行调用的过程就是组合 def teach(self): print('%s is teaching' %self.name) t1=Teacher('he',14,1993,1,1) t1.birth()
关于接口:
什么是接口 ?
接口只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能。
个人的理解,多用于协作开发时,有不同的人在不同的类中实现接口中的各个方法。
在Python中接口由抽象类和抽象方法去实现,接口是不能被实例化的,只能被别的类继承去实现相应的功能。
个人觉得接口在python中并没有那么重要,因为如果要继承接口,需要把其中的每个方法全部实现,否则会报编译错误,还不如直接定义一个class,其中的方法实现全部为pass,让子类重写这些函数。
当然如果有强制要求,必须所有的实现类都必须按照接口中的定义写的话,就必须要用接口。
方法一:用抽象类和抽象函数实现方法
#抽象类加抽象方法就等于面向对象编程中的接口 from abc import ABCMeta,abstractmethod class interface(object): __metaclass__ = ABCMeta #指定这是一个抽象类 @abstractmethod #抽象方法 def Lee(self): pass def Marlon(self): pass class RelalizeInterfaceLee(interface):#必须实现interface中的所有函数,否则会编译错误 def __init__(self): print '这是接口interface的实现' def Lee(self): print '实现Lee功能' def Marlon(self): pass class RelalizeInterfaceMarlon(interface): #必须实现interface中的所有函数,否则会编译错误 def __init__(self): print '这是接口interface的实现' def Lee(self): pass def Marlon(self): print "实现Marlon功能"
方法二:用普通类定义接口
class interface(object): #假设这就是一个接口,接口名可以随意定义,所有的子类不需要实现在这个类中的函数 def Lee(self):, pass def Marlon(self): pass class Realaize_interface(interface): def __init__(self): pass def Lee(self): print "实现接口中的Lee函数" class Realaize_interface2(interface): def __init__(self): pass def Marlon(self): print "实现接口中的Marlon函数" obj=Realaize_interface() obj.Lee() obj=Realaize_interface2() obj.Marlon()
最最搞笑的一个区别加了一个abc模块
并在父类上的每一个函数方法添加一个
@abstractmethod
装饰器
继承的实现原理
多个父类之间的优先级问题、
都有个相同类的
的时候
d=D(A,B,C)
我们会从对象》》》创建类》第一个父类A》》第二个B
如果出现了A(E):
那么就是对象d》》》创建类D》》》第一个父类A》》第一个父类的父类E》》》第二个父类B
算法是mro
对象掉mro
子类调用父类的方法
super()内置函数
他是一个函数 所以他是一个绑定方法
class An: def foo(self): print('ande') class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def foo(self): print('father') class Teacher(People,An): def __init__(self,name,age,sex,level): super().__init__(name,age,sex) self.level=level def foo(self): super().foo() An.foo(self) print('cccc') t1=Teacher('alex',10,'men',10) t1.foo()
封装:
1:将东西封起来不让别人看到。
2:隐藏数据,或者功能(黑盒子)
3:封装函数:隔离复杂度,让使用者只会用就可以
4:封装不是但存有意义的隐藏,
尝试:
封装数据
self.__name=name
封装函数
def __foo(self):
print('foo yin')
这种封装操作只在类的定义阶段发生
也就是说你在定义(实例化)一个对象后,是不可以封装的
t.__x=1
是不会做改变的
在类的外部,无法直接使用变形的属性,但是在类的内部是可以直接运行的
就是这种情况
class Teache: def __foo(self): print('---') def aa(self): print(' ',__foo(self))
property的内置模块使用方法
所装饰的函数会直接使用不用再进行()运行的方式
@property def aa(self): return (' ',__foo(self))
我们就可以把aa函数变成了一个数据函数的伪装
多态
理论 同一种事物的多种形态
传入一个参数,而且传入一个参数没有类型要求
我们可以这么理解,当我们说到车的时车会分成不通的种类,这样情况下呢,每一个类型的车都会有一个挂挡的函数,我们叫他多态
我们在开一辆特斯拉的时候,挂挡这个动作可能在特斯拉上只是动一下拨片就挂档了,而在拖拉机上我们需要使劲的摇动挡杆在能挂挡,他们都调用了一个挂挡的函数,却做出了不同的动作。这就是多态性
绑定方法和非绑定方
绑定方法:绑定给谁,就是给谁用的
凡是在勒种定义的函数,(没有任何装饰器),都是给对象用的
特点:obj.bar()自动吧obj当作第一个函数传入到bar()中。因为bar就是要处理obj的
绑定方法,有自动传值的功能,绑定给谁就可以绑定给谁并且将对象自动传入
绑定到类的方法
定义:在类当中定义的,被classmethod装饰的函数是绑定到类的方法
给谁用:给类用
特点:类.class_method()自动吧类当作第一个参数传入,因为class_method()要处理类
class People: def __init__(self,name): self.name=name def bar(self): print('------->',self.name) @classmethod def func(cls): print(cls) f=People('egon') People.func()
完成对象的存入和存取过程
import hashlib import pickle class People: def __init__(self,name,sex,user_id): self.name=name self.sex=sex self.user_id=user_id self.id=self.create_id() def tell_info(self): print(''' -----------%s info-------- id:%s name%s sex:%s user_id:%s ''' %(self.name,self.id,self.name,self.sex,self.user_id)) def create_id(self): #加密了名字并且id进行了储存 m=hashlib.md5() m.update(self.name.encode('utf-8')) m.update(self.sex.encode('utf-8')) m.update(str(self.user_id).encode('utf-8')) return m.hexdigest() def save(self): with open(r'D:py联系第一颗d22dateb%s'%(self.id),'wr') as f: pickle.dump(self,f) ############## #提取出来 import os student_path=r'D:py联系第一颗d22dateb' re=os.listdir(student_path) def get_all(): for i in re: file_path=r'%s\%s' %(student_path,i) with open(file_path,'rb') as f: obj=pickle.load(f) obj.tell_info()