最简洁的解释动态语言中的鸭子类型和闭包

常见定义

  • 闭包

是拥有独立变量(在封闭空间中定义的可以在本地环境中使用的变量)的函数

程序语言中的闭包(closure)概念不是由JavaScript最先提出的,从smalltalk开始,闭包就成了编程语言的一个重要概念。几乎所有的知名动态语言(如Perl、python、ruby等)都支持闭包,JavaScript也不例外。

  • 鸭子类型

是程序设计中的一种类型推断风格,这种风格适用于动态语言(比如PHP、Python、Ruby、Typescript、等)和某些静态语言(比如Golang,一般来说,静态类型语言在编译时便已确定了变量的类型,但是Golang的实现是:在编译时推断变量的类型),支持"鸭子类型"的语言的解释器/编译器将会在解析(Parse)或编译时,推断对象的类型。

鸭子类型并不是动态语言特有,但是多数动态语言都带有这个推断风格。

简洁解释

  • 闭包

以python为例,如果你见到的代码和下面的模式大概一样,那么就可以推定是运用了闭包了:

def adder(x):
    def wrapper(y):
        return x + y
    return wrapper

adder5 = adder(5)
# 输出 15
adder5(10)
# 输出 11
adder5(6)

一般情况下,函数执行完之后其内部变量将会不可被访问,但是为什么adder5(10)会输出15,而adder5(6)会输出11呢?按理说应该分别输出20和12的,可是adder(5)中的变量5却一直保留着。

划重点:一般情况下,函数执行完了之后内部变量就会被销毁(垃圾回收机制),但如果函数执行完了,内部变量还有用,那就不能被销毁,既然不能销毁那就得存储起来,正好函数也是对象,对象具有存储数据的能力,于是就把这个不能销毁的变量存在函数里了。这种函数存储变量的属性就叫闭包。

  • 鸭子类型

还是以python为例,鸭子类型其实是一种编译器\解释器的推断风格,就是你敲的代码,运行时解释器是如何识别的,其中有一种识别的模式是鸭子类型。

class A:
    def __init__(self, filename):
        if not filename.endswith(self.ext):
            raise Exception("Invalid file format")
        self.filename = filename

class B(A):
    ext = "banana"
    def play(self):
        print("Playing {} as banana".format(self.filename))

class C(A):
    ext = "captcha"
    def play(self):
        print("Playing {} as captcha".format(self.filename))

class A1:
    def __init__(self, filename):
        if not filename.endswith(".flac"):
            raise Exception("Invalid file format")
        self.filename = filename

    def play(self):
        print("Playing {} as flac".format(self.filename))

咋一看,上面的B、C都继承了A,那么他们完全可以随意调用A中定义的任何元素,但是,仔细一看,A1和A除了名字不同,其他都是一模一样的,那么B、C在只继承A的情况下,能否调用A1呢?答案是可以的!

划重点:所谓鸭子类型,就是只要你跟我的功能和实现的结构一模一样,就算名字不同,我们也是一样的对象。应征了“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子”这句话。

通俗解释就是:当你要重写一个class类A,它的原型是class类B,只要A中实现的方法在B中也有,那么无论def方法内容如何,你不必用A继承B,解释器也会知道你重写的类是继承B的。

猜你喜欢

转载自blog.csdn.net/weixin_42833042/article/details/84561316