Ducktyping引用维基百科的解释:Ducktyping是编程中的一种动态类型风格。在这种风格中,对象的有效语义不是由从特定类继承或实现特定接口决定的,而是由“当前的方法和属性集”决定的。更笼统地说:当你看到一只鸟,走路像鸭子,游泳像鸭子,叫声像鸭子,那么这只鸟就可以称为鸭子。换句话说,在鸭子类型中,重点是对象的行为以及它能做什么;而不是它所属的对象类型。让我们看一个例子,更形象地展示一下:#这是一个鸭子(Duck)类classDuck:defeat(self):print("Aduckiseating...")defwalk(self):print("一只鸭子在走路...")#这是一只狗(Dog)classclassDog:defeat(self):print("Adogiseating...")defwalk(self):print("Adogiswalking...")defanimal(obj):obj.eat()obj.walk()if__name__=='__main__':animal(Duck())animal(Dog())程序输出:一只鸭子正在吃东西...鸭子在走路...狗在吃东西...狗在走路...Python是一种动态语言,没有严格的类型检查。Duck和Dog只要分别实现了eat和walk方法,就可以直接调用了。另一个例子是list.extend()方法。除了list之外,dict和tuple也可以调用,只要是可迭代的即可。看完上面的例子,你应该对“对象行为”和“对象类型”有了更深的理解。稍微扩展一下,鸭子类型实际上与接口非常相似,只是没有显式定义接口。例如Go语言实现duck类型,代码如下:packagemainimport"fmt"//定义接口,包括Eat方法typeDuckinterface{Eat()}//定义Cat结构体,并实现theEatmethodtypeCatstruct{}func(c*Cat)Eat(){fmt.Println("cateat")}//定义Dog结构体并实现Eat方法typeDogstruct{}func(d*Dog)Eat(){fmt.Println("dogeat")}funcmain(){varcDuck=&Cat{}c.Eat()vardDuck=&Dog{}d.Eat()s:=[]Duck{&Cat{},&Dog{},}for_,n:=ranges{n.Eat()}}通过显式定义一个Duck接口来实现,每个结构体都实现接口中的方法。MonkeyPatchMonkeyPatch(MonkeyPatch)名声不好,因为它会在运行时动态修改模块、类或函数,通常是为了添加功能或修复错误。Monkey补丁工作在内存中,不修改源代码,因此只对当前运行的程序实例有效。但如果被滥用,它会使系统难以理解和维护。有两个主要问题:修补破坏了封装并且通常与目标紧密耦合,因此很容易受到攻击。两个打过补丁的库可能会相互卡住,因为第二个库可能会撤消第一个库的补丁。因此,它被认为是一种临时解决方法,而不是推荐的集成代码的方法。按照惯例,我举个例子来说明一下:#定义一个Dog类classDog:defeat(self):print("Adogiseating...")#在Dog类外添加一个monkeypatchclassdefwalk(self):print("Adogiswalking...")Dog.walk=walk#调用方式与类内部定义的属性和方法相同。dog=Dog()dog.eat()dog.walk()程序输出:Adogiseating...Adogiswalking...这相当于在类外的Dog类中增加了一个walk方法,而调用方法与类内部定义的属性和方法相同。再举一个实际的例子,比如我们常用的json标准库,如果要换成性能更高的ujson,就必须把每个文件的引入:importjson改成:importujsonasjson,如果改成这样成本相对较高。这时候可以考虑使用monkeypatch,只需要在程序入口添加:importjsonimportujsondefmonkey_patch_json():json.__name__='ujson'json.dumps=ujson.dumpsjson.loads=ujson.loadsmonkey_patch_json()这样以后调用dumps和loads方法的时候调用ujson包就很方便了。但是monkeypatch是一把双刃剑,上面也提到了这个问题,根据需要谨慎使用。以上就是本次分享的全部内容。现在想学习编程的朋友欢迎关注Python技术大本营获取更多技能和教程。
