关于鸭子类型

Posted seasen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于鸭子类型相关的知识,希望对你有一定的参考价值。

其实对我来说鸭子类型一直是一个比较模糊的概念,因为平常不去关注,关注的时候一般是在面试或者被面试的时候,今天在看一篇博客的时候又有看到,所以就索性弄清楚

 

``` 

首先 与duck typing相对应的是normal typing(对象的类型决定对象的特性)

*既然是相对,可以简单理解为 duck typing 就是对象的特性决定了他是什么,说一个烂大街的句子:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”,不是因为它是鸭子所以走路,游泳和叫像鸭子,而是走路,游泳和叫像鸭子所以被``认为``是鸭子

duck typing中对象的类型不重要,只要对象有类型A的方法和属性,那么它被当做类型A来使用。熟悉c的同学应该明白c就是normal typing,在编译阶段静态检查,函数定义和传参类型需要保持一致,不一致就编译报错。参数类型当然也包含了 ``对象`` 参数,后面我会举个Go的例子。
 
Python中的鸭子类型
class Duck:
  def fly(self):
    print("Duck flying")

class Airplane:
  def fly(self):
    print("Airplane flying")

class Whale:
  def swim(self):
    print("Whale swimming")


def runtest(obj): 

  obj.fly()


duck = Duck()
airplane = Airplane()
whale = Whale()

此时:
runtest(duck)        √
runtest(airplane)   √
runtest(whale)       ×

前两个有fly方法,所以在下面的runtest中直接就可以调用,runtest不去理会你传进来的到底是谁的对象,只要你有这个方法,那么我就可以去调用运行。

其实就是多态的一种体现,多态是什么?“多态是指不同类的对象对同一消息作出响应,同一个接口,不同的对象会做出不同的反应;同一个行为可以通过多种表现形式展现出来就是多态”

例如上面同样的runtest这个接口,输入了不同的对象,打印了不同的结果。
 
在go语言中的  接口:
首先我们定义一个规范,也就是说定义一个接口:

type Duck interface {
    Quack()   // 鸭子叫
    DuckGo()  // 鸭子走
}
这个接口是鸭子的行为,我们认为,作为一只鸭子,它需要会叫,会走。然后我们再定义一只鸡:

type Chicken struct {
}
假设这只鸡特别厉害,它也会像鸭子那样叫,也会像鸭子那样走路,那么我们定义一下这只鸡的行为:

func (c Chicken) Quack() {
    fmt.Println("嘎嘎")
}

func (c Chicken) DuckGo() {
    fmt.Println("大摇大摆的走")
}。
*注意,这里只是实现了 Duck 接口方法,并没有将鸡类型和鸭子接口显式绑定。这是一种非侵入式的设计。
然后我们让这只鸡,去叫,去像鸭子那样走路:

func main() {
    c := Chicken{}
    var d Duck
    d = c
    d.Quack()
    d.DuckGo()   # 这一点目前有点疑问,关于接口可以先参照下面的题外话代码
}
执行之后我们可以得到结果:

嘎嘎
大摇大摆的走

这里 go 的
func (c Chicken) DuckGo()
func (c Chicken) Quack()
两个函数可以看成是Python中的方法,go利用接口 type Duck interface 实现了go中的多态,如果没有这个interface的存在,可以看出 一个函数的方法,必须要指定入参的类型,例如func (c Chicken) Quack(),指定了这个入参是Chicken类型的,
如果传入其它的,当然就会编译报错
例:
type Vertex struct {
X, Y float64
}

type Vertex2 struct {
X, Y float64
}

func (v *Vertex) test(){
v.X++;
v.Y++;
}
func main(){
  v2 := Vertex2{2,3}
  v2.test()
}

# awesomeProject
. est.go:77:3: v.test2 undefined (type Vertex2 has no field or method test)

 

题外话插一嘴,关于go的接口的,这一部分参考go by exampel:

package main

import (
    "fmt"
    "math"
)

type geometry interface {
    area() float64
    perim() float64
}

type rect struct {
    width, height float64
}
type circle struct {
    radius float64
}

func (r rect) area() float64 {
    return r.width * r.height
}
func (r rect) perim() float64 {
    return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

func measure(g geometry) {            # 这里才体现出了接口的作用,入参统一为geometry,所以下面↓
    fmt.Println(g)
    fmt.Println(g.area())
    fmt.Println(g.perim())
}

func main() {
    r := rect{width: 3, height: 4}
    c := circle{radius: 5}

    measure(r)                        # 这里这里,r和c不是同一个结构体对象,但是可以同时作为入参使用measure函数,并且满足在measure中的g.area()和g.perim()方法
    measure(c)
}

```

知识储备有限,很可能会出现错误,欢迎指正,尤其是go语言部分刚刚接触,所以如果有错误请留言??

参考:

https://www.jianshu.com/p/cd06818935a8

https://www.cnblogs.com/hongjijun/p/12670584.html

以上是关于关于鸭子类型的主要内容,如果未能解决你的问题,请参考以下文章

关于python鸭子类型和白鹅类型

什么是动态调度和鸭子类型?

为啥 TypeScript 中的类允许使用鸭子类型

Python 中的鸭子类型和猴子补丁

如何用结构模式匹配表达 hasattr() 鸭子类型逻辑?

面向对象之鸭子类型