Python面向对象的三大特征

面向对象三大特征

封装、继承、多态作为面向对象编程的三大特征,其概念也是循序渐进的,首先需要有类的概念,随后将类进行封装,对外只暴露需要暴露的部分,简化对外交互;而后类与类之间会有继承关系;进而有了多态的概念。

封装

概念

封装是指一种将抽象性函数接口的实现细节部分包装、隐藏起来的方法。同时,它也是一种防止外界调用端访问对象内部实现细节的手段。

原则

封装的目的是隐藏对象的属性和实现细节,仅对外公开访问方法,并且控制访问级别。所以对于封装的原则,大致可以归纳为高内聚,低耦合。
1、高内聚:高内聚是指一个模块中各个部分之间关联应该是紧密的。
2、低耦合:低耦合是指多个模块之间的关联应该是松散的。

评价标准

不同程序员对于同样的类也可能会有不同的封装方法,评价类是否较好的完成封装这一特征的标准大致包含以下几方面:

  • 功能单一性
  • 可扩展性
  • 明确的输入输出

继承

概念

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

抽象

所谓抽象,与我们平时理解的抽象大致相同。就是将有共同特征的一类事物进行归纳,将其共有的、本质性的特征归纳抽象出来。

继承

所谓继承,就是基于抽象出来的共同特征定义父类之后,需要定义和父类具有相同的属性和方法的子类,即称子类继承于父类,子类中仍可以重新定义、追加属性和方法等。

  • 继承与重用的区别
    所谓代码重用是将已经编写完成的代码复制,在新的编码中重用之前程序已经完成的代码功能,代码重用可以虽然可以节省开发周期,简化编码难度。
    而继承与重用的核心区别就是继承是基于对类进行抽象之后,对于类进行的抽象归纳之后认为其所具有的相同的方法/属性。对于编码而言,不需要将原有代码复制到新的编码之中,而只需要声明其继承于某个已经完成的类作为父类即可。相比重用,减少了代码量,增加了代码的可读性。

继承相关名词概念解释

  • 父类 & 子类
    所谓父类即作为抽象的类可以产生相关子类的类。
    所谓子类就是通过继承其他类所创建出来的类。

  • 基类 & 派生类
    基类概念同父类,派生类概念同子类。

  • 新式类 & 经典类

    • 新式类:
      1、在Python 3.x中取消了经典类,默认都是新式类,创建新式类时不必显式的继承object。
      2、新式类多继承的原则是:广度优先。总结而言,即在新式类中对于子类继承多个父类的情况,如果继承的多个父类中有属性相同的,则越靠后继承的父类中的属性会覆盖靠前继承的父类的相同属性。
      3、新式类中增加了__slots__内置属性, __slots__的作用是可以限定实例属性的种类。
      4、新式类中增加了__getattribute__方法,对于访问类/实例中任何存在与否的方法/属性时都会调用__getattribute__方法。
      5、新式类内置增加了__new__方法。

    • 经典类:
      1、在Python2.x中,默认都是经典类,只有显式继承了object创建的才是新式类。
      2、经典类多继承的原则是:深度优先。总结而言,即在新式类中对于子类继承多个父类的情况,如果继承的多个父类中有属性相同的,则越靠前继承的父类中的属性会覆盖靠后继承的父类的相同属性。

  • 单继承 & 多继承
    单继承是指子类只可以继承自一个父类。
    多继承是指一个子类同时继承自多个父类。

抽象类&接口类

  • 抽象类
    抽象类只能被继承,而不能被实例化。其作用在于规定继承它的子类需要实现什么方法,而不是其本身实现了什么功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import abc  # 利用abc模块实现抽象类

# 定义父类Person
class Person(metaclass=abc.ABCMeta):
all_type = 'person'
@abc.abstractmethod # 定义抽象方法,无需具体实现
def sleep(self):
'子类需定义睡觉方法'
pass

@abc.abstractmethod # 定义抽象方法,无需具体实现
def eat(self):
'子类需定义吃饭方法'
pass

# 定义子类Student
class Student(Person):
pass
student = Student() # 此处抛出异常,因为子类没有定义抽象方法

# 定义子类Worker
class Worker(Person):
def sleep(self):
print('Worker睡觉方法')

def eat(self):
print('Worker吃方法')

# 定义子类Teacher
class Teacher(Person):
def sleep(self):
print('Teacher睡觉方法')

def eat(self):
print('Teacher吃方法')

# 定义子类Farmer
class Farmer(Person):
def sleep(self):
print('Farmer睡觉方法')

def eat(self):
print('Farmer吃方法')

worker=Worker()

teacher=Teacher()

farmer=Farmer()

# 此处体现了归一化思想,即相同方法在不同子类中的具体实现不同,但是调用者的调用方式相同
worker.sleep()
worker.eat()

teacher.sleep()
teacher.eat()

farmer.sleep()
farmer.eat()
  • 接口类
    继承的作用可以大致分类两方面:代码复用和实现接口继承。
    那么,什么是接口继承呢?接口继承的意义在于需要一种抽象类,该抽象类实现了一种兼容功能,使得调用者无需关心类的内部实现细节,便可以统一处理特定接口。这被称为”归一化”。归一化的意义在于外部功能的调用者不用关注细节,便可以统一的处理所有接口兼容的对象。
    同时,便要引出编程的”依赖倒置”原则。即:
    1、高层模块不应该依赖低层模块,二者都应该依赖其抽象;
    2、抽象不应该应该依赖细节,细节应该依赖抽象。
    总而言之,即程序要针对接口编程,而不是针对实现编程

多态

概念

多态指的是一类事物具有多种形态,同时,多态是继承的具体展现形式。

多态性

多态性是指在不考虑实例类型的情况下使用实例。

鸭子类型(Duck Type)

  • 鸭子类型概念
    If it walks like a duck and quacks like a duck, it must be a duck。(如果它走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。)
    鸭子类型是计算机领域中的一种设计风格,其意义即对象的特征不取决于其父类,而取决于对象本身的实现。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import abc

# 定义父类Person
class Person(metaclass=abc.ABCMeta):
@abc.abstractmethod
def eat(self):
'子类需定义吃饭方法'
pass


# 定义子类Worker
class Worker(Person):
def eat(self):
print('Worker吃方法')

# 定义子类Teacher
class Teacher(Person):
def eat(self):
print('Teacher吃方法')

# 定义子类Farmer
class Farmer(Person):
def eat(self):
print('Farmer吃方法')

worker=Worker()
teacher=Teacher()
farmer=Farmer()

# 此处worker、teacher、farmer都有吃的方法,但是具体的实现不同,所以eat方法本身不取决于Person父类中的eat方法,而是由每个子类所产生的实例自己决定
worker.eat()
teacher.eat()
farmer.eat()

# 我们也可以进一步的统一调用方式
def someone_eat(obj):
obj.eat()

someone_eat(worker)
someone_eat(teacher)
someone_eat(farmer)