如何在Python中“愚弄”鸭子输入

时间:2018-11-05 16:53:35

标签: python duck-typing

假设我有A类:

 class A:

     def __init__(self, x, y):
         self.x = x
         self.y = y

     def sum(self):
         return self.x + self.y

然后我定义了一个工厂方法factory

def factory(x, y):

    class B: pass

    b = B()
    setattr(b, 'x', x)
    setattr(b, 'y', y)
    B.__name__ = 'A'
    return b

现在,如果我执行print(type(A(1, 2)))print(type(factory(1, 2))),它们将显示它们是不同的类型。如果我尝试做factory(1, 2).sum(),我会得到一个例外。但是,type(A).__name__type(factory(1, 2)).__name__是等效的,如果我执行A.sum(factory(1, 2)),我将得到3,就好像我使用A调用它一样。所以,我的问题是这样:

在不对B定义factory(1, 2).sum()或不进行继承的情况下,我该怎么做才能使sum工作?

1 个答案:

答案 0 :(得分:4)

我认为您从根本上误解了工厂模式,并且可能对接口在Python中的工作方式感到困惑。要么,要么 I 从根本上被这个问题弄糊涂了。无论哪种方式,我们都需要进行一些整理。

  

在不使用factory(1、2).sum()的情况下,我该怎么办?   在B上定义总和还是继承?

只需返回A而不是其他类型的

def factory(x, y):
    return A(x, y)

然后

print(factory(1,2).sum())

将按预期输出3。但这是一个无用的工厂……可以A(x, y)并完成它!

一些注意事项:

  1. 当您具有易于构造的“可命名”类型时,通常使用“工厂”(或工厂模式)。考虑一下如何使用scipy.interpolate.interp1d(请参阅here)中的kind选项,它基本上是您可以用来进行插值的所有不同策略的枚举。本质上,这是一个工厂(但为了方便使用而隐藏在函数内部)。您可以想象这可能是独立的,所以您将调用“策略”工厂,然后将其传递给interp1d调用。但是,内联完成是Python中的常见模式。观察:这些策略很容易“命名”,通常很难构建(您可以想象必须传递一个进行线性插值的函数而不是仅仅进行kind='linear'会很烦人)。这就是使工厂模式有用的原因……

  2. 如果您不知道A是什么,那么绝对不是您要应用的工厂模式。此外,如果您不知道要序列化/反序列化的内容,则将无法调用或使用它。您必须知道这一点,或者有某种推断的方式。

  3. Python中的
  4. 接口不是 强制的,就像在其他语言(如Java / C ++)中一样。这就是鸭子打字的精神。如果接口执行类似于调用x.sum()的操作,那么实际上x的类型并不重要,它只需要具有一个称为sum()的方法即可。如果它像“ sum”鸭子一样运作,或者像“ sum”鸭子一样吵架,那么从Python的角度来看,它就是xnumpy数组还是A都没关系,它可以一样工作。在Java / C ++中,除非编译器绝对确定x已定义方法sum,否则类似的东西不会编译。幸运的是,Python不是那样的,所以您甚至可以即时定义它(也许您正在尝试使用B进行定义)。无论哪种方式,接口在Python中的概念都与其他主流语言完全不同。

P.S。

  

但是type(A).__name__type(factory(1, 2)).__name__是等效的

课程中,您说B.__name__ = 'A'时会明确地这样做。所以我不确定你要去那里做什么...

HTH!