类的继承和组合

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

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

类的组合

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)
posted @ 2020-12-17 20:34  培天王  阅读(159)  评论(0编辑  收藏  举报