6.2 继承与多态

继承的简介、super()

- 继承是面向对象三大特性之一
- 通过继承我们可以使一个类获取到其他类的属性和方法
- 在定义类时,可以在类名后面的括号中指定当前类的父类(超类、基类、super)
    子类(衍生类)可以直接继承父类中所有属性和方法(包括特殊方法__init__等)
例:
class Animal():
    def __init__(self, name):
        self._name = name

    def run(self):
        print('动物会跑~~~')
    
    def bark(self):
        print('动物会叫~~~')
        
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, name):
        self._name = name
        
class Dog(Animal):  ---> Animal为父类
    # 继承了父类Animal的所有方法和属性,也可以添加新的方法和属性
    # 当方法和属性的名称与父类的一致时,会覆盖掉从父类中继承过来的同名方法和属性
    # 这叫<方法的重写>,也叫覆盖,override
    # 特殊方法也可重写
    def __init__(self, name, age):  ---> 在子类Dog中新添加了属性age
        # 可以直接调用父类的__init__来初始化父类中定义的属性,但是此方法死板(父类名称改变时就出错了)
        # Animal.__init__(self, name) ---> 此语句替代了原来的 self._name = name
        # super()可以用来获取当前类的父类,并且通过super()返回的对象调用父类时,不需要传递self
        super().__init__(name) ---> 此方法比上面的方法灵活,可以动态获取父类,而不管父类有没有改变
        self._age = age ---> 剩余的没有原本没有在父类中的属性要单独初始化
        
    def run(self):
        print('狗会跑~~~') 
        
    def sleep(self):         
        print('狗会睡觉~~~')  
       
    @property           ---> 由于在父类中已有属性name的装饰器,因此不需要添加了
    def age(self):      ---> 但是age还没有装饰器,因此Dog中添加
        return self._age
    
    @age.setter
    def age(self, age):
        self._age = age
    


- 继承的好处:
    通过继承可以让子类获取到父类的方法和属性,避免编写重复性代码
    并且符合OCP原则, 所以经常通过继承来对一个类进行扩展
- 在创建类时,如果省略了父类,则默认父类为object
    object是所有类的父类,所有类都继承自object,如
    class Person(object):
        pass
- 继承是可以多代继承的,比如当类FemaleDog是从类Dog继承的,那么FemaleDog也是Animal的子类
- issubclass(a, b)
    检查类a是否是类b的子类,是则返回True,否则返回False
    如上例中:
        issubclass(FemaleDog, Animal)  ---> True
        issubclass(Dog, Animal)  ---> True
        issubclass(Dog, object)  ---> True
        issubclass(Animal, object)  ---> True
        issubclass(Animal, Dog)  ---> False
        
- isinstance(a, b)
    检查对象a是否是类b的实例,是则返回True,否则返回False
        如果该类b是对象a的父类,也会返回True
        由于object是所有类的父类,因此所有的对象都是object的实例
    isinstance(print, object) ---> True

方法的重写(覆盖) —> 包括特殊方法

- 上文提到了,如果在子类中存在有跟父类同名的方法,则通过子类去调用该方法时
    会调用子类的方法而不是父类的方法,这个特点成为方法的重写(覆盖,override)
- 当我们去调用一个对象的方法时,
    会优先去当前对象中寻找是否具有该方法,如果有则直接调用
    如果没有,则去当前对象的父类中寻找,若父类中有则直接调用
    如果没有,则去父类的父类中寻找,以此类推,知道找到object(所以类的父类)
    如果依然没有找到,则报错

多重继承(无特殊情况,不建议使用)

- 在Python中是支持多重继承的,也就是可以为一个类同时指定多个父类
    可以在类名的()后边添加多个类,来实现多重继承
    多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
- 在开发中若无特殊情况,应该尽量避免使用多重继承,因为多重继承会让代码过于复杂
- 如果多个父类中有同名的方法,(如果子类也跟他们同名,则会在子类里直接调用)则会在
第一个父类中寻找,然后找第二个。。。以此类推
    前边父类的方法会覆盖后边父类的方法
例子:
    class A(object):
        pass
    
    class B(object):
        pass
    
    class AB(B, A):  ---> 子类AB同时继承了父类B和A,顺序是B,然后才是A
        pass

- 类名.__bases__    这个属性可以用来获取当前类的所有父类,例:
    AB.__bases__    --->  (<class '__main__.B'>, <class '__main__.A'>)
    A.__bases__     --->  (<class 'object'>,)

多态

- 多态是面向对象的三大特征之一
- 字面上理解是 多种形态
    如:狗(狼狗、哈士奇、藏獒...)
- 一个对象可以以不同形态去呈现
定义两个类:
class A():
    def __init__(self, name):
        self._name = name
        
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, name):
        self._name = name
    
class B():
    def __init__(self, name):
        self._name = name
        
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, name):
        self._name = name

a = A('孙悟空')
b = B('猪八戒')

# 定义一个函数
def say_hello(obj):
    print('你好 %s' %obj.name)

