【问题标题】:Python, best way to override functions for optimizationPython,重写函数以进行优化的最佳方法
【发布时间】:2020-08-05 14:49:46
【问题描述】:

不知道如何最好地描述这个问题,但我只是想优化一些东西,因为我执行了这 100 次。所以我试图尽可能地消除逻辑。因此,为简单起见,让我们在下面使用一个简单的类:

class Steps():
   def __init__(self, descending, value):
      self.descending = descending
      self.value = value

   def step(self):
      if self.descending:
         self.value -= 1
      else:
         self.value += 1

   def other_funcs1(self):
      pass

   def other_funcs2(self):
      pass

a = Steps(descending=True, value=0)
b = Steps(descending=False, value=0)

a.step() # say this is done 100 million times
b.step() # say this is done 100 million times

由于我们一开始就已经知道它是降序还是升序,所以似乎没有必要在该 step() 中包含逻辑“if self.descending”,尤其是当我们调用它数百万次时。

一种更好的方法是继承一个基类,例如:

class Steps():
   def __init__(self, value):
      self.value = value

   def other_funcs1(self):
      pass

   def other_funcs2(self):
      pass

class StepsInc(Steps):
   def __init__(self, value):
      Steps.__init__(self, value)
   
   def step(self):
      self.value += 1

class StepsDec(Steps):
   def __init__(self, value):
      Steps.__init__(self, value)
   
   def step(self):
      self.value -= 1

a = StepsDec(value=0)
b = StepsInc(value=0)

a.step() # say this is done 100 million times
b.step() # say this is done 100 million times

以上应该更优化,因为我们不再做不必要的'if'语句。但是如果我有多个函数,取决于不同的参数,我该怎么做呢?因此,除了“降序”之外,我还有其他可以具有不同功能的参数。我不想有太多的课,而且看起来很乱。

我想到的一个解决方案不是继承,我可以做这样的事情,但不确定这是否是 pythonic:

class Steps():
       def __init__(self, descending, value):
          self.descending = descending
          self.value = value
          self.step = self.stepDec if self.descending else self.stepInc # can have multiple of this for different functions and parameters
          # more potential example
          #self.func = self.func1 if self.new_param else self.func2

       def stepInc(self):
          self.value += 1

       def stepDec(self):
          self.value -= 1

       def other_funcs1(self):
          pass

       def other_funcs2(self):
          pass

    
    
    a = Steps(descending=True, value=0)
    b = Steps(descending=False, value=0)
    
    a.step() # say this is done 100 million times
    b.step() # say this is done 100 million times

所以这个看起来比另一个继承基类的类更干净。而且我可以拥有许多基于不同参数的其他功能。所以有两个问题:1)这是pythonic吗?,如果不是更好的方法2)我怎样才能覆盖像“_ repr _”这样的默认函数?

所以对于第二个问题,假设 print(a) 我想打印“我正在下降”,而 print(b) 我想打印“我正在上升”。我知道这没有多大意义,但只是想知道如何覆盖这些。我绝对可以使用继承类示例来做到这一点,其中每个示例都有自己的“_ repr _”函数。但是有没有办法用我最新的例子来做到这一点?因为这看起来更干净,尤其是根据不同的参数有更多的功能。

希望这个问题很清楚。感谢您的帮助

【问题讨论】:

  • 关于打印的第二个问题。在上一个示例中,您只需像设置 self.step 那样做。 self.step_str = "descending" if self.descending else "ascending"。然后将其传递给您的 print()。
  • 呃,我怎么没想到。谢谢!猜猜原来的问题更复杂,似乎并不直接,因为还需要传递其他值。但你肯定回答了我的问题,哈哈。这是pythonic吗?或者我可以做一个更好的命名约定来确保它是一个函数而不是一个变量?
  • 如果你根据其他值递增/递减一个,我想知道你是否可以在你的类中添加一个字段_delta。该字段为 1 或 -1,并且只要其中一个参数更改,就会更新。在step() 函数中,您只需执行self.value += self._delta。不再有ifelse,但现在您必须添加一个非常量值。
  • 是的,我想这个例子可行,但真正的问题实际上是按数量来的。而且这个数量每一步都在变化。是的,我知道很难尝试用简化版本来描述实际问题,但我肯定会在未来考虑这种解决方案

标签: python optimization overriding overloading


【解决方案1】:

嗯,有一种很好的方法可以解决您的问题。解决方案只是通过在构造函数中注入一个方法来动态地向一个对象添加一个方法。因为,解决方案很简单,所以我只是提供代码:

# note: in this approach the method only exists with object instance
#       the method has nothing to do with class

# note this import

import types

def desc(self):
    self.value -= 1

def inc(self):
    self.value += 1

class Steps():
    def __init__(self, func, descending, value):
        # add func as a method
        # where the method name is step
        self.step = types.MethodType(func, self)
        # nothing changes here
        self.descending = descending
        self.value = value

a = Steps(desc, True, 0)
b = Steps(inc, False, 0)

a.step();
print("value of a: {}".format(a.value));

b.step();
print("value of b: {}".format(b.value));

输出是:

value of a: -1
value of b: 1

现在,您可能会惊讶地听到:我不会向任何人推荐它

让我解释一下原因:

  1. 首先,如果您以这种方式创建大量对象,则需要将大量方法与这些对象关联。您可能会浪费大量内存。
  2. 对象实例与不属于类的方法绑定。这可能会给正在阅读您的代码的人造成混淆。
  3. 如果您遵循紧密的 oop 编码风格,那么您只是将这些函数留在开放中,或者可能只是封装在一个类中,但这并不是解决此问题的行之有效的方法。

我知道,性能是您非常关心的问题,但我仍然建议您查看strategy pattern。当您了解这几节课会给您带来多大的灵活性时,再上几节课不会伤害您。并且每个知道该模式的人都会一眼就能理解您的代码。

现在,我已经说了很多。但是,我想提的最后一件事,如果你真的对performance 非常感兴趣,你可以write an extensionswitch to a compiled(faster) language

【讨论】:

  • 感谢您的详细回复。问题,这个例子是不是你有一个策略模式,但你不会推荐它?
  • @user1179317 它看起来像策略模式。但它不是正确的版本。特别是实施方法。最好通过对接口进行编程来实现“策略模式”(或者,您也可以说,对超类型进行编程)。但在这个例子中,这个方面确实缺失了。我希望你现在清楚了。
  • 老实说,我不清楚有什么区别。但我会试着去寻找它。不过谢谢
  • @user1179317 同样,您可以争论是否是“策略模式”,并且可以深入讨论,但在我看来,最好不要将其称为“策略模式”。当我想到模式时,有两个想法对我来说非常重要:1. 他们解决问题的方式和 2. 他们的实施方式。如果他们中的任何一个偏离轨道,我称之为该模式的糟糕版本,并且不推荐它。如果 python 有 interface 构造,那么让你理解会更好,我们使用 interface 作为 supertype 来代替 strategy objects。(我知道 python 有 ABC 虽然)
  • 非常感谢。我完全理解你的意思
猜你喜欢
  • 2015-01-17
  • 2020-10-30
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 2018-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
相关资源
最近更新 更多