美文网首页
11-Python继承

11-Python继承

作者: 墨雨love薏雪 | 来源:发表于2018-11-17 19:08 被阅读21次

    一、封装【private】

    1.概念

    广义的封装:函数和类的定义本身,就是封装的体现

    狭义的封装:一个类的某些属性,在使用的过程 中,不希望被外界直接访问,而是把这个属性给作为私有的【只有当前类持有】,然后暴露给外界一个访问的方法即可【间接访问属性】

    封装的本质:就是属性私有化的过程

    封装的好处:提高了数据的安全性,提高了数据的复用性

    说明:举例:插排,不需要关心属性在类的内部做了什么样的操作,只需要关心将值传进去,或者将结果获取出来

    2.属性私有化

    如果想让成员变量不被外界直接访问,则可以在属性名称的前面添加两个下划线__,成员变量则被称为私有成员变量

    私有属性的特点:只能在类的内部直接被访问,在外界不能直接访问

    代码演示:

    #1.属性不私有化的时候
    class Person():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def myPrint(self):
            print(self.name,self.age)
    
    #通过构造函数给属性赋值
    per = Person("张三",10)
    per.myPrint()   #张三 10
    #通过对象直接访问属性,并且给属性赋值
    per.name = "李四"
    per.age = 22
    per.myPrint()   #李四 22
    
    #2.属性私有化
    #写法:在属性的前面添加两个下划线
    #用法:只能在类的内部被访问,外界不能直接访问
    class Person1():
        def __init__(self,name,age):
            self.name = name
            self.__age = age
    
        def myPrint(self):
            print(self.name,self.__age)
    
    p1 = Person1("abc",10)
    p1.myPrint()   #abc 10
    p1.name = "hello"
    #其实动态绑定属性,age和__age其实是两个不同的变量
    p1.age = 222
    p1.myPrint()
    print(p1.age)
    
    #AttributeError: 'Person1' object has no attribute '__age',私有化了,在外界不能直接访问
    #print(p1.__age)
    

    3.get函数和set函数

    get函数和set函数并不是系统的函数,而是自定义的,为了和封装的概念相吻合,起名为getXxx和setXxx

    get函数:获取值

    set函数:赋值【传值】

    代码演示:

    #3.get函数和set函数
    class Person2():
        def __init__(self,name,age):
            self.name = name
            self.__age = age
            #特殊情况一
            self.__weight__ = 20.0
            #特殊情况二
            self._height = 155.0
    
        def myPrint(self):
            print(self.name,self.__age)
    
        # 书写私有属性age的get函数和set函数【通过自定义的函数进行私有属性的赋值和获取值,暴露给外界】
        """
        get函数和set函数并不是系统的函数,而是自定义的,为了和封装的概念相吻合,起名为getXxx和setXxx
        get函数:获取值
        set函数:赋值【传值】
        """
        #set函数:给成员变量赋值
        #命名方式:setXxx
        #特点:需要设置参数,参数和私有成员变量有关
        def setAge(self,age):
            #数据的过滤
            if age < 0:
                age = 0
            self.__age = age
        #get函数:获取成员变量的值
        #命名方式:getXxx
        #特点:需要设置返回值,将成员变量的值返回
        def getAge(self):
            return self.__age
    
        #注意:有几个私有属性,则书写几对get函数和set函数
    
    p2 = Person2("abc",10)
    p2.myPrint()   #abc 10
    #print(p2.__age)
    #间接的访问了私有的成员变量
    print(p2.getAge())
    p2.setAge(22)
    print(p2.getAge())
    
    p2.setAge(-20)
    print(p2.getAge())
    
    #总结:通过将属性私有化之后,然后提供get函数和set函数,外部代码就不能随意更改成员变量的值,这样在一定程度上保证了数据的安全性
    
    #4.工作原理【了解】
    #当编译器加载了程序之后,不能直接访问p2.__age,Python解释器把__age解释成_Person2__age
    #p2.__age = 100
    p2._Person2__age = 100
    print(p2.getAge())
    
    #5.特殊情况:尽量不要直接访问
    #a.在一个变量的前后各加两个下划线,在Python中被认为特殊成员变量,将不再属于私有变量
    #print(p2.__weight__)
    #b.特殊变量
    #print(p2._height)
    
    #面试题:下面变量的含义
    """
    xxx:普通的变量
    _xxx:受保护的变量,不建议使用这种形式
    __xxx:表示私有的,外界无法直接访问,只能通过暴露给外界的函数访问
    __xxxx__:一般是系统的内置变量,比如:__name__,__solts__,自定义标识符的时候尽量不要使用这种形式
    """
    

    4.@property装饰器

    装饰器的作用:可以给函数动态添加功能,对于类的成员方法,装饰器一样起作用

    Python内置的@property装饰器的作用:将一个函数变成属性使用

    @property装饰器:简化get函数和set函数

    使用:@property装饰器作用相当于get函数,同时,会生成一个新的装饰器@属性名.settter,相当于set函数的作用

    作用:使用在类中的成员函数中,可以简化代码,同时可以保证对参数做校验

    代码演示:

    class Person1():
        def __init__(self,name,age):
            self.__name = name
            self.__age = age
    
        def myPrint(self):
            print(self.__name,self.__age)
    
        """
       def setAge(self,age):
            #数据的过滤
            if age < 0:
                age = 0
            self.__age = age
    
        def getAge(self):
            return self.__age
        """
    
        #注意:函数的命名方式:变量的名称
        #作用:相当于get函数,设置返回值,将成员变量的值返回
        @property
        def age(self):
            return  self.__age
    
        #注意:函数的命名方式:需要和@property中函数的命名保持一致
        #作用:相当于set函数,设置参数,给成员变量赋值
        @age.setter
        def age(self,age):
            if age < 0:
                age = 0
            self.__age = age
    
        @property
        def name(self):
            return  self.__name
    
        @name.setter
        def name(self,name):
            self.__name = name
    
    
    p1 = Person1("abc",10)
    p1.myPrint()   #abc 10
    #p1.setAge(20)
    #print(p1.getAge())
    
    print(p1.age)  #10
    p1.age = 18   #相当于调用了set函数,将18传值,实质调用的是@age.setter修饰的函数
    print(p1.age) #相当于调用了get函数,将成员变量的值获取出来,实质调用的是@peoperty修饰的函数
    
    p1.name = "zhangsan"
    print(p1.name)
    

    5.私有方法

    如果类中的一个函数名前面添加__,则认为这个成员函数时私有化的

    特点:也不能在外界直接调用,只能在类的内类调用

    代码演示:

    class Site():
        def __init__(self,name):
            self.name = name
    
        def who(self):
            print(self.name)
            self.__foo()
    
        #私有成员方法,只能在当前类的内部内调用
        def __foo(self):    #私有函数
            print("foo")
    
        def foo(self):    #公开函数
            print("foo~~~~")
    
        #注意:以上两个函数是两个不同的函数,不存在覆盖的问题
    
    s = Site("千锋")
    s.who()
    #s.__foo()  #AttributeError: 'Site' object has no attribute 'foo'
    s.foo()
    

    二、继承【extends】

    1.概念

    如果两个或者两个以上的类具有相同的属性或者成员方法,我们可以抽取一个类出来,在抽取的类中声明公共的部分

    被抽取出来的类:父类,基类,超类,根类
    
    两个或者两个以上的类:子类,派生类
    
    他们之间的关系:子类  继承自  父类
    

    注意:

    a.object是所有类的父类,如果一个类没有显式指明它的父类,则默认为object
    
    b.简化代码,提高代码的复用性
    

    2.单继承

    2.1使用

    简单来说,一个子类只能有一个父类,被称为单继承

    语法:

    父类:

    class 父类类名(object):

    类体【所有子类公共的部分】
    

    子类:

    class 子类类名(父类类名):

    类体【子类特有的属性和成员方法】
    

    说明:一般情况下,如果一个类没有显式的指明父类,则统统书写为object

    代码演示:

    person.py文件【父类】

    #1.定义父类
    class Person(object):
        #构造函数【成员变量】
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    
        #成员方法
        def show(self):
            print("show")
    
        def __fun(self):
            print("fun")
    

    worker.py文件【子类1】

    from  extends01.person import Person
    
    #2.定义子类
    class Worker(Person):
        #构造函数【成员变量】
        def __init__(self,name,age,job):
            """
            self.name = name
            self.age = age
            """
            self.job = job
    
            #6.在子类的构造函数中调用父类的构造函数【从父类中继承父类中的成员变量】
            #方式一:super(当前子类,self).__init__(属性列表)
            #super(Worker, self).__init__(name,age)
            #方式二:父类名.__init__(self,属性列表)
            Person.__init__(self,name,age)
            #方式三:super().__init__(属性列表)
            #super().__init__(name,age)
    
    
        #成员方法
        def work(self):
            print("work")
    

    student.py文件【子类2】

    from extends01.person import  Person
    
    class Student(Person):
        # 构造函数【成员变量】
        def __init__(self, name, age, score):
    
            Person.__init__(self,name,age)
            self.score = score
    
        # 成员方法
        def study(self):
            print("study")
    

    extendsDemo01.py文件【测试模块】

    #测试模块
    from extends01.person import Person
    from extends01.worker import Worker
    from extends01.student import Student
    
    #3.创建父类的对象
    p = Person("zhangsan",10)
    p.show()
    #p.__fun()
    
    #4.创建子类的对象
    w = Worker("aaa",20,"工人")
    w.work()
    
    #5.子类对象访问父类中的内容
    #结论一:子类对象可以调用父类中的公开的成员方法【因为继承,私有方法除外】
    w.show()
    #w.__fun()
    #结论二:通过在子类的构造函数中调用父类的构造函数,子类对象可以直接访问父类中的成员变量【私有变量除外】
    print(w.name,w.age,w.job)
    
    s = Student("小明",9,90)
    s.study()
    s.show()
    

    2.2特殊用法

    代码演示:

    #6.子类中出现一个和父类同名的成员函数,则优先调用子类中的成员函数
    #子类的成员函数覆盖了父类中的同名的成员函数
    s = Student("小明",9,90)
    s.study()
    s.show()
    
    #7.父类对象能不能访问子类中特有的成员函数和成员变量?----->不能
    per = Person("gs",10)
    #per.work()
    
    #8.slots属性能否应用在子类中
    #结论三:在父类中定义slots属性限制属性的定义,子类中是无法使用,除非在子类中添加自己的限制
    #父类
    class Student(object):
        __slots__ = ("name","age")
    
    #子类
    class SeniorStudent(Student):
        pass
    
    
    s  = Student()
    s.name = "zhangsan"
    s.age = 10
    #s.score = 90
    
    ss = SeniorStudent()
    ss.name = "lisi"
    ss.age = 20
    ss.score = 60
    

    总结:

    继承的特点:

    a.子类对象可以直接访问父类中非私有化的属性
    
    b.子类对象可以调用父类中非私有化的成员方法
    
    c.父类对象不能访问或者调用子类 中任意的内容
    

    继承的优缺点:

    优点:

    a.简化代码,减少代码的冗余
    
    b.提高代码的复用性
    
    c.提高了代码的可维护性
    
    d.继承是多态的前提
    

    缺点:

    通常使用耦合性来描述类与类之间的关系,耦合性越低,则说明代码的质量越高
    
    但是,在继承关系中,耦合性相对较高【如果修改父类,则子类也会随着发生改变】
    

    3.多继承

    一个子类可以有多个父类

    语法:

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

    类体
    

    代码演示:

    father.py文件【父类1】

    class Father(object):
        def __init__(self,money):
            self.money = money
    
        def play(self):
            print("playing")
    
        def fun(self):
            print("father中的fun")
    

    mother.py文件【父类2】

    class Mother(object):
        def __init__(self,faceValue):
            self.faceValue = faceValue
    
        def eat(self):
            print("eating")
    
        def fun(self):
            print("mother中的fun")
    

    child.py文件【子类】

    from extends02.father import Father
    from extends02.mother import Mother
    
    #定义子类,有多个父类
    class Child(Mother,Father):
        def __init__(self,money,faceValue,hobby):
            #调用父类中的构造函数
            Father.__init__(self,money)
            Mother.__init__(self,faceValue)
            self.hobby = hobby
    
        def study(self):
            print("study")
    

    extendsDemo03.py文件【测试模块】

    from extends02.father import Father
    from extends02.mother import Mother
    from extends02.child import Child
    
    
    f = Father(100000)
    m = Mother(3.0)
    
    #创建子类对象
    c = Child(1000,3.0,"打游戏")
    #子类对象调用父类中的成员方法
    c.play()
    c.eat()
    
    #结论;如果多个父类中有相同的函数,通过子类的对象调用,调用的是哪个父类中的函数取决于在父类列表中出现的先后顺序
    c.fun()
    

    4.函数重写【override】

    在子类中出现和父类同名的函数,则认为该函数是对父类中函数的重写

    4.1系统函数重写

    __str__   
    __repr__
    

    代码演示:

    class Animal(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        #重写__str__函数,重写之后一般return一个字符串,有关于成员变量
        def __str__(self):
            return "name=%s age=%d"%(self.name,self.age)
    
        #重写__repr__,作用和str是相同的,优先使用str
        def __repr__(self):
            return "name=%s age=%d"%(self.name,self.age)
    
    a = Animal("大黄",10)
    print(a)   #<__main__.Animal object at 0x00000226A87AC240>
    print(a.__str__())
    
    #当一个类继承自object的时候,打印对象获取的是对象的地址,等同于通过子类对象调用父类中__str__
    #当打印对象的时候,默认调用了__str__函数
    #重写__str__的作用:为了调试程序
    
    """
    总结:【面试题】
    a.__str__和__repr__都未被重写的时候,使用对象调用的是__str__,此时__str__返回的是对象的地址
    b.__str__和__repr__都被重写之后,使用对象调用的是__str__,此时__str__返回的是自定义的字符串
    c.重写了__str__,但是没有重写__repr__,则使用对象调用的是__str__,此时__str__返回的是自定义的字符串
    d.未重写__str__,但是重写了__repr__,则使用对象调用的是__repr__,此时,__repr__返回的是自定义的字符串
    """
    
    #使用时机:当一个对象的属性有很多的时候,并且都需要打印,则可以重写__str__,可以简化代码,调试程序
    

    4.2自定义函数重写

    代码演示:

    #函数重写的时机:在继承关系中,如果父类中函数的功能满足不了子类的需求,则在子类中需要重写
    #父类
    class People(object):
        def __init__(self,name):
            self.name = name
    
        def fun(self):
            print("fun")
    
    #子类
    class Student(People):
        def __init__(self,name,score):
            self.score = score
            super(Student,self).__init__(name)
    
        #重写;将函数的声明和实现重新写一遍
        def fun(self):
            #在子类函数中调用父类中的函数【1.想使用父类中的功能,2.需要添加新的功能】
            #根据具体的需求决定需不需要调用父类中的函数
            super(Student,self).fun()
            print("fajfhak")
    
    
    s = Student("fhafh",10)
    s.fun()
    

    相关文章

      网友评论

          本文标题:11-Python继承

          本文链接:https://www.haomeiwen.com/subject/ohyifqtx.html