为了账号安全,请及时绑定邮箱和手机立即绑定

浅谈继承与子类型:从Java、Go到Python

标签:
Java Python Go

Java是我职业生涯中最早接触的编程语言。它在我早期学习编程概念时奠定了基础。在学习了几种截然不同的语言之后,我的视野因此变得更加宽广。今天,我想谈谈“继承”这个概念。

Java中的继承在Java中

在 Java 中,_继承_的概念与_子类型_的概念紧密相关。子类型体现了_是_的关系。例如,Rabbit 类是 Mammal 类的一种。因此,一个 Rabbit 实例继承了 Mammal 的所有行为:它继承了这些行为。

因此,当你需要传递一个 Mammal 参数时,你可以传递一个 Rabbit 实例;或者当你需要返回 Mammal 类型时,你可以返回一个 Rabbit 实例。你如果学过 Java、.Net 或类似的语言,这就是你理解继承的方式,也就成了你的新习惯。

这叫做明确的继承。

    class Animal {
        void feed();
    }

    class Rabbit extends Animal {                     //1
    }

全屏 退出全屏

  1. 因为 RabbitAnimal,所以它可以 feed()
Go中的继承

Go语言中的继承是如何实现的?

当我第一次接触 Go 时,我惊讶地发现它没有子类型,却仍然支持继承。Go 采用的是鸭子类型。

看起来像鸭子,游起来像鸭子,叫声像鸭子,那它大概就是鸭子。

如果一个 Go struct 实现了 interface 中的所有函数,它隐式实现了 interface

    type Animal interface {
        feed()                                        //1
    }

    type 兔 struct {
    }

    func (r *兔) feed() {                           //2
        // 饲喂兔子
    }

点击这里进入全屏模式,点击这里退出全屏模式。

  1. 一个 Animal 可以吃
  2. 因为存在一个 feed() 函数,它接受一个 Rabbit 作为参数,因此 Rabbit 继承了 Animal 的特性。

我不是很喜欢Go处理错误的方式,但对隐式实现我有些犹豫。一方面,我尝试保持开放的态度;另一方面,我认为无论是编程还是生活中,事情总是明说比不说好。

Python中的继承特性

Python 是我所知道的,关于继承最有趣的语言。

子类型和基于类型的继承特性自 Python 初创以来就一直存在。

class Animal:
    def feed(self):                               #1 饲喂
        pass                                      #2 待实现

class Rabbit(Animal):                             #3 兔子
    pass

进入全屏、退出全屏

  1. 一个 Animal 能吃
  2. Python 中没有抽象类,只有类
  3. 因为 RabbitAnimal,所以 Rabbit 也能吃

在这方面,Python 和 Java 在继承方面的运作方式相同。Python 还提供了动态类型,我把它称为 魔术方法。例如,为了使某东西可迭代,即能够返回一个迭代器,你只需要实现 __iter__()__next__() 即可:

    class SingleValueIterable():

        done = False

        def __init__(self, value):
            self.value = value

        def __iter__(self):                           #1
            # 返回自身作为迭代器
            return self

        def __next__(self):                           #1
            # 检查是否已完成迭代
            if self.done:
                # 抛出StopIteration异常表示迭代完成
                raise StopIteration()
            else:
                # 标记迭代完成并返回值
                self.done = True
                return self.value

    svi = SingleValueIterable(5)
    sviter = iter(svi)                                #2
    # 获取迭代器实例

    for x in sviter:
        print(x)                                      #3
        # 遍历迭代器并打印每个值
        # 打印当前值。

全屏进入 退出全屏

  1. 动态类型检查方法
  2. 创建一个 Iterator - Python 自动识别,因为我们已经实现了上述方法。
  3. 输出 5

这种鸭子类型的这种做法的问题是,它只能用于Python的预定义特殊方法(如__init____str__等)。如果你想提供一个类,让第三方可以隐式地继承自它,又应该怎么办?

    class Animal:

        def feed(self):
            pass

    class 兔子类:

        def feed(self):
            pass

全屏模式:进入 退出

在上述片段中,Rabbit 并不是 Animal,这真让我们感到遗憾。这时引入了 PEP 544,名为“协议:结构次类型(静态鸭子类型)”。该 PEP 解决了我们类无法定义魔法方法的问题。它定义了一个简单的 Protocol 类:一旦从中继承,这些方法就可以进行静态鸭子类型检查,因此得名“静态鸭子类型”。

    from typing import Protocol

    class Animal(Protocol):                           #1

        def feed():                                   #2
            pass

    class 兔子类:

        def feed():                                   #2
            pass

    class 捕蝇草:

        def feed():                                   #2
            pass

进入全屏 退出全屏

  1. 继承自 Protocol
  2. 因为 Animal 是一个 Protocol,任何定义了 feed() 的类都将被视为 Animal,好坏自明
结论啦

面向对象编程、继承和子类型可能具有特定含义,这些含义在不同语言中可能有所不同,这取决于你最初学习的是哪种语言。Java 称自己为面向对象语言,并提供了完整的面向对象特性。Go 并不是一种面向对象语言,但它仍然通过鸭子类型实现了类似子类型的功能。Python 支持显式和隐式的继承机制,但不支持类似Java的接口。

你可以通过将新编程语言与已知语言进行比较来学习它。了解一种语言的特性对于用目标语言编写地道代码至关重要。熟悉那些在你已知语言中不存在的特性:这将拓宽你对编程的整体理解。

再往前走:

此处省略内容

_原文发表于A Java Geek 1月26日, 2025年
[OO: 面向对象]

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消