zoukankan      html  css  js  c++  java
  • 类的继承和组合

    • 类的继承: 什么是什么, 比如人是动物, 所以人就属于动物类

    • 类的组合: 什么有什么, 比如老师有名字, 老师有年龄, 老师有课程, 所以名字, 年龄, 课程这些属性类就可以组合到老师类

    类的组合

    class Teacher:
        def __init__(self,name:str,age,sex,level:int,salary):
            self.name=name
            self.age=age
            self.sex=sex
            self.level=level
            self.salary=salary
    
    class Course:
        def __init__(self,course_name,course_price,course_period):
            self.course_name=course_name
            self.course_price=course_price
            self.course_period=course_period
    
    python=Course('python',1000,'1mons')
    
    teacher1=Teacher('ross',18,'male',10,3000)
    teacher2=Teacher('joey',28,'male',20,3000)
    
    teacher1.course=python  #不需要改动Teacher类的代码就可以直接组合课程属性到对象teacher1
    teacher2.course=python
    
    print(teacher1.course.course_name)
    

    抽象类

    抽象类, 如下图, 每种动物都有行走的方法, 但是定义了不同的方法名字, 很不友好, 如果解决这个问题就可以使用抽象类

    class People:
        def walk(self):
            print('people is walking')
    
    class Pig:
        def run(self):
            print('pig is running')
    
    peo1=People()
    pig1=Pig()
    
    peo1.walk()
    pig1.run()
    
    # people is walking
    # pig is running
    

    通过抽象类规范子类的方法, 抽象类只能被规范, 不能被实例化

    引入模块`abc`, 下面的代码中父类Animal通过abc模块的abstractmethod的装饰器装饰了方法run和eat, 所以在子类People和Pig中必须要有被装饰的这两个方法run和eat, 如果子类中没有这两个方法, 则会报错如下
    
    TypeError: Can't instantiate abstract class People with abstract methods eat, run
    
    import abc
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def run(self):
            pass
        @abc.abstractmethod
        def eat(self):
            pass
    
    class People(Animal):
        def run(self):
            print('people is walking')
        def eat(self):
            print('people is eating')
    
    class Pig(Animal):
        def run(self):
            print('pig is running')
    
        def eat(self):
            print('pig is eating')
    
    peo1=People()
    pig1=Pig()
    
    def func(obj):
        obj.run()
    
    func(peo1)
    func(pig1)
    

    super函数

    ​ 通常情况下, 我们在子类中定义了和父类同样的方法名, 那么子类的方法就会覆盖父类的方法, 而super关键字实现了对父类方法的改写(增加了功能,增加的功能写在子类中,父类方法中原来的功能得以保留)。也可以说,super关键字帮助我们实现了在子类中调用父类的方法

    class Animals(object):
        def __init__(self,name):
            self.name=name
        def greet(self):
            print('hello, I am %s' %self.name)
    
    class Dog(Animals):
        def greet(self):
            super().greet()
            print('wangwang')
    
    dog=Dog('dog')
    dog.greet()
    
    print(Animals.mro())
    
    # 输出结果
    # hello, I am dog
    # wangwang
    # [<class '__main__.Animals'>, <class 'object'>]
    

    ​ python对于每一个类都有一个mro列表, 可以通过mro()方法直接查看, 例如class.mro()

    class BaseClass(object):
        def __init__(self):
            print('enter BaseClass')
            print('leave BaseClass')
    
    class A(BaseClass):
        def __init__(self):
            print('enter A')
            super().__init__()
            print('leave A')
    
    class B(BaseClass):
        def __init__(self):
            print('enter B')
            super().__init__()
            print('leave B')
    
    class C(B,A):
        def __init__(self):
            print('enter C')
            super().__init__()
            print('leave C')
    
    print(C.mro())
    # 输出结果
    # [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.BaseClass'>, <class 'object'>]
    
    c=C()
    # 输出结果
    # enter C
    # enter B
    # enter A
    # enter BaseClass
    # leave BaseClass
    # leave A
    # leave B
    # leave C
    
    

    如果不使用super函数

    class BaseClass(object):
        def __init__(self):
            print('enter BaseClass')
            print('leave BaseClass')
    
    class A(BaseClass):
        def __init__(self):
            print('enter A')
            # super().__init__()
            BaseClass().__init__()
            print('leave A')
    
    class B(BaseClass):
        def __init__(self):
            print('enter B')
            # super().__init__()
            BaseClass().__init__()
            print('leave B')
    
    class C(B,A):
        def __init__(self):
            print('enter C')
            # super().__init__()
            B().__init__()
            A().__init__()
            print('leave C')
    
    print(C.mro())
    
    # 输出结果:
    # [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.BaseClass'>, <class 'object'>]
    
    c=C()
    # 输出结果:
    enter C
    enter B
    enter BaseClass
    leave BaseClass
    enter BaseClass
    leave BaseClass
    leave B
    enter B
    enter BaseClass
    leave BaseClass
    enter BaseClass
    leave BaseClass
    leave B
    enter A
    enter BaseClass
    leave BaseClass
    enter BaseClass
    leave BaseClass
    leave A
    enter A
    enter BaseClass
    leave BaseClass
    enter BaseClass
    leave BaseClass
    leave A
    leave C
    

    封装

    属性的隐藏

    在需要隐藏的属性前加上双下划线__

    class A:
        __x=1                      # 外部调用时会变形为_A__x
        def __init__(self,name):
            self.__name=name       # 外部调用时会变形为_A__name
        def __foo(self):           # 外部调用时会变形为_A__foo
            print('run foo')       
    

    这种变形的特点:

    • 在类外部无法直接调用obj.__AttrName
    • 在类内部可以直接调用obj.__AttrName
    • 子类无法覆盖父类__开头的属性

    这种变形需要注意的问题是:

    • 这种机制也并没有真正意义上限制我们从外部直接访问属性, 知道了类名和属性名就可以拼出名字

    • 变形过程只在类的定义时发生一次, 在定义后的赋值操作, 不会变形

      image-20201218175456489

    • 在继承中, 父类如果不想让子类覆盖自己的方法, 可以将方法定义为私有的

    下列代码子类的属性会覆盖同名的父类属性

    class A:
        def foo(self):
            print('A.foo')
        def bar(self):  
            print('A.bar')
            self.foo()
    
    class B(A):
        def foo(self):
            print('B.foo')
    b=B()
    b.bar()
    
    # 输出结果
    # A.bar   因为class B中没有bar这个方法, 所以会去父类当中找, 所以输出了A.bar
    # B.foo   由于bar这个方法中有self.foo, 而B类中有这个方法, 所以就是输出B.foo, 子类的方法是可以覆盖父类的方法的
    

    如果就是想要使用父类的方法, 而不是子类的方法, 这时候就会用到隐藏属性了

    class A:
        def __foo(self): #_A__foo
            print('A.foo')
        def bar(self):   #_A__bar
            print('A.bar')
            self.__foo() #_A__foo
    
    class B(A):
        def __foo(self): #_B__foo
            print('B.foo')
    b=B()
    b.bar()
    
    # 输出结果
    # A.bar
    # A.foo
    

    封装的意义

    封装数据属性, 明确的区分内外, 控制外部对隐藏的属性的操作行为

    class People:
        def __init__(self,name,age):     #隐藏name和age属性, 通过tell_info和set_info两个接口调用或者修改这两个属性,
            self.__name=name             #避免外部直接操作这两个属性
            self.__age=age
    
        def tell_info(self):
            print('Name:<%s> Age:<%s>' %(self.__name,self.__age))
    
        def set_info(self,name,age):
            if not isinstance(name,str):
                print('名字必须是字符串')
                return
            if not isinstance(age,int):
                print('名字必须是数字类型')
                return
            self.__name=name
            self.__age=age
    
    p=People('ross',12)
    p.tell_info()
    p.set_info('van',18)
    p.tell_info()
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('认证')
        def __input(self):
            print('输入取款金额')
        def __print(self):
            print('打印账单')
        def __take(self):
            print('取款')
        def with_draw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print()
            self.__take()
    
    a=ATM()
    a.with_draw()  #对于外部调用来说, 只有with_draw一个接口, 隔离复杂度
    

    神奇的property

    property装饰器就是把方法变成属性调用的, 调用方法的时候不需要加括号

    @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作, 可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

    class Student(object):
    
        @property
        def birth(self):
            return self._birth
    
        @birth.setter
        def birth(self, value):
            self._birth = value
    
        @property
        def age(self):
            return 2015 - self._birth
    
    stu=Student()
    stu.birth=1999
    print(stu.age)
    

    上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。

    class Screen:
        @property
        def width(self):
            pass
        @width.setter
        def width(self,width):
            if not isinstance(width,int):
                print('wrong value type')
                return 2
            self.__width=width
        @width.getter
        def width(self):
            print(self.__width)
        @width.deleter
        def width(self):
            print('你们不要再打啦')
            
        @property
        def height(self):
            pass
        @height.setter
        def height(self,height):
            if not isinstance(height,int):
                print('wrong value type')
                return 2
            self.__height=height
        @property
        def resolution(self):
            return self.__width*self.__height
    
    s = Screen()
    s.width = 1024
    s.height = 768
    s.width
    print('resolution =', s.resolution)
    
  • 相关阅读:
    __proto__、prototype、constructor 之间的关系
    call()与apply()区别
    条件注释判断浏览器
    永远在页面底部的层
    jQuery1.9和jQuery2.0加载(IE10下条件判断失效)
    mousewheel
    2013多校第四场 G题 ZZ的搬砖难题
    hdu 4389 x mod f(x) 数位DP
    hdu 4468 spy 构造kmp
    hdu 4466 triangle 三角形统计 数学计数
  • 原文地址:https://www.cnblogs.com/peitianwang/p/14151816.html
Copyright © 2011-2022 走看看