面向对象三大特性
一、封装
封装是面向对象的三大特征之一。它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类提供的方法来实现对内部信息的操作和访问。
我认为封装是面向对象编程语言对客观世界的模拟,在客观世界里,对象的状态信息都被隐藏在对象内部,外界无法直接操作和修改。对一个类或对象实现良的封装,有以下好处:
隐藏实现细节:外部代码不需要了解对象的内部实现,只需调用提供的接口即可。
提高安全性:可以限制对对象内部数据的直接访问,从而避免不合理的修改。
简化接口:将复杂的实现隐藏起来,提供简单易用的接口,使得使用者只需要关心如何使用而不需要了解内部的复杂实现。
提高代码可维护性:修改内部实现不会影响到外部代码,只需要确保接口的行为保持一致即可。
增强代码的可重用性:封装使得代码模块可以在不同的项目中复用,提高了开发效率。
从设计角度讲,封装应该遵循:
- 分而治之
- 将一个大的需求分解为多个类,每个类处理一个独立的功能。
- 拆分的好处:便于分工,便于复用,可扩展性强。
- 变则疏之
- 变化的地方独立封装,避免影响其他类。
- 高内聚
- 类中各个方法都在完成一项任务(单一职责的类)。
- 低耦合
- 类与类的关联性与依赖性要低(每个类独立),让一个类的改变,尽少影响其他类。
二、继承
继承可以让子类具有父类的特征,提高了代码的复用性。
从设计上是一种增量净化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
吃、喝、拉、撒是猫和狗都具有的功能,如果分开写猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
PS:在Python3中所有类均继承于object类,故没有经典类和新式类的说法,在Python2中继承于object类的称为新式类,没有继承于object类称为经典类。
继承的实际案例:
"""继承的实际案例"""
class People():
school = 'SH'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(People):
def __init__(self, name, age, gender, course=None):
if course is None:
course = []
"""子类里面一定再次调用父类的__init__方法"""
People.__init__(self, name, age, gender) # 指名道姓的调用方法
self.course = course
def choose_course(self):
print('%s 选课成功, %s' % (self.name, self.course))
stu = Student('ly', 20, 'male')
print(stu.school) # SH 属性的查找
print(stu.name)
print(stu.age)
print(stu.gender)
class Teacher(People):
def __init__(self, name, age, gender, level):
People.__init__(self, name, age, gender)
self.level = level
def score(self, stu_name):
print('%s 得了%s分' % (stu_name, 10))
tea = Teacher('kevin', 19, 'female')
print(tea.name)
print(tea.age)
print(tea.gender)
单继承下的属性查找
单继承下的属性查找:先从对象自己名称空间中查找,然后去产生这个对象的类中查找,最后去继承的父类中查找。
class A():
def f2(self):
pass
class B(A):
def __f1(self): # _Foo_f1
print("from Foo.f1")
class Bar(Foo):
def __f1(self): # _Bar_f1
print("from Bar.f1")
obj = Bar()
obj.f2()
多继承下的属性查找
多继承下分菱形查找和非菱形查找
菱形查找分 经典类 和 新式类
经典类:按照深度优先的查找顺序
新式类:按照广度优先查找
以下为经典类深度优先,了解即可。
class G: # 在python2中,未继承object的类及其子类,都是经典类
def test(self):
print('from G')
class E(G):
def test(self):
print('from E')
class F(G):
def test(self):
print('from F')
class B(E):
def test(self):
print('from B')
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B,C,D):
# def test(self):
# print('from A')
pass
obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
mro和C3算法
如果类中存在继承关系,在可以通过mro()获取当前类的继承关系,(找成员的顺序)。而mro算法是通过C3算法实现的。
示例:
如上图:A 继承于B和C,则mro如下:
mro(A) = [A] + [B, C]
mro(A) = [A, B, C]
C3算法如下:
mro(A) = [A] + merge(mro(B), mro(C), [B,C])
mro(A) = [A] + merge([B, object], [C, object], [B,C])
mro(A) = [A] + [B] + merge([object], [C, object], [C])
mro(A) = [A] + [B,C] + merge([object, object])
mro(A) = [A] + [B, C, object]
mro(A) = [A, B, C, object]
super的使用
super()代表父类,可通过super调用父类方法等:
示例:
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A, B):
pass
多态
多态是指同一个方法调用由于对象不同会产生不同的行为。父类的同一种动作或者行为,在不同的子类上有不同的实现。多态增加了程序的灵活性。增加程序的可扩展性。
来看一个没有多态的代码:
class ArmyDog(object):
def bite_enemy(self):
print("追击敌人")
class DrugDog(object):
def track_drug(self):
print("追查毒品")
class Person(object):
def work_with_army(self,dog):
dog.bite_enemy()
def work_with_drug(self,dog):
dog.track_drug()
p = Person()
p.work_with_army(ArmyDog())
p.work_with_drug(DrugDog())
不难看出,如果添加一个类,继承dog,代码的增加是很麻烦的。
下面看一个有多态的情形:
class Dog(object):
def work(self):
pass
class ArmyDog(Dog):
def work(self):
print("追击敌人")
class DrugDog(Dog):
def work(self):
print("追击毒品")
class Person(object):
def work_with_dog(self, dog):
dog.work()
p = Person()
p.work_with_dog(ArmyDog())
p.work_with_dog(DrugDog())
这样,类的添加就显得很方便。
PS:多态度要注意一下两个点
1. 多态是方法的多态,属性没有多态。
2. 多态以继承和重写父类方法为前提
可以通过abc模块限制子类必须对父类方法进行重写
示例:
import abc # abstract class 抽象类 具体的Specific
class Animal(metaclass=abc.ABCMeta): # 把animal类变成了抽象类
"""父类中得方法不是为了实现逻辑的,实现功能的,而是单纯的为了限制子类的行为"""
@abc.abstractmethod # 把抽象类中得方法变成抽象方法, 它不实现具体的功能,就是单纯的为了限制子类中的方法
def speak(self):
pass
@abc.abstractmethod
def jiao(self):
pass
"""抽象类和普通类有什么区别? 抽象类只能够被继承、不能够被实例化"""
# Animal() # Can't instantiate abstract class Animal with abstract methods speak
鸭子类型
"""Python崇尚的是鸭子类型"""
"""鸭子类型就是更多的关注的是对象的行为,而不是对象的类型"""
class People(Animal):
def speak(self):pass
# print('from People.speak')
def jiao(self):
pass
class Dog(Animal):
def speak(self):
pass
class Pig(Animal):
def speak(self):
pass
"""多态带来的特性:在不考虑对象类型的情况下,直接调用对象的方法或者属性"""
def animal(obj):
return obj.speak()
animal(obj)
animal(obj1)
animal(obj2)
"""面试题:请举出Python中使用多态的例子:len"""
len('hello')
len([1,2,3,4])
len((1,2,3,4))
def len(obj):
return obj.__len__
len('helloworld')
len([1,2,3,4])
len((1,2,3,4))