面试扣分点:什么是鸭子类型

来源:未闻Code

有一类面试官特别讨厌,面试的时候,会问一些偏、难、怪的题目。假设你今天去面试,遇到面试官问你:“什么是鸭子类型?”。你怎么回答?

熟读维基百科的你,脑海中闪过了下面这张截图:

0f9371155be35068078228b37eb0e9e5.png

图中的红框像闪电一样从你的脑子里划过。你用中指扶了扶黑框眼镜,自信地说道:

鸭子类型就是说,一个函数不会关心它传入参数的类型,只关心这个参数对应的对象有没有自己想要的方法和属性。如果有,就能运行。如果没有,就不能运行。这就像是我看到了一只鸟,只要它能像鸭子一样叫,像鸭子一样走路,有鸭子一样的白色羽毛,那么,无论它实际上是什么东西,我都认为它是鸭子。

说完这段话,一道光从你的镜片上一闪而过。你心里想,这下稳了。

面试官又问:那你用 Golang 写一个鸭子类型的例子。

你一想,Golang 是静态语言啊,参数都要声明类型的,怎么绕过它的类型检测呢?你又转念再一想,不对,Golang 确实可以绕过类型检测的。使用接口就可以了。

于是你刷刷刷写下来一段 Golang 语言的代码:

package main


import (
    "fmt"
)


type Animal interface {
    Sleep()
    Eat(food string)
}


type People struct {
    name string
}

type Pet struct {
    name string
}

func (p People) Sleep(){
    fmt.Println(p.name, "在睡觉")
}

func (p Pet) Sleep() {
    fmt.Println(p.name, "在睡觉")
}

func (p People) Eat(food string) {
    fmt.Printf("%s在吃%s\n", p.name, food)
}

func (p Pet) Eat(food string) {
    fmt.Printf("%s在吃%s\n", p.name, food)
}

func check(animal Animal) {
    animal.Eat("狗狼")
    animal.Sleep()
}


func main(){
    singleDog := People{name: "单身狗",}
    dog := Pet{name: "旺财",}
    check(singleDog)
    check(dog)
}

代码运行效果如下图所示:

10ccc63a825f0694d93d9169d8ce1cb0.png

然后你解释道,在函数main()里面,变量singleDog的类型是 People 类型,变量dog的类型是Pet类型。虽然他们是不同的类型,但是由于他们都有Eat方法和Sleep方法,所以,他们都能在check函数里面运行。

面试官又问,你的代码写得没有问题,例子也举得没有问题。那我再问你,既然check函数不关心传入参数的类型,只关心他们的方法,是不是说明check函数接收的参数是鸭子类型?

你说,是的。

面试官又问,但是,我们从代码里面可以看到,check函数接收的这个参数animal的类型是接口类型。那是不是说明接口类型等于鸭子类型

你一时回答不上来。

面试官又问:那接口类型和鸭子类型是什么关系?鸭子类型是像intstringmap这样内置的类型吗?我们可以在 Golang 里面使用var a string 声明一个类型为string的变量,那请问怎么声明一个类型为鸭子的变量?

你一时想不起来 Golang 自带的关键词里面,哪个关键词包含duck这个单词。

面试官露出了耐克式的微笑,对你说:“回家等通知吧。”

这个讨厌的面试官最后一个问题把你难住了。但是这个问题其实是一个陷阱。面试官给你玩了一个文字游戏。当他把鸭子类型整型、字符串类型合在一起说的时候,让你觉得鸭子类型也是一种类型。但实际上鸭子类型并不是一种类型,鸭子类型是一种动态类型的风格:

dff85f9c9bc1a5c095f78ebcd81c131a.png

怎么解释什么叫做设计风格呢?我们再用 Python 举个例子:

  • 确保传入的变量必须是特定类型,再执行对应的方法

# 确保参数是特定类型再调用里面的方法
def check(animal):
    if isinstance(animal, Pet):
        animal.eat()
    elif isinstance(animal, People):
        animal.eat()
    else:
        raise Exception("类型错误!")
  • 不管传入的参数是什么类型,只要它有 eat方法都能执行。如果这个对象没有eat方法,Python 自动就会抛出异常。

def check(animal):
    animal.eat()

在鸭子类型这种设计风格中,开发者不关心对象是什么类型。它只关心对象有没有特定的方法。

总结:鸭子类型是一种设计风格,不是一种具体的类型。

-------- End --------

39dd8c25dd9d520b178ceae0472fd280.png
精选资料

回复关键词,获取对应的资料:

关键词资料名称
600《Python知识手册》
md《Markdown速查表》
time《Python时间使用指南》
str《Python字符串速查表》
pip《Python:Pip速查表》
style《Pandas表格样式配置指南》
mat《Matplotlib入门100个案例》
px《Plotly Express可视化指南》
精选视频

可视化: Plotly Express

财经: Plotly在投资领域的应用 | 绘制K线图表

排序算法: 汇总 | 冒泡排序 | 选择排序 | 快速排序 | 归并排序 | 堆排序 | 插入排序 | 希尔排序 | 计数排序 | 桶排序 | 基数排序

f8c80ed4751544dc7991da391c916fe8.png
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值