假设我有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
工作?
答案 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)
并完成它!
一些注意事项:
当您具有易于构造的“可命名”类型时,通常使用“工厂”(或工厂模式)。考虑一下如何使用scipy.interpolate.interp1d
(请参阅here)中的kind
选项,它基本上是您可以用来进行插值的所有不同策略的枚举。本质上,这是一个工厂(但为了方便使用而隐藏在函数内部)。您可以想象这可能是独立的,所以您将调用“策略”工厂,然后将其传递给interp1d
调用。但是,内联完成是Python中的常见模式。观察:这些策略很容易“命名”,通常很难构建(您可以想象必须传递一个进行线性插值的函数而不是仅仅进行kind='linear'
会很烦人)。这就是使工厂模式有用的原因……
如果您不知道A
是什么,那么绝对不是您要应用的工厂模式。此外,如果您不知道要序列化/反序列化的内容,则将无法调用或使用它。您必须知道这一点,或者有某种推断的方式。
接口不是 强制的,就像在其他语言(如Java / C ++)中一样。这就是鸭子打字的精神。如果接口执行类似于调用x.sum()
的操作,那么实际上x
的类型并不重要,它只需要具有一个称为sum()
的方法即可。如果它像“ sum”鸭子一样运作,或者像“ sum”鸭子一样吵架,那么从Python的角度来看,它就是。 x
是numpy
数组还是A
都没关系,它可以一样工作。在Java / C ++中,除非编译器绝对确定x
已定义方法sum
,否则类似的东西不会编译。幸运的是,Python不是那样的,所以您甚至可以即时定义它(也许您正在尝试使用B
进行定义)。无论哪种方式,接口在Python中的概念都与其他主流语言完全不同。
P.S。
在课程中,您说但是
type(A).__name__
和type(factory(1, 2)).__name__
是等效的
B.__name__ = 'A'
时会明确地这样做。所以我不确定你要去那里做什么...
HTH!