Python学习Day6:面向对象学习
编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。 两种最重要的编程范式分别是面向过程编程和面向对象编程
面向过程编程
面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,即:将之前实现的代码块复制到现需功能处。程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 随着程序越来越大, 这种编程方式的维护难度会越来越高。 所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护的, 那还是用面向对象最方便了。
示例:
1 #面向过程编程 2 3 while True: 4 if cpu利用率 > 90%: 5 #发送邮件提醒 6 连接邮箱服务器 7 发送邮件 8 关闭连接 9 10 if 硬盘使用空间 > 90%: 11 #发送邮件提醒 12 连接邮箱服务器 13 发送邮件 14 关闭连接 15 16 if 内存占用 > 80%: 17 #发送邮件提醒 18 连接邮箱服务器 19 发送邮件 20 关闭连接
函数式编程
将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
练习:在终端输出结果如下
张三,18岁,男,上山去砍柴
李四,20岁,男,开车去东北
王二,28岁,男,最爱大宝健
1 #函数式编程 2 3 def firewood(name,age,gender): 4 print("%s,%s岁,%s,上山去砍柴" %(name,age,gender)) 5 6 def drive(name,age,gender): 7 print("%s,%s岁,%s,开车去东北" %(name,age,gender)) 8 9 def dbj(name,age,gender): 10 print("%s,%s岁,%s,最爱大宝健" %(name,age,gender)) 11 12 firewood('张三',18,'男') 13 drive('李四',20,'男') 14 dbj('王二',28,'男') 15 # 张三,18岁,男,上山去砍柴 16 # 李四,20岁,男,开车去东北 17 # 王二,28岁,男,最爱大宝健
面向对象编程
1、类、对象、实例
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下:
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
练习:在终端输入结果如下
张三,18岁,男,上山去砍柴
张三,18岁,男,开车去东北
张三,18岁,男,最爱大宝健
王二,28岁,男,上山去砍柴
王二,28岁,男,开车去东北
王二,28岁,男,最爱大宝健
1 #面向对象 2 3 class Foo: 4 def __init__(self,name,age,gender): 5 self.name = name 6 self.age = age 7 self.gender = gender 8 9 def firewood(self): 10 print("%s,%s岁,%s,上山去砍柴" % (self.name,self.age,self.gender)) 11 12 def drive(self): 13 print("%s,%s岁,%s,开车去东北" % (self.name,self.age,self.gender)) 14 15 def dbj(self): 16 print("%s,%s岁,%s,最爱大宝健" %(self.name,self.age,self.gender)) 17 18 foo = Foo('张三',18,'男') 19 foo.firewood() 20 foo.drive() 21 foo.dbj() 22 # 张三,18岁,男,上山去砍柴 23 # 张三,18岁,男,开车去东北 24 # 张三,18岁,男,最爱大宝健 25 26 foo2 = Foo('王二',28,'男') 27 foo2.firewood() 28 foo2.drive() 29 foo2.dbj() 30 # 王二,28岁,男,上山去砍柴 31 # 王二,28岁,男,开车去东北 32 # 王二,28岁,男,最爱大宝健
面向对象编程的主要作用也是使你的代码修改和扩展变的更容易,那么小白要问了,既然函数都能实现这个需求了,还要OOP干毛线用呢? 呵呵,说这话就像,古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后小白就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。
上面的程序做个简单认识即可,下面我们通过写一个cs程序来对面向对象编程做进一步认识
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
CS游戏 1、暂不考虑开发场地等复杂的东西,角色恐怖份子、警察 2、每个人都有生命值 3、武器 4、被打中后就会掉血的功能 5、开枪功能 6、换子弹 7、买枪
1 #面向对象class 2 3 class Role(object): #定义一个类,class是定义类的语法,Role是类名,(object)是新式类的写法,暂且先记住 4 def __init__(self,name,role,weapon,life_value=100,money=15000):#初始化函数,在生成一个角色时要初始化的属性就写在这里 5 self.name = name 6 self.role = role 7 self.weapon = weapon 8 self.life_value = life_value 9 self.money = money 10 #__init__()叫做初始化方法(或构造方法),在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作 11 12 def shot(self): 13 #开了枪后要减少子弹数 14 print("%s is shotting......." %(self.name)) 15 16 def got_shot(self): 17 #中枪后要减血 18 print("ah.......%s:I got shot......."%(self.name)) 19 20 def buy_gun(self,gun_name): 21 #检查钱够不够,买了枪后要扣钱 22 print("%s just bought %s "%(self.name,gun_name)) 23 24 #生成一个角色,会自动把参数传给Role下面的__init__(...)方法,r1 25 r1 = Role('Alex','police','Ak47') #此时self相当于 r1,Role(r1,'Alex','police','AK47') 26 r2 = Role('Jack','terrorist','B22') #此时self相当于 r2,Role(r2,'Alex','polic','AK47') 27 28 print(r1,type(r1)) 29 print(r1.role) 30 r1.shot() 31 r1.got_shot() 32 r1.buy_gun("DZT100") 33 # <__main__.Role object at 0x00000000021A9940> <class '__main__.Role'> 34 # police 35 # Alex is shotting....... 36 # ah.......Alex:I got shot....... 37 # Alex just bought DZT100
2、类变量和实例变量
类变量为大家都共有的变量,只加载在类内存当中,不会加载在每个实例里;举个栗子:创建14亿用户,大家国籍都是中国,如果不把国籍写到类变量中,而是写到实例变量默认参数中,则14亿个实例,每个都要加载国籍到内存当中,会占用大量内存,这就是为什么我们要了解类变量的意义
1 #类变量和实例变量 2 class Role(object): #定义一个类,class是定义类的语法 3 n = 123 #类变量 4 name = "类name" 5 def __init__(self,name,role,weapon,life_value=100,money=15000): 6 self.name = name #实例变量 7 self.role = role 8 self.weapon = weapon 9 self.life_value = life_value 10 self.money = money 11 12 r1 = Role('Alex','police',"AK47") #实例 13 14 print(Role.n,r1.n) 15 print(Role.name,r1.name) 16 # 123 123 17 # 类name Alex
1 #实例变量 2 3 class Role: 4 n = 123 #类变量 5 name = "类name" 6 def __init__(self,name,role,weapon,life_value=100,money=10000): 7 self.name = name #实例变量 8 self.role = role 9 self.weapon = weapon 10 self.life_value = life_value 11 self.money = money 12 13 r1 = Role('ZhangSan','police','AK47') #实例 14 r1.name = 'LiSi' 15 r1.bullet = True 16 print(r1.name,r1.bullet) 17 # LiSi True
类进行实例化以后,可以对实例变量进行重新赋值和增加新的变量。
1 #类变量和实例变量 2 3 class Role: 4 n = 123 #类变量 5 name = '类name' 6 def __init__(self,name,role,weapon,life_value = 100,money = 10000): 7 self.name = name #实例变量 8 self.role = role 9 self.weapon = weapon 10 self.life_value = life_value 11 self.money = money 12 13 r1 = Role('ZhangSan','Police','AK47') #实例 14 r2 = Role('LiSi','Police','AK47') 15 16 r1.n = 'r1的123' 17 print(r1.name,r1.n) 18 print(r2.name,r2.n) 19 # ZhangSan r1的123 20 # LiSi 123 21 22 Role.n = 'Role的123' 23 print(r1.name,r1.n) 24 print(r2.name,r2.n) 25 # ZhangSan r1的123 26 # LiSi Role的123
上述结果发现,赋值r1.n后只是影响实例r1,不影响r2.n,这是因为实例变量和类变量名一致时,实例变量改变时不影响类变量,只影响在当前实例;当重新赋值类变量时,r2也跟着改变;对上面程序在进行个升级!!!
1 #类变量和实例变量 2 3 class Role(object): #object新式类 4 n_list = [] #类变量 5 def __init__(self,name,role,weapon,life_value=100,money=10000): 6 self.name = name #实例变量 7 self.role = role 8 self.weapon = weapon 9 self.life_value = life_value 10 self.money = money 11 12 r1 = Role('ZhangSan','police','AK47') 13 r2 = Role('LiSi','police','AK47') 14 15 r1.n_list.append("from r1.......") 16 r2.n_list.append("fron r2.......") 17 18 print(r1.n_list) 19 print(r2.n_list) 20 print(Role.n_list) 21 # ['from r1.......', 'fron r2.......'] 22 # ['from r1.......', 'fron r2.......'] 23 # ['from r1.......', 'fron r2.......']
从上述结果可以看出r1,r2改变了类变量,因为他们的内存地址都是一致的
3、析构函数
在实例释放、销毁的时候自动执行,通常用于做一下收尾工作(如关闭数据连接,关闭打开的临时文件)
1 #析构函数 2 3 class Role(object): 4 def __init__(self,name,role,weapon,life_value=100,money=10000): 5 self.name = name 6 self.role = role 7 self.weapon = weapon 8 self.life_value = life_value 9 self.money = money 10 11 def __del__(self): 12 print("%s 彻底死了!" % self.name) 13 14 def shot(self): 15 #射击减少子弹 16 print("%s shotting......." % self.name) 17 18 def got_shot(self): 19 #中枪减血 20 print("ah...%s: I got shot..." % self.name) 21 22 r1 = Role('ZS','police','AK47') 23 r2 = Role('LS','police','AK47') 24 25 r1.got_shot() 26 del r1 27 28 r2.got_shot() 29 30 # ah...ZS: I got shot... 31 # ZS 彻底死了! 32 # ah...LS: I got shot... 33 # LS 彻底死了!
程序结束,del删除实例时才会执行__del__里的内容
4、私有属性、私有方法
记住私有的概念:只能在类里面进行调用 看完下面的代码你就明白了
1 #私有属性和私有方法 2 3 class Role(object): 4 5 def __init__(self,name,role,weapon,life_value=100,money=10000): 6 self.name = name 7 self.role = role 8 self.weapon = weapon 9 self.__life_value = life_value #定义私有属性,私有属性在外部不能调用,只能在类里面使用 10 self.money = money 11 12 def show_status(self): #定义函数,调用私有属性 13 print("name:%s weapon:%s life_value:%s"%(self.name,self.weapon,self.__life_value)) 14 15 def __shot(self): #定义私有方法 16 print("%s is shooting......."%(self.name)) 17 18 def got_shot(self): 19 self.__shot() #调用私有方法 20 print("ah.......%s:I got shot......."%(self.name)) 21 22 r1 = Role('ZS','police','AK47') 23 24 #私有属性 25 #print(r1.__life_value) #外部调用life_value 直接报错 26 #AttributeError: 'Role' object has no attribute '__life_value' 27 r1.show_status() #通过方法执行私有属性 28 #name:ZS weapon:AK47 life_value:100 29 30 #私有方法 31 #r1.__shot() #外部调用私有方法,直接报错 32 #AttributeError: 'Role' object has no attribute '__shot' 33 r1.got_shot() #通过其他方法执行私有方法 34 # ZS is shooting....... 35 # ah.......ZS:I got shot.......
5、继承
继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
1 #继承 2 3 class People(): 4 5 def __init__(self,name,age): 6 self.name = name 7 self.age = age 8 9 def eat(self): 10 print("%s is eatting......"%(self.name)) 11 12 def sleep(self): 13 print("%s is sleeping......."%(self.name)) 14 15 class Man(People): #继承类People 16 17 def play(self): #增加新功能 18 print("%s is playing......."%(self.name)) 19 20 def sleep(self): #重构sleep功能 21 People.sleep(self) 22 print("Man is sleeping.......") 23 24 class Woman(People): #继承类People 25 26 def get_birth(self): # 增加新功能 27 print("%s is born a baby......."%(self.name)) 28 29 m1 = Man("TT",18) 30 w1 = Woman("MM",18) 31 32 m1.eat() #调用People方法 33 m1.play() #调用Man方法 34 # TT is eatting...... 35 # TT is playing....... 36 37 m1.sleep() 38 # TT is sleeping....... 39 # Man is sleeping....... 40 41 w1.get_birth() 42 # MM is born a baby.......
由上述代码可知,类的继承可以节省大量重复代码。
1 #类的继承,子类初始化 2 3 class People(): 4 def __init__(self,name,age): 5 self.name = name 6 self.age = age 7 8 def eat(self): 9 print("%s is eatting......"%(self.name)) 10 11 def sleep(self): 12 print("%s is sleeping......."%(self.name)) 13 14 class Man(People): 15 16 def __init__(self,name,age,money): #重构初始化,覆盖父类 17 #People.__init__(self,name,age) #加载父类初始化 18 super(Man,self).__init__(name,age) #加载父类初始化 19 self.money = money 20 print("%s 一出生就有 $%s" % (self.name,self.money)) 21 22 def play(self): #增加新功能 23 print("%s is playing......."%(self.name)) 24 25 class Woman(People): 26 def get_birth(self): 27 print("%s is born a baby......."%(self.name)) 28 29 m1 = Man("TT",18,100000) 30 w1 = Woman("MM",18) 31 # TT 一出生就有 $100000
子类需要重构初始化的时候,会把父类的初始化覆盖掉,所以在初始化时需要加载父类的初始化。
#类的继承,多继承 class People(object): #新式类 def __init__(self,name,age): self.name = name self.age = age def eat(self): print("%s is eatting......"%(self.name)) def sleep(self): print("%s is sleeping......."%(self.name)) class Relation(object): def make_friends(self,obj): print("%s is making friend with %s"%(self.name,obj.name)) class Man(Relation,People): #多继承 def play(self): print("%s is playing......."%(self.name)) class Woman(People): def get_birth(self): print("%s is born a baby......"%(self.name)) m1 = Man("TT",18) w1 = Woman("MM",18) m1.make_friends(w1) # TT is making friend with MM
多继承时需注意,在多继承中从父类继承初始化属性时,顺序从左到右开始初始化,只要初始化到属性数据就不再向后继续,所以越往前越优先;当父类有初始化,子类也有初始化时,执行子类的初始化,父类的不生效
刚才我们已经知道了新式类的概念,那么经典类与新式类的区别是什么呢?!
通过上面的程序我们知道Python的类可以继承多个类,那如果Python的类如果继承了多个类,有多层继承关系,那么其初始化时寻找的路线是什么样的呢?,通过下面这段代码来看下:Python3以后都是新式类了
1 #经典类 2 3 class A(): 4 def __init__(self): 5 print("A") 6 7 class B(A): 8 pass 9 10 class C(A): 11 def __init__(self): 12 print("C") 13 14 class D(B,C): 15 pass 16 17 obj = D() 18 #A 19 20 #新式类 21 class A(object): 22 def __init__(self): 23 print("A") 24 25 class B(A): 26 pass 27 28 class C(A): 29 def __init__(self): 30 print("C") 31 32 class D(B, C): 33 pass 34 35 obj = D() 36 #C
当类是经典类时,多继承情况下,会按照深度优先方式查找;当类是新式类时,多继承情况下,会按照广度优先方式查找;具体参考下图
6、多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。Pyhon不直接支持多态,但可以间接实现
看完上面的话,完全懵逼; 说人话!!--》一个接口,多种实现,实现接口重用
1 #多态 2 3 class Animal: 4 def __init__(self,name): 5 self.name = name 6 7 @staticmethod 8 def animal_talk(obj): 9 obj.talk() 10 11 class Cat(Animal): 12 def talk(self): 13 print("%s:Meow!"%(self.name)) 14 15 class Dog(Animal): 16 def talk(self): 17 print("%s:Woof!"%(self.name)) 18 19 c = Cat("HH") 20 d = Dog("MM") 21 22 Animal.animal_talk(c) 23 Animal.animal_talk(d) 24 25 # HH:Meow! 26 # MM:Woof!