这种与isinstance()混合的Python鸭子打字的方法是否有意义?

Chr*_*ard 1 python oop duck-typing

假设我们有以下类:

class Duck(object):
    pass

class OldFashionedDuck(Organism, Duck):
    def look(self):
        self.display_biological_appearance()
    def walk(self):
        self.keep_balance_on_two_feet()
    def quack(self):
        self.make_noise_with_lungs("Quack!")

class ArtificialDuck(Robot, Duck):
    def look(self):
        self.display_imitation_biological_appearance()
    def walk(self):
        self.engage_leg_clockwork()
    def quack(self):
        self.play_sound("quack.au")
Run Code Online (Sandbox Code Playgroud)

在这个例子中,OldFashionedDuck和ArtificialDuck没有共同的实现,但是通过构造,它们都将返回True for isinstance(...,Duck).

这并不完美,但我认为这可能有助于尊重鸭子打字,并且(通过空mixin继承)允许isinstance().从本质上讲,它提供了一个满足接口的契约,因此它不是基于完成所有工作的类调用isinstance(),而是基于任何人都可以选择加入的接口.

我已经看到基于"isinstance()被认为有害的文章",因为它打破了鸭子打字.但是,我至少作为程序员想知道,如果不一定是对象从哪里获取函数,但是它是否实现了接口.

这种方法是否有用,如果是这样可以改进吗?

Kar*_*tel 6

我已经看到基于"isinstance()被认为有害的文章",因为它打破了鸭子打字.但是,我至少作为程序员想知道,如果不一定是对象从哪里获取函数,但是它是否实现了接口.

我想你错过了这一点.

当我们谈论"鸭子打字"时,我们真正的意思是不正式化我们的界面.因此,您尝试做的事情归结为试图回答"如何在不正式化我的界面的情况下正式化我的界面?"这一问题.

我们期望一个给我们的对象实现一个接口 - 我们描述的那个,不是通过创建一个基类,而是通过编写一堆文档和描述行为(如果我们感觉特别活泼,设置一些测试套件) - 因为我们说这是我们所期望的(再次在我们的文档中).我们通过尝试使用它来验证对象是否实现了接口,并将任何产生的错误视为调用者的责任.调用给我们错误对象的代码是顽皮的,而这就是bug需要修复的地方.(同样,测试可以帮助我们跟踪这些事情.)

简而言之,测试isinstance(this_fuzzball_that_was_handed_to_me, Duck)并不能帮助解决问题:

  • 它可以通过isinstance检查,但以违反我们期望(或者说return NotImplemented)的方式实施方法.这里只做真正的测试.

  • 它可以通过检查,但实际上完全无法实现一个或多个方法; 毕竟,base Duck不包含任何实现,Python没有理由在派生类中检查它们.

  • 也许更重要的是,它可能无法通过检查,即使它是一个完全可用的鸭子fuzzball.也许这是一些无关的对象了quack,walk并且look功能直接,手动连接到它的属性(相对于他们是其类的属性,当抬头而变得方法).

好吧,所以,"不要那样做",你说.但现在你正在为每个人做更多的工作; 如果客户并不总是选择加入,那么使用鸭子代码进行检查是没用的,也是危险的.与此同时,你获得了什么?

这与EAFP原则有关:不要试图通过观察它来弄清楚某些东西是否是鸭子; 弄清楚如果它是一只鸭子,将其视为鸭子并处理如果不是的血腥混乱.


但是如果你不关心鸭子的打字理念,并且绝对必须强迫一些严谨的东西,你可能会对标准库abc模块感兴趣.