# 对于say_hello()函数来说,只要对象中含有name属性,那么这个对象就可以作为参数传递
# 这个函数并不会考虑对象的类型,只要该对象有name属性即可
say_hello(a) ---> 你好 孙悟空
say_hello(b) ---> 你好 猪八戒

# 下面这个例子就违反了多态:只适用于一种类型的对象,无法处理其他对象(导致函数适应性差)
def say_hello_2(obj):
    # 做类型检查
    if isinstance(obj, A):
        print('你好 %s' %obj.name)
say_hello_2(b) --->
---> 在say_hello_2()中我们做了一个类型检查,只有obj是A类型的对象时,才可以正常使用
    其他类型的对象都无法使用该函数,这个函数就违反了多态

面向对象的特征总结

面向对象的三大特征:
- 封装
    确保对象中的数据安全
- 继承
    保证了对象的可扩展性
- 多态
    保证了程序的灵活性

属性和方法(总结)

# 涉及到的内容:
    类属性
    实例属性
    类方法
    实例方法
    静态方法

# 定义一个类
class A(object):
    
    # 类属性,直接在类中定义的属性是类属性
        类属性可以通过类或类的实例访问到
        但是类属性只能通过类对象来修改,无法通过实例对象来修改
    count = 0

    def __init__(self, name):
        # 实例属性,通过实例对象添加的属性属于实例属性
            实例属性只能通过实例对象来访问和修改,类对象无法访问修改
        self._name = name
    
    # 实例方法
        在类定义中,以self作为第一个参数的方法都是实例方法
        实例方法在调用时,python会将调用对象作为self传入(不用我们传入)
        实例方法可以通过实例和类去调用(两者等价)
            # 假设a = A('孙悟空'),a是类A的一个对象
            当通过实例调用时,会自动将当前调用对象作为self传入,如a.test()
            当通过类调用时,不会自动传递self,此时必须手动传递self,如A.test(a)
    def test(self):
        print('这是test方法~~~', self) ---> 这是test方法~~~ <__main__.A object at 4664654998>
        
    # 类方法
    # 在类内部使用 @classmethod 来修饰的方法属于类方法
    # 类方法的第一个参数时cls,也会被自动传递,cls就是当前的类对象
        类方法和实例方法的区别:
            实例方法第一个参数是self,类方法的第一个参数是cls
        类方法可以通过类或者实例去调用,没有区别(唯一区别是第一个参数不一样)
    @classmethod
    def test_2(cls):
        print('这是test_2方法,他是一个类方法~', cls)
        ---> 这是test_2方法,他是一个类方法~  <class '__main__.A'>
        ---> 类方法要用类去调用: A.test_2()
    # 调用
    a.test_2() # 通过实例调用
    A.test_2() # 通过类调用
        
    # 静态方法
    # 在类中使用 @staticmethod 来修饰的方法属于静态方法
    # 静态方法不需要指定任何默认参数,可以通过类和实例去调用
    # 静态方法,基本上是一个和当前类无关的方法,而只是一个保存到当前类中的函数
    # 静态方法一般是一些工具方法,和当前类无关
    @staticmethod 
    def test_3():
        print('test_3执行了~')
    # 调用
    a.test_3()
    Dog.test_3()  ---> 如果test_3有参数,就要传参数
        
        
        
a = A()

垃圾回收(了解)

- 程序在运行过程中会产生垃圾,会影响到程序的运行性能,必须被及时清理
- 什么是垃圾:没用的东西就是垃圾
- 在程序中,没有被引用的<对象>就是垃圾
- 要进行垃圾回收: 将垃圾对象从内存中删除
- 在Python中有自动的垃圾回收机制,可自动删除没有被引用的对象删除(所以不用手动删除)

class A():
    def __init__(self):
        self.name = 'A类'

    # a = A()
    a = None # 将a设置为了None,此时没有任何变量对A()对象进行引用,就变成了垃圾

    print(a.name)

    input('请按回车键退出!')

特殊方法

也称魔术方法,使用__来开头,__结尾,如__del__
# 定义一个Perdon类
class Person(object):
    '''人类'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # __str__()这个特殊方法会在尝试将对象转换为字符串的同时调用
    # 他的作用可以用来指定对象转换为字符串的结果
    # 使用print函数时用到
    def __str__(self):
        return 'Person [name=%s, age=%d]' % (self.name, self.age)
        ---> 打印对象p1: print(p1) ---> Person [name=孙悟空, age=18]
        
    # __repr__()这个特殊方法会在对当前对象使用repr()函数是调用
    # 作用是指定对象在<交互模式>中直接输出的效果
    # 在交互界面时起作用
    def __repr__(self):
        return 'Hello'
        
    ... 还有很多特殊方法,去python帮助手册查询
        
# 创建两个Person类的实例
p1 = Person('孙悟空', 18)
p2 = Person('猪八戒', 40)

# 单打印一个对象时,实际上打印的是对象中的特殊方法 __str__()的返回值
print(p1)  --->   <__main__.Person object at 4638749>
# 所以当我们在类Person中添加一段代码:
    def __str__(self):
        return 'hello'
# 那么打印对象print(p1),会返回 hello
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值