Python入门笔记—第七章【面向对象之OOP(第二部分,封装&继承)】

第七章:面向对象之OOP(第二部分,封装&继承)


2 面向对象的三大特性

2.1 封装

- 作用:对对象成员进行有限制的访问

- 3个级别

    - 公有成员,public

    - 受保护成员,protected

    - 私有成员,private

注:public,protected,private不是关键字,但是变量命名等尽量避开

- Python中下划线的使用(参考:https://blog.csdn.net/g11d111/article/details/71367649

    - object  #public(公有成员)

    - _object   #obey python coding convention, consider it as private(将其狭义的看做保护成员)

    - __object   #private(私有成员)

    - __object__   #special, python system use, user should not define like it

    (特殊成员,与公私有性质无关,如__init__表示构造函数,__doc__等)

其实在Python中并不存在保护成员的概念,如_object这样狭义上的保护成员在子类以及外部都可以进行访问。尽管可以在外部访问,但是需要把他看做私有成员,尽量不要在外部访问。

以双下划线开头是Python中最高级别的封装,即私有成员,只有类对象本身可以访问该成员。但是Python中private的私有并不是真的私有,而是一种name mangling的改名策略,使用【对象名._类名__私有成员名】即可访问

封装案例:

class Student():
    name = "xiaoming"
    _age = 18
    __school = "NCU"

s = Student()
print(s.name)        #访问公有成员
print(s._age)        #此处虽然可以访问,但是要将其看做私有成员
#print(s.__school)    #在外部不能访问私有成员,此处会报错
print("*" * 20)
 
print(Student.__dict__)  #查看类Student中的成员
s._Student__school = 19  #使用name mangling可以访问、修改私有成员,但是最好不要这样用
print(s._Student__school) 

结果:

xiaoming
18
********************
{'__module__': '__main__', 'name': 'xiaoming', '_age': 18, '_Student__school': 'NCU', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
19

2.2 继承

- 概念:一个类可以获得另一个类的成员属性和成员方法

- 作用:可以减少重复性的代码,增加类代码的服用功能,增强类与类之间的联系

- 继承与被继承的概念

    - 被继承的类称为父类,也叫基类或者超类

    - 继承的类称为子类,也叫派生类

    - 继承和被继承之间存在着is-a的关系,即

继承的语法:参见下列代码

#在Python中所有的类都有一个父类,即object

class Person():
    name = "xiaoming"
    age  = 18
    _petname = "mingming"  #
    __score  = 0
    def sleep(self):
        print("sleeping")
class Student(Person):         #类Student继承父类Person,父类名Person写在子类括号中
    school_id = "9527"         
    def homework(self):
        print("I must do homework")

s = Student()
print(s.name)          #用子类访问父类中的成员
print(s._petname)      #用子类访问父类中的保护成员,可以访问但最好不要使用,将其看做私有成员
#print(s.__score)      #用子类访问父类中的私有成员,此处会报错
print(s.sleep())       #用子类访问父类中的方法
print("*" * 20)        #分割线
print(s.school_id)     #子类访问自己的成员属性
print(s.homework())    #子类访问自己的成员方法

结果:

xiaoming
mingming
sleeping
None
********************
9527
I must do homework
None

- 继承的特征

    - 所有的类都继承自类object,即类object是所有类的父类

    - 子类可以使用父类除私有成员之外的所有内容

    - 子类在继承父类后并没有将父类成员全部传入子类当中,即没有另外开辟存储空间,而是通过引用关系访问(代码示例1)

    - 子类可以定义子类独有的属性和方法,也即增加代码的功能性(代码示例1)

    - 子类和父类中的成员相同,则优先使用子类中的成员(代码示例1)

    - 子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员进行代码重用。有下列两种方法:(代码示例2)

        - 父类名.父类成员

        - super().父类成员

代码实例1:

#在Python中所有的类都有一个父类,即object

class Person():
    name = "xiaoming"
    age  = 18
    _petname = "mingming"  #
    __score  = 0
    def sleep(self):
        print("sleeping")
class Student(Person):         #类Student继承父类Person,父类名Person写在子类括号中
    school_id = 9527           #此school_id为子类Student独有的属性
    name = "DaNa"

print(Person.__dict__)
print(Student.__dict__)
print("*" * 20)
s = Student()
print(s.name)                  #子类和父类拥有相同的成员,则优先使用子类中的成员

结果:

{'__module__': '__main__', 'name': 'xiaoming', 'age': 18, '_petname': 'mingming', '_Person__score': 0, 'sleep': <function Person.sleep at 0x0000000002813488>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'__module__': '__main__', 'school_id': 9527, 'name': 'DaNa', '__doc__': None}     
********************       #由此处可以看出,父类并没有把父类中的成员全部到子类中,而是通过子类引用的方法来访问父类中的成员
DaNa

代码实例2:

class Person():
    name = "xiaona"
    age  = 19
    def sleep(self):
        print("sleeping……………")
    def work(self):
        print("make some money")
class Teacher(Person):
    name = "dana"
    age  = 21
    def test(self):
        print("make the test")
    def work(self):    
        Person.work(self)        #扩充父类功能的方法1
        #super().work()          #扩充父类功能的方法2,super表示得到父类
        self.test()

t = Teacher()
t.work()

结果:

make some money
make the test

- 关于super:

    - super - super不是关键字, 而是一个类

    - super的作用是获取MRO(MethodResolustionOrder)列表中的第一个类

    - super于父类直接没任何实质性关系,但通过super可以调用到父类

    - super使用两个方,参见在构造函数中调用父类的构造函数

继承变量函数的查找顺序问题:

- 优先查找自己的变量,没有则往上查找父类

- 构造函数如果子类中没有定义,则往上查找父类;如果子类中有定义,则不再往上查找父类

2.3 构造函数

详解见Python入门笔记—番外篇【面向对象之构造函数】

2.4 单继承与多继承

- 单继承(上述关于继承的案例都是单继承

    - 每个类只能继承一个类

    - 优点:传承有序,语法简单,逻辑清晰,隐患少

    - 缺点:功能不能无限扩展,只能局限于当前继承链中的功能

- 多继承

    - 每个类允许继承多个类

    - 优点:类的功能扩展方便

    - 缺点:继承关系复杂,容易出错

代码实例3:

#多继承代码
class Person():
    def __init__(self,name):
        self.name = name
    def work(self):
        print("Working")

class Bird():
    def __init__(self,name):
        self.name = name
    def fly(self):
        print("Flying")

class Fish():
    def __init__(self,name):
        self.name = name
    def swim(self):
        print("Swimming")

#单继承
class Student(Person):
    def __init__(self,name):
        self.name = name
stu = Student("yueyue")
stu.work()

#多继承
class SwimMan(Person,Fish):
    def __init__(self,name):
        self.name = name
swi = SwimMan("yueyue")
print("*" * 20)
swi.work()
swi.swim()

#多继承
class SuperMan(Person,Bird,Fish):
    def __init__(self,name):
        self.name = name
sup = SuperMan("yueyue")
print("*" * 20)
sup.work()
sup.fly()
sup.swim()

结果:

Working
********************
Working
Swimming
********************
Working
Flying
Swimming

2.4.1 菱形继承/钻石继承问题

- 多个子类继承来自同一个父类,这些子类又被同一个类继承,形成一个菱形(钻石)的形状,如下图

                                                               

#菱形问题代码
class A():
    pass

class B(A):
    pass

class C(A):
    pass

class A(B,C):
    pass

- 对于解决菱形问题,一般采用MRO方法(Method Resolution Order,MRO)

- MRO具体参考:https://www.cnblogs.com/whatisfantasy/p/6046991.html

- Python 3中采用MRO C3的方法来解决菱形问题(上面链接有详解)

- MRO列表计算原则:

    - 子类永远在父类前面

    - 如果有多个父类,则根据继承语法中括号内类的书写顺序存放

    - 如果多个类继承了同一个父类,则孙子类只会选取继承语法括号中的第一个父亲的父类?

  

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章