Python攻城师的成长————面向对象的三大特征(继承、多态)

本文讲解了Python中的继承与多态概念,包括如何通过继承重用Animal类的功能,子类如何覆盖父类方法实现多态,以及多继承带来的复杂性和如何避免。通过实例演示了如何在实际项目中利用这些特性提高代码灵活性和扩展性。
摘要由CSDN通过智能技术生成

学习目标:

  • 了解继承与多态的概念,重点是要学会运用继承去处理问题

学习内容:

继承

在面对对象程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类,而被继承的class称为基类、父类或超类。

为了便于理解,我们可以举例子说明一下:
比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:

class Animal(object):
    def run(self):
        print('Animal is running...')

现在我们有了Animal这个类,基于这个动物类,我们可以再分出一个Dog类和Cat类。现在我们也想让这两个类拥有run方法,这时为了方便就可以去"借用"Animal类里的run方法:

class Dog(Animal):
    pass

class Cat(Animal):
    pass

现在按照继承定义的说法,对Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

dog = Dog()
dog.run()

cat = Cat()
cat.run()

'''
运行结果如下:

Animal is running...
Animal is running...
'''

继承的特点:

  1. 在调用父类的方法时,需要加上父类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数

  2. Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到父类中逐个查找。(先在本类中查找调用的方法,找不到才去父类中找)。

  3. 继承中父类:用于被继承的类,称之为父类,也叫做基类,或者超类。子类:继承其他类的类,称之为子类,也叫做派生类。

  4. 实现代码复用,减少代码量;拥有父类的所有功能,只需要编写子类附加的功能即可。

继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:

            	┌───────────────┐
            	│    object     │
            	└───────────────┘
           	            │
       	   ┌────────────┴────────────┐
       	   │                         │
    	   ▼                         ▼
	┌─────────────┐           ┌─────────────┐
	│   Animal    │           │    Plant    │
	└─────────────┘           └─────────────┘
      	  │                         │
 	┌─────┴──────┐            ┌─────┴──────┐
 	│            │            │            │
 	▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│   Dog   │  │   Cat   │  │  Tree   │  │ Flower  │
└─────────┘  └─────────┘  └─────────┘  └─────────┘

这时候就可以提到继承里的一个特殊情况了,在python里可能有别与Java,在java里是不能直接继承多个父类的,而python是可以的只要在子类的小括号里用逗号隔开就行了。

多继承

Python2.2之前类是没有共同的祖先的,之后,引入object类,它是所有类的共同祖先类object。Python2中为了兼容,分为古典类(旧式类)和新式类。Python3中全部都是新式类。新式类都是继承自object的,新式类可以使用super。

语法结构

class ClassName(父类列表:A,B,C,...):
    类的代码体

多继承的产生也就是为了去可以免除一些方法的反复编写,只要从写好的地方继承过了就可以了。由此就会带来一些缺点:

当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径。Python语法是允许多继承,但Python代码是解释执行,只有执行到的时候,才发现错误。团队协作开发,如果引入多继承,那代码很有可能不可控。不管编程语言是否支持多继承,都应当避免多继承。Python的面向对象,我们看到的太灵活了,太开放了,所以要团队守规矩。

  1. 多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突。
  2. 多继承的实现会导致代码设计的复杂度增加,所以有些高级编程语言舍弃了类的多继承:C++支持多继承;Java舍弃了多继承。
  3. 实现多继承的语言,要解决二义性,深度优先或者广度优先。

多态

子类可以从父类继承相同的属性或者方法,比如相同的函数名,在子类中可以有不同的实现,即子类可以有自己的特殊性,这就叫做多态。

多态的话按照我的理解就是一个物体的多种形态,比如可口可乐,在原先的基础上现在又有了樱桃味的,无糖等等。就是让原本功能比较简单的类,去在原基础上可以添加其他功能,官方点说就是增加它的灵活性。

为了更加生动的理解多态,再次利用上面的例子:
现在就可以对子类增加一些方法,比如Dog类:

class Dog(Animal):

    def run(self):
        print('Dog is running...')

    def eat(self):
        print('Eating meat...')

现在就可以看出,其实多态是基于继承来完成的。你看到了,无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running…,符合逻辑的做法是分别显示Dog is running…和Cat is running…,因此,对Dog和Cat类改进如下:

class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')
        
'''
再次运行,结果如下:

Dog is running...
Cat is running...
'''

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就可以说多态是基于继承来完成的

现在就可以总结出实现多态有两个前提:

  1. 继承:多态必须发生在父类与子类之间
  2. 重写:子类重写父类方法

这时候就会有小白会问了那学了多态有什么用?

怎么说呢其实以上代码实际貌似没啥用,为什么一定要去定义通过同一个函数名?我定义run1、run2不可以吗?代码量也一样啊!
其实真正体验出多态好处的代码一般是这样写的

class Animal():
    def run(self):
        print("I am an Animal")
        
class Dog(Animal):
    def run(self):
        print("I am a dog")

class Cat(Animal):
    def run(self):
        print("I am a cat")

def func(obj):
    obj.run()

if __name__ == "__main__":
    dog=Dog()
    cat=Cat()
    func(dog)
    func(cat)

仅仅需要一个函数,就可以把不同对象的run函数表现出来了。这就增加了程序的灵活性,以不变应万变,不管你类中的run()写得如何得天花乱坠,我都可以用一个统一的形式来调用。另外,它增加了程序的可扩展性,不管是我们或者调用者,如果想增加新的功能,都不需要修改原来的代码。

如,我想新增一个bird类,仅需增加以下代码,无需修改原来的。

class Bird(Animal):
    def run(self):
        print("I am a bird")

对于调用者,也仅需增加以下代码,无需修改原来的。

bird=Bird()
func(bird)

所以说多态有什么用?一是增加程序的灵活性,二是增加程序的可扩展性。

静态语言 vs 动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

class Timer(object):
    def run(self):
        print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。


学习时间:

  • 周一至周五晚上 7 点—晚上9点
  • 周六上午 9 点-上午 11 点
  • 周日下午 3 点-下午 6 点

学习产出:

  • 技术笔记 2 遍
  • CSDN 技术博客 3 篇
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值