事故发生地

在学习 Python 面向对象视频 之 鸭子类型与多态 时看到这样两句话不得其解:

非动态语言必须通过继承和接口实现多态
动态语言不需要实现接口


菜鸟的问题

(非动态语言以 Java 为例,动态语言以 Python 为例)

  1. 接口和多态是什么关系?
  2. Python 为什么没有接口?没有接口怎么实现接口对应的设计模式
  3. Python 为什么没有重载?
  4. Python 的多态是什么样的?

接口和多态是什么关系?

在我的理解中,多态具有两种形式,重载和重写:

重载在同一个类中,相同的方法名对应着不同的方法实现,其区别在于他们需要的参数和返回值不同。
重写用于父类和子类间,子类重写父类的方法,只是对应的方法实现不同,其方法名和参数都相同。

这样看来,重写是基于继承的。那么接口呢?好像除了强制要求子类实现某些方法,我不知道还有什么别的作用。
正因如此,接口只是一个规范。在多个类都实现一个接口的时候他的作用就体现出来了,我们不需要知道在某个类中这个方法的具体实现,只需要这个类中有这个方法而我们可以直接调用就可以了。有了这样一个统一的标准,我们就不必担心同样功能的方法被起了各种各样的名字了。
所以,我觉得可以用这么一句话回答这个问题,接口不是实现多态的,而是基于多态的,有了多态接口才能发挥作用


Python 为什么没有接口?没有接口怎么实现接口对应的设计模式

接口的作用就是规范,规范参数类型,返回值等等,从而使相同的调用能够实现不同的效果;并且接口也是为了用来弥补语言自己表达能力的不足,如:Java 只支持单继承,为了使类拥有更多的特性,使用了多实现来弥补。静态的语言必须全部都规定好才能正确使用。
而在 Python 中,变量是没有类型的,不需要事先规定,不管调用者传入什么类型的对象,被调用者就会认为那就是我所需要的对象(鸭子类型),如果在运行时传入的对象不具备需要的属性或方法,程序会直接报错。需要实现这个方法的话,就照实现就是了,返回和形参不限制类型,比如:在创建了类的实例后,动态地给该实例加上一个函数属性。这就是动态类型的优势。
这种风格被称为“鸭子类型”:

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。
在我想成为鸭子的时候,不是我必须真的是个鸭子,而是我的行为表现的像是个鸭子,那我就是个鸭子

相关概念 - “鸭子测试”:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
“换言之,不要检查它是不是一个鸭子:检查它像不像一个鸭子地叫,等等。取决于你需要哪个像鸭子的行为的子集来使用语言。”


Python 为什么没有重载?

函数重载主要是为了解决两个问题:

  1. 可变参数类型。
  2. 可变参数个数。

另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
对于情况 1,函数功能相同,但是参数类型不同,Python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 Python 中很可能是相同的代码,没有必要做成两个不同函数。
对于情况 2 ,函数功能相同,但参数个数不同,Python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,Python 自然就不需要函数重载了。


Python 的多态是什么样的?

多态即多种形态,在运行时确定其状态,在编译阶段无法确定其类型,这就是多态。
1)Python是解释性语言。不进行预编译,因此它就只在运行时确定其状态;
2)Python中变量是弱类型的。在定义时不用指明其类型,它会根据需要在运行时确定变量的类型。


结语:一些粗浅之语,有长进再补充。


Reference:

Java 中的接口有什么作用?
Python 里没有接口,如何写设计模式?
为什么 Python 不支持函数重载?
Python面向对象编程(二)
鸭子类型