1

我有一个图书馆,有一位家长和十几个孩子:

# mylib1.py:
#
class Foo(object):
  def __init__(self, a):
    self.a = a


class FooChild(Foo):
  def __init__(self, a, b):
    super(FooChild, self).__init__(a)
    self.b = b

# more children here...

现在我想用一个简单的(但有点特别,用于另一种方法)方法来扩展该库。所以我想改变父类并使用它的孩子。

# mylib2.py:
#
import mylib1

def fooMethod(self):
  print 'a={}, b={}'.format(self.a, self.b)

setattr(mylib1.Foo, 'fooMethod', fooMethod)

现在我可以像这样使用它:

# way ONE:
import mylib2

fc = mylib2.mylib1.FooChild(3, 4)
fc.fooMethod()

或像这样:

# way TWO:
# order doesn't matter here:
import mylib1
import mylib2

fc = mylib1.FooChild(3, 4)

fc.fooMethod()

所以,我的问题是:

  1. 这是好事吗?
  2. 这应该如何以更好的方式完成?
4

2 回答 2

4

一种常见的方法是使用mixin

如果需要,可以动态添加如何动态添加 mixin 作为基类而不会出现 MRO 错误?.

于 2015-06-12T09:05:31.613 回答
0

编程中有一个通用规则,即您应该避免依赖全局状态。换句话说,这意味着您的全局变量应该尽可能保持不变。类(大部分)是全局的。

您的方法称为猴子修补。如果你没有一个非常好的理由来解释它,你应该避免它。这是因为猴子补丁违反了上述规则。

想象一下,您有两个独立的模块,并且它们都使用这种方法。其中之一将 Foo.fooMethod 设置为某个方法。另一个 - 另一个。然后你以某种方式在这些模块之间切换控制。结果是,很难确定 fooMethod 在哪里使用。这意味着很难调试问题。

有些人(例如 Brandon Craig-Rhodes)认为即使在测试中修补也很糟糕。

我建议使用在实例化 Foo() 类(及其子类)的实例时设置的一些属性,该属性将控制 fooMethod 的行为。那么这个方法的行为将取决于你如何实例化对象,而不是全局状态。

于 2015-06-12T09:07:24.293 回答