有的人说,编程有3种范式:
1、面向过程:就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
2、面向函数:面向函数是面向过程的升级版,也就是把每个解决问题的代码写成一个函数,需要的时候调用函数就好了
3、面向对象:把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
面向对象编程:是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容
面向对象5个特性:
1、Class类:一个类即是一类拥有相同属性的对象的描述,在类中定义了这些对象的都具备的属性(variables(data))、共同的方法(函数)。
2、Object对象:一个对象即是对一个类实例化的后的实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,每个人都有相同点,又有不同点。
3、Encapsulation 封装:在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
4、Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承、
5、Polymorphism 多态:多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态
在说类之前,我们先用函数写个代码:
我们要实现类似面向对象的一个代码:【把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。】
1 #写面向对象之前,先将个别的 2 #我们定义个妈妈的函数, 3 #特征:名字,身高,体重(一般写面向对象的时候,想到他们有什么数据,有什么动作) 4 #动作:会做饭,唠叨 5 def Mom(name,height,weight): 6 def init(name,height,weight):#这个相当于初始化函数 7 xian_mom = { 8 "name":name, 9 "height":height, 10 "weight":weight, 11 "cook_dinner":cook_dinner#把动作和初始化函数相关联 12 } 13 #把所有的特征写进一个函数,然后返回这个函数 14 return xian_mom 15 def cook_dinner(xian_mom):#妈妈的动作 16 print(" %s 今天做个,蛋糕" % (xian_mom['name'])) 17 return init(name,height,weight)#给函数返回这个函数 18 obj = Mom("xfd","165cm","52kg")#给函数传入参数 19 print(obj)#obj是个字典 20 #{'name': 'xfb', 'height': '164cm', 'weight': '58kg', 21 # 'cook_dinner': <function Mom.<locals>.cook_dinner at 0x000001CD5D4F3598>} 22 print(obj['name']) 23 #xfd
那上面我们完成了这个函数,但是调用妈妈这个会做饭的动作,怎么调用呢?
1 print(obj["cook_dinner"]) 2 #返回了一个对象,缺少一个参数<function Mom.<locals>.cook_dinner at 0x0000023635E93950> 3 print(obj["cook_dinner"](obj))#把自己传进去【动作和初始化函数绑定了】 4 # xfd 今天做个,蛋糕 5 #None
上面函数返回了个none 因为cook_dinner这个函数没有返回值,是打印出来的
那我们把函数的数据都结构化了,现在就可以写我们的面向对象的代码了。
1 # 一个类里面一般都有2个属性 2 # 1、数据属性:也就是变量 3 # 2、函数属性:也就是函数,在面向对象里面叫方法 4 # 类的数据属性,和函数属性都用用【。】的方式来运行 5 6 7 class Mom:#类名 :一般首字母是大写,或者使用驼峰式 8 '这是一个妈妈类' #类的描述文档 9 gender = "woman" #妈妈都有个共同的属性,那就是女性咯 10 #这里一般都定义共有的属性【数据属性】 11 def __init__(self,name,height,weight): 12 #init方法是,只要运行函数自动加载init方法 13 self.name = name 14 self.height = height 15 self.weight = weight 16 #这样写就相当于我们定义了一个数据属性字典 17 def cook_dinner(self): 18 #self: obj["cook_dinner"](obj) 就相当于把自己传入进去 19 print(" %s 今天做个,蛋糕" % (self.name)) 20 21 22 print(Mom.__dict__)#查看类的属性字典包含数据属性和函数属性的字典 23 #{'__module__': '__main__', '__doc__': '这是一个妈妈类', 24 # 'gender': 'woman', '__init__': <function Mom.__init__ at 0x0000019C233D39D8>, 25 # 'cook_dinner': <function Mom.cook_dinner at 0x0000019C233D3950>, 26 # '__dict__': <attribute '__dict__' of 'Mom' objects>, 27 # '__weakref__': <attribute '__weakref__' of 'Mom' objects>} 28 #是不是感觉上面的是个字典? 29 #怎么取出gender的值呢? 30 print(Mom.__dict__["gender"])#woman 31 32 obj = Mom("xfd","165cm","52kg")#实例化,产生对象 33 print(obj.__dict__)#这个查看你额类里面包含哪些字典 34 #{'name': 'xfd', 'height': '165cm', 'weight': '52kg'} 35 obj.cook_dinner() 36 # xfd 今天做个,蛋糕
类属性的增删改查
class Mom: gender = "woman" def __init__(self,name): self.name = name def cook_dinner(self): print(" %s 今天做个,蛋糕" % (self.name)) #查询类有那些属性(数据属性,函数属性) print(Mom.__dict__) print(Mom.__dir__) #查询gender的值 print(Mom.gender)#woman #修改类数据属性 Mom.gender = 'man' #增加类的数据属性 Mom.hair = 'curly hair' #删除类数据属性 del Mom.gender #函数属性的增删改查是上面的一样 #增加函数属性 def lao_dao(self): print("%s 这么还不起床" %self.name) Mom.laodao = lao_dao#不能带括号,不然执行了 #'laodao': <function lao_dao at 0x00000266CE11C268>} #查询函数属性 m1= Mom("sjc") m1.laodao()#增加的函数属性可以调用 #修改 cook_dinner def cook(self): print('今天不听话,不给做饭') Mom.cook_dinner = cook#重新赋值 m1.cook_dinner()#今天不听话,不给做饭
实例的增删改查
class Mom: gender = "woman" def __init__(self,name): self.name = name def cook_dinner(self): print(" %s 今天做个,蛋糕" % (self.name)) #生成实例 m1 = Mom('sjc') #查看实例的属性字典 print(m1.__dict__)#{'name': 'sjc'} #查看 print(m1.name)#sjc print(m1.cook_dinner)#<bound method Mom.cook_dinner of <__main__.Mom object at 0x00000264ED9B8400>> print(m1.cook_dinner())#自己没有访问类的 #增加 m1.age = 28 #删除 del m1.age #实例化的过程,就是执行init方法
类的继承
1、单继承于多继承
1 class Father: 2 money = 1000 3 def __init__(self,name): 4 self.name = name 5 print('执行了Father') 6 def Make_Money(self): 7 print("%s 正在给儿子挣钱" % self.name) 8 9 10 class Son(Father):#这是单继承,多继承就是, class Son(Father,Mom) 11 pass 12 # print(Son.money)#输出1000 ,儿子可以调用爸爸的类的数据属性 13 # Son.Make_Money()#可以调用,但是报错 14 print(Father.__dict__) 15 print(Son.__dict__)#{'__module__': '__main__', '__doc__': None}没有方法是这么调用的呢? 16 ret = Son("sjc")#输出:执行了Father(由于继承的关系执行了Father的init方法) 17 print(ret.name)#输出:sjc 18 print(ret.money)#输出:1000 19 ret.Make_Money()#输出sjc 正在给儿子挣钱 20 #结果显示继承关系中,儿子不仅可以调用类的数据属性,也可以调用类的方法
2、如果儿子属性和爸爸属性重名了呢?
1 class Father: 2 money = 1000 3 def __init__(self,name): 4 self.name = name 5 print('执行了Father') 6 def Make_Money(self): 7 print("%s 正在给儿子挣钱" % self.name) 8 9 10 class Son(Father): 11 money = 5000 12 def __init__(self,name,age): 13 self.name = name 14 self.age = age 15 def Make_Money(self): 16 print('执行儿子方法 %s' % self.age) 17 print(Son.money)#5000 ,子类和父类的数据重名了,那优先找自己,不是覆盖 18 ret = Son("jsc",'3') 19 ret.Make_Money()#输出:执行儿子方法 3,方法也是用自己,没有被覆盖,
什么时候用继承呢?代码如果大量重复的时候可以使用继承
1 猫可以:喵喵叫、吃、喝、拉、撒 2 狗可以:汪汪叫、吃、喝、拉、撒 3 如果我们要分别为猫和狗创建一个类,那么就需要为猫/狗 4 实现他们所有的功能,伪代码如下: 5 # 猫和狗有大量相同的内容 6 class 猫: 7 def 喵喵叫(self): 8 print 9 '喵喵叫' 10 def 吃(self): 11 # do something 12 def 喝(self): 13 # do something 14 def 拉(self): 15 # do something 16 def 撒(self): 17 # do something 18 class 狗: 19 def 汪汪叫(self): 20 print 21 '喵喵叫' 22 def 吃(self): 23 # do something 24 def 喝(self): 25 # do something 26 def 拉(self): 27 # do something 28 def 撒(self): 29 # do something
使用继承
1 上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。 2 如果使用继承的思想,如下实现: 3 动物:吃、喝、拉、撒 4 猫:喵喵叫(猫继承动物的功能) 5 狗:汪汪叫(狗继承动物的功能) 6 7 伪代码如下: 8 9 class 动物: 10 def 吃(self): 11 # do something 12 def 喝(self): 13 # do something 14 def 拉(self): 15 # do something 16 def 撒(self): 17 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 18 class 猫(动物): 19 def 喵喵叫(self): 20 print 21 '喵喵叫' 22 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 23 class 狗(动物): 24 def 汪汪叫(self): 25 print 26 '汪汪叫'
(1)派升:比如上述代码中,现在动物类里面只有“吃喝拉撒”,但是子类里面,定义了父类没有的 “喵喵叫” 这就是派升。 父类无,子类有了,那这就是派升了。
注意:以上方法就是个例子,但是现实中,少用,因为这种方式把2个程序耦合起来了。一般设计程序的时候,需要程序经量解耦。
(2)接口继承
例:比如我们要写一个程序,这个程序必须要有读与写得功能。(比如,硬盘,光盘,都有读写功能),但是硬盘的读写和光盘的读写的方法是不一样的。
1 import abc 2 #导入这个模块来限制,不然就无法限制 3 class All_file(metaclass=abc.ABCMeta):#metaclass 记得这个 4 #我们限制所有的文件都有一个读和写得方法 5 #定义一个父类,规定所有的子类必须实现父类的功能,这就是接口继承 6 #父类不实现此方法,但是子类必须实现。 7 @abc.abstractmethod#需要限制哪个就在前面加这个 8 def read(self): 9 pass 10 @abc.abstractmethod 11 def write(self): 12 pass 13 class Disk(All_file):#自定义磁盘的功能,但是必须要有读写的功能,不然会报错 14 def read(self):# 15 print('disk read') 16 def write(self): 17 print('disk write') 18 class Cdrom(All_file):#自定义光盘的读写 19 def read(self): 20 print('cdrom read') 21 def write(self): 22 print('cdrom write') 23 m1=Cdrom() 24 m1.read() 25 m1.write()
类的继承顺序
python3 类都是新式类,所以都是按照广度优先
在子类里面怎么调用父类的方法
1 class Vehicle:#交通工具类 2 Country='China' 3 def __init__(self,name,speed,load,power):#名字,速度,承载,电源 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 def run(self): 9 print('开动啦') 10 print('开动啦') 11 #父类正常定义 12 class Subway(Vehicle):#地铁 13 def __init__(self,name,speed,load,power,line):#派升下,多一个参数 14 Vehicle.__init__(self.name,speed,load,power) 15 #实例化和对象的才会自动传参的时候,这里不是实例化,或者对象,所以要写self.传的是子类的 16 #在定义子类的时候,要把父类的init方法在放置在子类加载 17 self.line=line 18 def show_info(self):# 19 print(self.name,self.speed,self.load,self.power,self.line) 20 def run(self): 21 Vehicle.run(self)#调用父类的run方法 22 print('%s %s 线,开动啦' %(self.name,self.line)) 23 line1=Subway('西安地铁','10km/s',888,'电',1) 24 25 line1.show_info() #调用自己方法 26 #北京地铁 10km/s 888 电 13 27 line1.run()#开动啦,开动啦,西安地铁 1 线,开动啦
上面的方法并不完美,如果因某些原因,类名变量,或者什么原因,就会导致整个子类都要修改。所有用到了super函数
class Vehicle:#交通工具类 Country='China' def __init__(self,name,speed,load,power):#名字,速度,承载,电源 self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦') print('开动啦') #父类正常定义 class Subway(Vehicle):#地铁 def __init__(self,name,speed,load,power,line): super().__init__(name,speed,load,power) #用super可以调用到父类的方法 self.line=line def show_info(self):# print(self.name,self.speed,self.load,self.power,self.line) def run(self): super().run()#调用父类的run方法 :开动啦,开动啦 print('%s %s 线,开动啦' %(self.name,self.line)) line1=Subway('西安地铁','10km/s',888,'电',1) line1.show_info() #调用自己方法 #北京地铁 10km/s 888 电 13 line1.run()#开动啦,开动啦,西安地铁 1 线,开动啦
#特点,好处之一,不用传self参数,不用传类名了。
类的组合
当一个类的功能,自己没有别人有,但是他需要别的类的方法或者数据属性时,可以用组合
1 class Equip: #装备 2 def fire(self,name): 3 print('%s 购买了暴风大剑'% name) 4 5 class Hero: #英雄类,一个英雄需要购买装备,因而需要组合Equip类 6 camp='Noxus' 7 def __init__(self): 8 self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性 9 r1=Hero() 10 r1.equip.fire('盖伦') #可以使用组合的类产生的对象所持有的方法 11 #盖伦 购买了暴风大剑