Python-封装、继承和多态

  1. 封装、继承和多态
    1. 封装
    2. 继承
    3. 重写
    4. 多态

封装、继承和多态

封装、继承和多态,是面向对象编程的三大特征

封装

封装有两方面含义:
1、将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样就无需关心方法内部的具体实现细节,从而隔离了复杂度。
2、在类对象的内部,可以通过访问控制,把某些属性和方法隐藏起来,不允许在类对象的外部直接访问,而是在类对象的内部对外提供公开的接口方法(如getter、setter )以访问隐藏的信息,这样,就对隐藏的信息进行了保护

>>> class Student(object):
...     def __init__(self):
...         self.__score = 0
...     def set_score(self, score):
...         if 0 <= score <= 100:
...             self.__score = score
...         else:
...             raise ValueError('分数必须在0-100之间')
...     def get_score(self):
...         print(self.__score)
...
>>> 
>>> s1 = Student()
>>> s1.get_score()
0
>>>
>>> s1.set_score(-10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in set_score
ValueError: 分数必须在0-100之间
>>> s1.set_score(100)
>>>
>>> s1.get_score()
100

继承

当几个类对象中有共同的属性和方法时,就可以把这些属性和方法抽象并提取到一个基类中,每个类对象特有的属性和方法还是在本类对象中定义,这样,只需要让每个类对象都继承这个基类,就可以访问积累中的属性和方法了,继承基类的每个类对象被称为派生类,基类也被称为父类或者超类,派生类也被称为子类。

python中的所有类对象都继承自一个统一的基类:object。这就是为什么我们在定义类对象时要在类名后面添加(object)。

继承是实现代码复用的重要手段

#!/usr/bin/python3

class animals(object):
    def eat(self):
        print('吃饭')

    def drink(self):
        print('喝水')


class dog(animals):
    def run(self):
        print('奔跑')

class bird(animals):
    def fly(self):
        print('飞翔')

dog = dog()
bird = bird()

dog.eat()
dog.drink()
dog.run()

bird.eat()
bird.drink()
bird.fly()

继承的语法格式
子类只有一个直接父类时称为单继承,假设子类和父类分别为ChildClass和ParentClass,子类继承父类的语法格式为:

class ChildClass(ParentClass):
    pass

子类有多个直接父类时称为多继承,假设子类是ChildClass,直接父类是ParentClass1,ParentClass2,……,ParentClassn,子类继承父类的语法格式为:

class ChildClass(ParentClass1, ParentClass2, ...,ParentClassn):
    pass 

子类会继承所有父类(包括所有直接父类和所有间接父类)的所有属性和方法。

#!/usr/bin/python3

class ParentClassA(object):
    A1 = 'A1'

    def imA(self):
        pass

class ParentClassB(object):
    __B1 = 'B1'

    def __imB(self):
        pass

class ChildClassC(ParentClassA, ParentClassB):
    C1 = 'C1'

    def imC(self):
        pass

print(dir(ChildClassC))
# 'A1','imA','_ParentClassB__B1','_ParentClassB__imB' 属性和方法时继承自ParentClassA和ParentClassB,还有自己的属性和方法
[root@lyucan ~]# ./13.py
['A1', 'C1', '_ParentClassB__B1', '_ParentClassB__imB', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'imA', 'imC']

重写

如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其进行重写从而提供自定义的实现。
重写的方式为:在子类中定义与父类中同名的属性或方法(包括装饰器)。

重写的好处是可以具有多态特征

子类重写父类的属性(或方法)后通过子类或其实例只能访问子类中重写后的属性(或方法),而无法再访问父类中被重写的属性(或方法)。

#!/usr/bin/python3

class ParentClass(object):
    P1 = 'P1(父类)'

    def __init__(self):
        print('__init__()方法被调用了(父类)')

    def im(self):
        print('im()方法被调用了(父类)')

    @classmethod
    def cm(cls):
        print('cm()方法被调用了(父类)')

    @staticmethod
    def sm():
        print('sm()方法被调用了(父类)')

class ChildClass(ParentClass):
    P1 = 'C1(子类)'

    def __init__(self):
        print('__init__()方法被调用了(子类)')

    def im(self):
        print('im()方法被调用了(子类)')

    @classmethod
    def cm(cls):
        print('cm()方法被调用了(子类)')

    @staticmethod
    def sm():
        print('sm()方法被调用了(子类)')


pc = ParentClass()
print(pc.P1)
pc.im()
pc.cm()
pc.sm()

cc = ChildClass()
print(cc.P1)
cc.im()
cc.cm()
cc.sm()

多态

一个变量,具有多种形态,它指的是:即便不知道一个变量所引用的对象是什么类型,仍然可以通过这个变量调用方法,在程序运行过程中根据变量所引用的对象类型,动态的决定调用用哪个对象中的方法。如果子类中不存在指定名称的方法,会到父类中去查找,如果在父类中找到了,则调用父类中的方法,在父类中也没有找到,则抛出异常。

#!/usr/bin/python3

class ParentClass(object):
    def do_sth(self):
        print('ParentClass调用了do_sth()')

class ChildClass1(ParentClass):
    def do_sth(self):
        print('ChildClass1调用了do_sth()')

class ChildClass2(ParentClass):
    def do_sth(self):
        print('ChildClass2调用了do_sth()')

class ChildClass3(ParentClass):
    def do_sth(self):
        print('ChildClass3调用了do_sth()')

class ChildClass4(object):
    def do_sth(self):
        print('ChildClass4调用了do_sth()')


def func(p):
    p.do_sth()

func(ParentClass())
func(ChildClass1())
func(ChildClass2())
func(ChildClass3())
func(ChildClass4())
[root@lyucan ~]# vim 15.py
[root@lyucan ~]# ./15.py
ParentClass调用了do_sth()
ChildClass1调用了do_sth()
ChildClass2调用了do_sth()
ChildClass3调用了do_sth()
ChildClass4调用了do_sth()

python是动态语言,在调用函数时不会检查参数的类型,从而导致与静态语言(例如java)的多态是有区别的,对于静态语言,实现多态有三个必要条件:
1、继承
2、重写
3、父类类型的变量引用父类或子类类型的实例对象
因此对于静态语言,在上面程序中,要限定形参p的类型是ParentClass,传入的实参只能是ParentClass、ChildClass1、ChildClass2的实例对象。

动态语言的多态崇尚“鸭子类型”:当看到一只鸟走起来像鸭子,游泳起来像鸭子,并且叫起来也像鸭子,那么这只鸟可以被称为鸭子。
在鸭子类型中,我们并不关心对象是什么类型,到底是不是鸭子,我们只关心对象的行为。

在上面的程序中,我们并不关心变量p所引用的对象是什么类型,到底是不是ParentClass或其子类型,只关心变量p所引用的对象是否有do_sth()这个方法。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com