多态和鸭子类型有什么区别?

41

我对这两个术语有些困惑,这是我所知道的:

多态性是指不同类型的对象能够被一个共同的接口处理的能力。而鸭子类型则是一种动态类型,它允许不同类型的对象响应相同的方法。

据我所理解,多态性更多地关注于创建可在不同类之间共享的接口。而鸭子类型则是关注于松散的类型,只要该方法存在于消息的接收者上,就允许调用该方法。

这样说对吗?我对这两个概念感到有些困惑,它们似乎有关联,但我不知道它们之间的具体关系。非常感谢!

4个回答

41
多态(在面向对象编程的语境下)指子类可以重写基类的方法。这意味着一个类的方法可以在子类中做不同的事情。例如:一个 Animal 类可以有一个 talk() 方法,而 Animal 的子类 DogCat 可以让方法 talk() 发出不同的声音。

鸭子类型 意味着代码将简单地接受具有特定方法的任何对象。假设我们有以下代码:animal.quack()。如果给定的对象 animal 具有我们想要调用的方法,则我们就可以使用它(不需要额外的类型要求)。无论 animal 实际上是一只 Duck 还是其他也会嘎嘎叫的动物都没关系。这就是为什么它被称为鸭子类型:如果它看起来像一只鸭子(例如,它具有一个名为 quack() 的方法),那么我们可以把它当作一只鸭子来处理。

这两者是否相关?它们只是编程语言可能具有的独立功能。有些编程语言具有多态,但没有鸭子类型(例如Java)。还有一些语言具有多态和鸭子类型(例如Python)。


14
"多态性意味着子类可以重写基类的方法。" - 这难道不就是继承吗?" - gerky
3
在面向对象编程中,确实可以通过继承来实现多态性。但是,多态性也可以在没有类的情况下实现,例如定义一个名为add(int x, int y)和一个名为add(String s, String t)的函数,这两个函数具有相同的名称,但实际参数确定了调用哪个函数。 - Simeon Visser
2
如果你在说多态,那么覆盖是什么? - Rupesh Patel
如果每个类都希望成员变量Foo可用于鸭子类型,并且每个类都实现了接口IFoo,那么多态或鸭子类型可以相互实现;而且如果每个想要公开多态方法集的类都有一个具有已知名称的成员变量来返回实现这些方法的包装器,则也可以相互实现。 - supercat
有趣的观点。可能有点晚了。重载+继承+覆盖都有助于子类的多态行为。(注意:Poly=many,morph=forms)。现在让我们退后一步,关注实现细节,多态子类可能会表现/行为不同。另一方面,鸭子类型是关于使用给定实例/对象的属性或方法,而不关心对象的类型。在Python中,“StringIO”更/少像文件对象一样进行鸭子类型,并且期望文件对象的代码应该可以毫无问题地继续运行。 - dopstar
显示剩余12条评论

21

这是Python中多态的一个示例。

class Animal:
    def __init__(self, name):  # Constructor of the class
        self.name = name

    def talk(self):  # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")

class Cat(Animal):
    def talk(self):
        return 'Meow!'

class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'

animals = [Cat('Missy'),
           Cat('Mr. Mistoffelees'),
           Dog('Lassie')]

for animal in animals:
    print(animal)
    print(animal.name + ': ' + animal.talk())

这是Python中鸭子类型的一个例子。

class Duck:
    def quack(self):
        print("Quaaaaaack!")

    def feathers(self):
        print("The duck has white and gray feathers.")

    def name(self):
        print("ITS A DUCK NO NAME")

class Person:
    def quack(self):
        print("The person imitates a duck.")

    def feathers(self):
        print("The person takes a feather from the ground and shows it.")

    def name(self):
        print("John Smith")

def in_the_forest(duck):
    duck.quack()
    duck.feathers()
    duck.name()

def game():
    for element in [Duck(), Person()]:
        in_the_forest(element)

game()
  • 在多态性中,我们看到子类(CatDog)继承自父类(Animal)并重写方法Talk。
  • 在鸭子类型的情况下,我们不创建一个子类,而是创建一个具有相同名称但不同功能的方法的新类。

5
对我来说,第一个案例似乎像是简单的继承。我真的不认为在 Python 这样的动态类型语言中谈论多态性有意义。实际上,这里的第一个案例也是鸭子类型。在 Python 中,函数参数没有类型限制,所以你可以将任何东西传递给任何函数。这不是多态性,只是动态类型。 - Evan Zamir
2
第一个例子中,它不仅仅是简单的继承,对吧?有一个动物数组,他正在调用其元素上的talk()方法。这就是子类型多态性。简单继承的目标基本上只是在类之间共享代码。这显然超越了那个范畴。 - Hashman
1
这是最好的例子。 - avibrazil
很好的例子,谢谢。 - Seyed Mostafa SeyedAshoor

16

简短回答:

鸭子类型是实现多态的一种方式。另一种方式是使用静态类型。

详细回答:

这里涉及到两个不同的概念,类型和编程技术。

鸭子类型是一种类型。而类型意味着在传递了一个不符合预期的对象时,何时抛出错误。鸭子类型是一种类型,在程序运行时调用的方法不可用时会抛出错误。静态类型具有编译时检查的功能,因此如果类型信息不匹配,在编译代码时会抛出错误,这就是类型。

多态是一种编程技术,您允许多种类型的对象来完成某些职责。您可以使用基类型表示所有子类类型。您可以使用鸭子类型来表示具有所需方法的所有不同类型。您可以使用接口表示实现接口的所有类型。

有些答案称多态是继承,这是不正确的。尽管您可以使用继承来创建多态行为,通常也是这样做的,但这不是多态的核心。

首先,您不需要继承来实现上述多态。

其次,“多态”一词在依赖抽象的客户端代码上下文中更有意义,而不是在实现代码中。仅仅因为你有一个超类和几个从它继承且覆盖一些方法的子类,并不意味着它是多态的。要创建多态性,您必须以多态方式编写客户端代码来使用这些类。


2

两种多态性

  1. 方法重载(编译时多态性)。
  2. 方法覆盖(运行时多态性)。

方法重载:相同的函数名称和不同的数据类型被称为方法重载

例如:

  int addTwovalues(int a, int b)
  { return (a+b)}

  float addTwovalues(float a, float b)
  { return (a+b)}

  Method overriding :- same function name and same data type but different Class
     is known as       Method overriding.


  class a
 {
  virtual int addtwovalues()
   {  // to do  }
  }
 class b:a
 {
     override int addtwovalues()
   {  // to do  }

  }



  a obj=new a();
  obj.addtwovalues();

  b objb=new a();
  objb.addtwovalues();  //run time Polymorphism 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接