2015-09-19 102 views
2

我正在使用一些开源的python代码,我需要做一些修改。这是开始的情况。Python - 如何正确地扩展类来修改基本方法的操作

启动状态

class BaseGatherer: 

    def get_string(self): 
     #a bunch of stuff I don't need to modify 
     return self.my_string #set with a hard value in __init__() 

class BaseDoer: 

    def do_it(self, some_gatherer): 
     #a bunch of stuff I don't need to modify 
     string_needed = some_gatherer.get_string() 
     self.do_stuff(string_needed) 

外部看,这会习惯于如下:

my_gatherer = BaseGatherer() 
my_doer = BaseDoer() 
my_doer.do_it(my_gatherer) 

我需要改变

我需要做的什么是两件事:

  1. BaseGatherer::get_string()返回它的my_string,插入的修改每次调用时都会发生更改。例如,它第一次被调用我得到my_string[0:1] + 'loc=0' + my_string[1:],我第二次获得'my_string[0:1] + 'loc=10' + my_string[1:]

  2. 修改BaseDoer::do_it()调用BaseDoer::do_stuff()在一个循环(直到有停止条件),每段时间设定string_neededsome_gatherer.get_string()它通过每次调用#1都会返回一个不同的字符串。

有下列限制

  • 我使用的基本代码将定期更新,我不希望在所有修改的代码;我希望能够克隆我从中获得的回购,并且只能修改我的“扩展”代码。可以假设BaseGathererBaseDoer类的名称在基本代码中没有更改,也没有我在意的方法的名称,尽管我不需要修改的某些辅助方法将得到更新(其中对我来说是关键)。

我的问题

我的主要问题是什么是最好的,最Python化的方式做到这一点

我尝试

鉴于我所提到的限制,我的第一个倾向是写两个BaseGathererBaseDoer这使我需要分别编写的get_string()do_it()方法的新版本的变化的派生类。但是,我有一种感觉,我应该使用函数装饰器来做到这一点。我已经阅读了他们一些,我得到了基本的想法(他们包装一个函数,以便您可以控制/修改传递给参数或从你正在包装的函数返回的值,而不修改该函数?;请纠正我,如果我我错过了关键的东西)。但是,我不知道如何在派生类中实现它,无论在语法上还是逻辑上。例如,我是否必须给函数装饰器编写一个@classmethod装饰器?如果是这样,为什么?

这是我做的。它有效,但我想学习和理解a)做我想做的事情的正确方式是什么,b)如何实际做到这一点。

class DerivedGatherer(BaseGatherer): 

    def __init__(self): 
     super(DerivedGatherer, self).__init__() #I'm in Python 2.7 :(
     self.counter = 0 #new variable not in BaseGatherer 

    #DON'T override BaseGatherer::get_string(), just write a new function 
    def get_string_XL(self): 
     self.counter += 10 
     return self.my_string[0:1] + 'loc=' + str(self.counter) + self.my_string[1:] 

class DerivedDoer(BaseDoer): 

    def do_it_XL(self, some_derived_gatherer): 
     while(~some_stop_condition()): 
      string_needed = some_derived_gatherer.get_string_XL() 
      self.do_stuff(string_needed) 

,那么我会叫它如上刚刚创建,但衍生的实例并调用它们XL方法,而不是基类人的。

但是,有这样的问题,不符合我上面的目标/要求:BaseGatherer::get_string()BaseDoer::do_it()执行很多其他功能,我不得不复制到我的新功能(请参阅其中的评论) 。这意味着当我使用的代码得到更新时,我必须进行复制以更新我的派生类。但是,我只是在改变你在这里看到的内容:在每次调用时都会改变的字符串中插入一些内容,然后在do_it()的末尾处放置一个循环。这就是为什么我有一种感觉,即函数装饰器是要走的路。我可以包装get_string()do_it(),这样对于前者,我可以通过查看本地计数器来修改每个调用的字符串,对于第二个,我可以只有一个循环,在循环中调用基地do_it()并传递不同的字符串每次(可以多次拨打do_it())。

任何帮助建议最好的方式来做到这一点,以及如何将非常感激。

谢谢!

+1

如果你打算子类,只是子类。不要打扰装饰者。如果你需要将它们应用到多个地方,装饰者是非常棒的,但在这里并不是这样。 –

+0

感谢您的回应,但正如我在最后提到的,我在这里的尝试要求我为基本方法'get_string()'和'do_it()'复制一堆逻辑到我的新版本中(我只是将它遗漏了这里)。我想做的是“重载”基本方法,而不必重写所有的逻辑,因此每次使用我的代码时都更新我的代码。我想跟随DRY。 – ministry

+0

如果您需要复制代码,装饰者不会让您避免复制代码。装饰者不是那样的魔术。 –

回答

1

这是最容易与普通的继承做,没有码重复:

class DerivedGatherer(BaseGatherer): 

    def __init__(self): 
     super(DerivedGatherer, self).__init__() 
     self.counter = 0 

    def get_string(self): 
     self.counter += 10 
     super_string = super(DerivedGatherer, self).get_string() 
     return super_string[0:1] + 'loc=' + str(self.counter) + super_string[1:] 

class DerivedDoer(BaseDoer): 

    def do_it(self, some_derived_gatherer): 
     while(~some_stop_condition()): 
      super(DerivedDoer, self).do_it(some_derived_gatherer) 
1

正如Martijn向您解释的那样,您可以从子类中的方法轻松地调用基类中的方法。你可以用super来做到这一点,以确保你获得整个班级结构。为了说明,不过,如果你有只有一个超类和一个子类简单的层次,这是你如何能做到这一点:

>>> class Foo(object): 
...  def xxx(self): 
...   return "Foo.xxx was called" 
... 
>>> class Bar(Foo): 
...  def xxx(self): 
...   return "Bar.xxx was called and then %s" % Foo.xxx(self) 
... 
>>> Bar().xxx() 
'Bar.xxx was called and then Foo.xxx was called' 
+0

不管你现在有多少基类,使用super都不是更好的做法吗? – Ben

+0

@Ben最好的做法是保持一致,这是肯定的。 Google为“超级考虑超级”和“超级考虑有害”。 Python 3中新的super()语法非常棒;对于Python 2.7来说,它是IMO的一个教学问题。 –

+2

我一直认为,如果你使用super,它会正确地遵循MRO,但是手动定义下一个要调用的基类会打破这个。 – Ben