写给小白的Python之017:面向对象-封装、继承、多态

 

1. 封装

面向对象三大特性:封装、继承、多态

封装(Encapsulation)这是定义类的 准则,单个类。根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中。

封装的意义:

1.将属性和方法放到一起做为一个整体,然后通过实例化对象来处理;

2.隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了;

3.对类的属性和方法增加 访问权限控制。

 

2. 继承

继承:这是设计类的 技巧,父与子。

  • 主要体现是实现代码的 重用,相同的代码不需要重复的编写;
  • 子类可以在父类功能上进行重写,扩展类的功能。

在面向对象编程中,当我们已经创建了一个类,而又想再创建一个与之相似的类,比如添加几个方法,或者修改原来的方法,这时我们不必从头开始,可以从原来的类派生出一个新的类, 我们把原来的类称为父类或基类,而派生出的类称为子类或派生类,子类继承了父类的所有数据和方法。

从技术上说, OOP里继承最主要的用途是实现多态。对于多态而言,重要的是接口继承性,属性和行为是否存在继承性,这是不一定的。事实上, 大量工程实践表明,重度的行为继承会导致系统过度复杂和臃肿,反而会降低灵活性。因此现在比较提倡的是基于接口的轻度继承理念。这种模型里因为父类(接口类)完全没有代码,因此根本谈不上什么代码复用。

2.1 单继承

子类的定义如下:

class 子类名(父类名):

    <statement-1>

    ...

    <statement-N>

父类名 BaseClassName 对于子类来说必须是可见的。也可以继承在其他模块中定义的父类:

class 子类名(模块名.父类名):

对于子类的属性引用:首先会在当前的子类中搜索,如果没有找到,则会递归地去父类中寻找。

从C++术语上讲,Python 类中所有的方法都是 vitual 的,所以子类可以覆写(override)父类的方法。在子类中一个覆写的方法可能需要调用父类的方法,可以通过以下方式:

父类名.方法(self, arguments)

 

2.2 多继承

Python支持多继承,一个多继承的定义形如:

class 子类名(父类名1, 父类名2, 父类名3):

多个父类有同名属性和方法

子类的魔法属性__mro__决定了属性和方法的查找顺序。如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性mro的顺序来查找)

子类重写父类的属性和方法

子类和父类的方法名和属性名相同,则默认使用子类的,叫 子类重写父类的同名方法和属性。

使用重写的目的:当子类发现父类的大部分功能都能满足需求,但是有一些功能不满足,则子类可以重写父类方法。

2.3 调用父类方法

重写之后,如果发现仍然需要父类方法,则可以强制调用父类方法。

方法1. 指定执行父类的方法

无论何时何地,self都表示是子类的对象。在调用父类方法时,通过传递self参数,来控制方法和属性的访问修改。通常用于多继承。

  • 父类名.父类方法(self, 参数列表)
  • 父类名().属性/方法   # 不推荐这样访问父类的实例属性,相当于创建了一个新的父类对象

子类继承了多个父类,如果父类类名修改了,那么子类也要涉及多次修改。而且需要重复写多次调用,显得代码臃肿。

方法2. super() 带参数版本

只支持新式类,支持Python2和3。工作中使用这个。

  • super(子类名, self).父类方法(参数列表)

# super(子类名, self).__init__() # 执行父类的 __init__方法

# self.方法名()

示例:

class Animal:

    def __init__(self, age):  # 1.父类有时候需要接收一些参数
        self.age = age


class Cat(Animal):

    def __init__(self, age):  # 3.一般情况下,父类需要的参数,子类也是动态来获取
        self.name = '伊丽莎白'
        super(Cat, self).__init__(age)  # 2.子类需要为父类传递实参


cat = Cat(12)  # 4. 根据子类需求,传递对应的实参
print(cat.age)  # 12

方法3. super()的简化版

只支持新式类,只支持Python3

  • super().父类方法(参数列表)  # 执行父类的 实例方法

super().__init__()  # 执行父类的 __init__方法

知识点:

  • 使用super() 可以逐一调用所有的父类方法,并且只执行一次。调用顺序遵循 mro 类属性的顺序。
  • 注意:如果继承了多个父类,且父类都有同名方法,则默认只执行第一个父类的(同名方法只执行一次,目前super()不支持执行多个父类的同名方法)
  • super() 在Python2.3之后才有的机制,用于通常单继承的多层继承。

3. 多态

多态是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。即 不同类的对象调用相同方法,产生不同的结果。

多态(Polymorphism):

  • 不同的子类对象调用相同的父类方法,产生不同的 执行结果;父类能工作的地方,子类都能工作;
  • 多态以继承和重写父类方法为前提;
  • 多态是调用方法的技巧,不会影响到类的内部设计。

多态的好处:

在保证安全性的前提下,提高了方法调用的灵活性。

多态的实现:

1.定义一个父类

2.定义多个子类,并重写父类的方法

3.传递子类对象给调用者,不同子类对象能产生不同执行效果

示例:

class Dog(object):
    def work(self):  # 父类提供统一的方法,哪怕是空方法
        pass

class ArmyDog(Dog):   # 继承 Dog
    def work(self):  # 子类重写方法,并且处理自己的行为
        print('追击敌人')

class DrugDog(Dog):
    def work(self):
        print('追查毒品')

class Person(object):
    def work_with_dog(self, dog):
        dog.work()    # 使用小狗可以根据对象的不同而产生不同的运行效果, 保障了代码的稳定性
 

# 子类对象可以当作父类来使用
dog = Dog()
print(isinstance(dog, Dog))  # True

ad = ArmyDog()
print(isinstance(ad, Dog))  # True

dd = DrugDog()
print(isinstance(dd, Dog))  # True

p = Person()
p.work_with_dog(dog)
p.work_with_dog(ad)  # 同一个方法,只要是 Dog 的子类就可以传递,提供了代码的灵活性
p.work_with_dog(dd)  # 并且传递不同对象,最终 work_with_dog 产生了不同的执行效果

# 最终效果
# Person 类中只需要调用 Dog 对象 work() 方法,而不关心具体是 什么狗
# work() 方法是在 Dog 父类中定义的,子类重写并处理不同方式的实现
# 在程序执行时,传入不同的 Dog 对象作为实参,就会产生不同的执行效果

多态总结:

# 定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。

# 好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!

鸭子类型:

动态语言的“鸭子类型”(duck typeing),它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。即只要一个对象相似的属性和方法。

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

类型检查是毁掉多态的利器, 比如type、 isinstance以及isubclass函数,所以,一定要慎用这些类型检查函数。

猜你喜欢

转载自www.cnblogs.com/salmond/p/9001257.html