面向对象三大特性
面向对象的三大特性是指:封装、继承和多态。
一、封装
封装,顾名思义就是将功能封装到对象中,需要调用功能时,直接通过调用对象中的功能即可。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
第一步:将内容封装到某处。
self 是一个形式参数,当执行 obj1 = Foo(‘wupeiqi‘, 18 ) 时,self 等于 obj1;当执行 obj2 = Foo(‘alex‘, 78 ) 时,self 等于 obj2;内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性,在内存中的存储形式如下:
第二步:从某处调用被封装的内容。
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过self间接调用
1、通过对象直接调用被封装的内容。
1 class Foo: 2 3 def __init__(self, name, age): 4 self.name = name 5 self.age = age 6 7 obj1 = Foo(‘lriwu‘, 18) 8 print obj1.name # 直接调用obj1对象的name属性 9 print obj1.age # 直接调用obj1对象的age属性 10 11 obj2 = Foo(‘linxian‘, 73) 12 print obj2.name # 直接调用obj2对象的name属性 13 print obj2.age # 直接调用obj2对象的age属性
2、通过self间接调用被封装的内容。
1 class Foo: 2 3 def __init__(self, name, age): 4 self.name = name 5 self.age = age 6 7 def detail(self): 8 print self.name 9 print self.age 10 11 obj1 = Foo(‘lriwu‘, 18) 12 obj1.detail() # Python默认会将obj1传给self参数,即:Foo.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 lriwu ;self.age 是 18 13 14 obj2 = Foo(‘lin‘, 73) 15 obj2.detail() # Python默认会将obj2传给self参数,即:Foo.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 lin ; self.age 是 78
二、继承
继承,即子类可以继承父类的内容。
例如:
猫可以:喵喵叫、吃、喝、玩
狗可以:汪汪叫、吃、喝、玩
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
1 class 猫: 2 3 def 喵喵叫(self): 4 print ‘喵喵叫‘ 5 6 def 吃(self): 7 # do something 8 9 def 喝(self): 10 # do something 11 12 def 玩(self): 13 # do something 14 15 16 class 狗: 17 18 def 汪汪叫(self): 19 print ‘喵喵叫‘ 20 21 def 吃(self): 22 # do something 23 24 def 喝(self): 25 # do something 26 27 def 玩(self): 28 # do somethingView Code
上述代码不难看出,吃、喝、玩是猫和狗都具有的功能,而我们却分别为猫和狗的类中编写了两次,代码重复。如果使用继承的思想,如下实现:
动物:吃、喝、玩
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能
1 class Animal: 2 3 def eat(self): 4 print "%s 吃 " %self.name 5 6 def drink(self): 7 print "%s 喝 " %self.name 8 9 def play(self): 10 print "%s 玩 " %self.name 11 12 13 class Cat(Animal): 14 15 def __init__(self, name): 16 self.name = name 17 self.breed = ‘猫‘ 18 19 def cry(self): 20 print ‘喵喵叫‘ 21 22 class Dog(Animal): 23 24 def __init__(self, name): 25 self.name = name 26 self.breed = ‘狗‘ 27 28 def cry(self): 29 print ‘汪汪叫‘ 30 31 32 # ######### 执行 ######### 33 34 c1 = Cat(‘小白家的小黑猫‘) 35 c1.eat() 36 37 c2 = Cat(‘小黑的小白猫‘) 38 c2.drink() 39 40 d1 = Dog(‘胖子家的小瘦狗‘) 41 d1.eat()View Code
方法的重写:
1 class F: 2 def f1(self): 3 print(‘F.f1‘) 4 5 def f2(self): 6 print(‘F.f2‘) 7 8 class S(F): 9 def s1(self): 10 print(‘S.s1‘) 11 12 def f2(self): 13 print(‘S.f2‘) 14 15 obj = S() 16 obj.f2() #S.f2 先再子类中查找方法,再去父类查找
调用父类的方法:
方式一:子类里通过父类类名调用父类的方法
1 class F: 2 def f1(self): 3 print(‘F.f1‘) 4 5 def f2(self): 6 print(‘F.f2‘) 7 8 class S(F): 9 def s1(self): 10 print(‘S.s1‘) 11 12 def f2(self): 13 F.f2(self) #通过类名调用方法,需要自己传self参数 14 print(‘S.f2‘) 15 16 obj = S() 17 obj.f2() 18 # 输出:先执行父类的方法 19 # F.f2 20 # S.f2
方式二:使用supper关键字调用父类的方法(建议使用supper,supper关键字只有新式类中才有)
1 class F: 2 def f1(self): 3 print(‘F.f1‘) 4 5 def f2(self): 6 print(‘F.f2‘) 7 8 class S(F): 9 def s1(self): 10 print(‘S.s1‘) 11 12 def f2(self): 13 super().f2() //类中调用父类方法,不需要自己传self参数。完整为super(S,self).f2(),这里(S,self)参数会自动传。 14 print(‘S.f2‘) 15 16 obj = S() 17 obj.f2() # F.f2 S.f2
18 super(S,obj).f2 # F.f2 类外调用父类方法,需要手动传参数
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Animal: ‘‘‘ 人和狗都是动物,所以创造一个Animal基类 ‘‘‘ def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print(‘%s is eating‘%self.name) class Dog(Animal): ‘‘‘ 狗类,继承Animal类 ‘‘‘ def __init__(self,name,breed,aggressivity,life_value): super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法 self.breed = breed #派生出了新的属性 def bite(self, people): ‘‘‘ 派生出了新的技能:狗有咬人的技能 :param people: ‘‘‘ people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) #super().eat() print(‘from Dog‘) class Person(Animal): ‘‘‘ 人类,继承Animal ‘‘‘ def __init__(self,name,aggressivity, life_value,money): #Animal.__init__(self, name, aggressivity, life_value) #super(Person, self).__init__(name, aggressivity, life_value) super().__init__(name,aggressivity, life_value) #执行父类的init方法 self.money = money #派生出了新的属性 def attack(self, dog): ‘‘‘ 派生出了新的技能:人有攻击的技能 :param dog: ‘‘‘ dog.life_value -= self.aggressivity def eat(self): #super().eat() Animal.eat(self) print(‘from Person‘) egg = Person(‘egon‘,10,1000,600) ha2 = Dog(‘二愣子‘,‘哈士奇‘,10,1000) print(egg.name) print(ha2.name) egg.eat()派生例子
总结
父类中没有的属性在子类中出现,叫做派生属性;
父类中没有的方法在子类中出现,叫做派生方法;
只要是子类的对象调用,子类中存在名字就一定用子类的。子类中不存在该名字才会到父类中查找,如果父类也不存在该名字就报错;
父类、子类都有的名字,用子类的。如果类、子类都有的名字,想调用父类的名字就需要在子类中调用父类的名字;
多继承:
1、Python的类可以继承多个类,Java和C#中则只能继承一个类;
2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先。
python2.0:
- 当类是经典类时,多继承情况下,会按照深度优先方式查找;
- 当类是新式类时,多继承情况下,会按照广度优先方式查找;
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了更多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
python3.0:
python3中所有的类都是新式类,多继承情况下,会按照广度优先方式查找。
经典类多继承:(深度优先)
class D: def bar(self): print ‘D.bar‘ class C(D): def bar(self): print ‘C.bar‘ class B(D): def bar(self): print ‘B.bar‘ class A(B, C): def bar(self): print ‘A.bar‘ a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中没有,则继续去D类中找,如果D类中没有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()经典类多继承
新式类多继承:(广度优先)
1 class D(object): 2 3 def bar(self): 4 print ‘D.bar‘ 5 6 7 class C(D): 8 9 def bar(self): 10 print ‘C.bar‘ 11 12 13 class B(D): 14 15 def bar(self): 16 print ‘B.bar‘ 17 18 19 class A(B, C): 20 21 def bar(self): 22 print ‘A.bar‘ 23 24 a = A() 25 # 执行bar方法时 26 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中没有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 27 # 所以,查找顺序:A --> B --> C --> D 28 # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 29 a.bar()新式类多继承
例子:
1 class BaseReuqest: 2 3 def __init__(self): --------------------------------------------->3 4 print(‘BaseReuqest.init‘) 5 6 7 class RequestHandler(BaseReuqest): 8 def __init__(self): ------------------------------------------>2 9 print(‘RequestHandler.init‘) 10 BaseReuqest.__init__(self) 11 12 def serve_forever(self): ------------------------------------->5 13 # self,是obj 14 print(‘RequestHandler.serve_forever‘) 15 self.process_request() ------------------------------------->6 从头开始查找即先在Minx类中查找,无则再在RequestHandler类中查找,最后执行的时Minx类中process_request方法 16 17 def process_request(self): 18 print(‘RequestHandler.process_request‘) 19 20 class Minx: 21 22 def process_request(self): ----------------------------------->7 23 print(‘minx.process_request‘) 24 25 26 class Son(Minx, RequestHandler): 27 pass 28 29 30 obj = Son() # 先查找是否有构造方法,有就执行,无就不执行构造方法 ------>1 31 obj.serve_forever() ------------------------------------------->4
三、多态
继承:正常情况下,父类不能调用子类的方法;
多态:可以实现父类调用子类的方法。
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
class F1: pass class S1(F1): def show(self): print ‘S1.show‘ class S2(F1): def show(self): print ‘S2.show‘ # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类F1 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.showPython伪代码实现Java或C#的多态
class F1: pass class S1(F1): def show(self): print ‘S1.show‘ class S2(F1): def show(self): print ‘S2.show‘ def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)Python “鸭子类型”