如何在Python中"愚弄"鸭子打字

Woo*_*193 5 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
Run Code Online (Sandbox Code Playgroud)

我定义了一个名为的工厂方法factory:

def factory(x, y):

    class B: pass

    b = B()
    setattr(b, 'x', x)
    setattr(b, 'y', y)
    B.__name__ = 'A'
    return b
Run Code Online (Sandbox Code Playgroud)

现在,如果我这样做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.所以,我的问题是:

如果factory(1, 2).sum()不在sumB上定义或继承,我需要做什么才能完成工作?

Mat*_*ith 4

我认为你从根本上误解了工厂模式,并且可能对 Python 中接口的工作方式感到困惑。要么是这样,要么从根本上对这个问题感到困惑。不管怎样,我们需要做一些整理。

我需要在这里做什么才能使 factory(1, 2).sum() 工作而不在 B 上定义 sum 或进行继承?

只需返回一个A而不是其他类型:

def factory(x, y):
    return A(x, y)
Run Code Online (Sandbox Code Playgroud)

然后

print(factory(1,2).sum())
Run Code Online (Sandbox Code Playgroud)

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

一些注意事项:

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

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

  3. Python 中的接口不像Java/C++ 等其他语言那样强制执行。这就是鸭子打字的精神。如果一个接口执行类似 call 的操作x.sum(),那么实际上是什么类型并不重要x,它只需要有一个名为 的方法即可sum()。如果它的行为像“sum”鸭子,叫起来像“sum”鸭子,那么从Python的角度来看,它就是“sum”鸭子。无论xnumpy数组还是A,它都可以正常工作。在 Java/C++ 中,除非编译器绝对确定已定义该方法,否则此类内容不会编译。幸运的是,Python 不是这样的,所以您甚至可以动态定义它(也许您正在尝试使用)。不管怎样,Python 中的接口概念与其他主流语言中的接口概念有很大不同。xsumB

聚苯乙烯

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

当然是,当你说 时,你明确地这样做B.__name__ = 'A'。所以我不确定你想达到什么目的......

哈!