【问题标题】:Python design pattern for magic methods and pandas inheritance魔术方法和熊猫继承的Python设计模式
【发布时间】:2018-02-07 23:39:25
【问题描述】:

所以我基本上提出这个问题是为了一些建议

我有几个类,它们基本上会执行一些 pandas 操作并返回一个数据框。但是这些数据框需要在某些过滤器选项上加减。我计划编写一个类来覆盖 __add____sub__ 方法,以便实现这些过滤器的代码添加或减去这些数据帧。下面是一个基本结构

import ArithOperation
class A:
  def dataA(self, filenameA):
    dfa = pd.read_excel(filenameA)
    return ArithOperation(dfA)

class B:
  def dataB(self, filenameB):
    dfb = pd.read_excel(filenameB)
    return ArithOperation(dfB)

dfA 和 dfB 这里是 pandas 数据帧。

class ArithOperation:
  def __init__(self, df):
     self.df = df
  def __add__(self, other):
     # here the filtering and manual addition is done
     return ArithOperation(sumdf)
  def __sub__(self, other):
     # here the filtering and manual subtraction is done
     return ArithOperation(deltadf)

基本上我做如下计算

dfa = A().dataA()
dfb = B().dataB()

sumdf = dfa+dfb
deltadf = dfa-dfb

但是如何使 sumdfdeltadf 也具有默认数据框功能。我知道我应该将数据框类继承到 ArithOperation 但我对在许多地方实例化 ArithOperation() 感到困惑和不舒服。

  1. 是否有更好的设计模式来解决这个问题?
  2. 要在 ArithOperation 上继承哪个类,以便我在 ArithOperation 对象上也拥有所有 pandas 数据框函数?

【问题讨论】:

    标签: python inheritance design-patterns


    【解决方案1】:

    在我看来,您想要自定义现有类型的行为(在这种情况下是数据框,但它可以是任何类型)。通常我们想要将某些行为更改为不同的行为,或者我们想要扩展该行为,或者我们想要两者都做。当我说行为思考方法时)。

    您还可以在 has-a 方法之间进行选择(即包装一个对象,方法是创建一个新类,其对象包含对原始对象的引用。这样您就可以创建几个新的、不同的或类似的方法来创建新的事情,最终使用一些现有的,通过使用存储的引用来调用原始方法。这样你就可以将原始类接口适应不同的接口。这被称为包装器模式(或适配器模式)。

    这就是你所做的。但是接下来你面临一个问题:你如何接受原始类的所有可能的方法?您将不得不重写所有可能的方法(不实用),只是将它们委托给原始类,或者您找到一种方法将它们全部委托给除了您覆盖的少数几个方法。我不会介绍最后一种可能性,因为您可以使用继承,这使得事情变得非常简单。

    只需从原始类继承,您就可以创建具有与父类相同行为的对象。如果您需要新的数据成员,请覆盖__init__ 并添加它们,但不要忘记调用父类__init__,否则对象将无法正确初始化。这就是您使用super().__init__() 的地方,如下所述。如果您想添加新方法,只需将常用的定义添加到您的子类。如果要扩展现有方法,请按照__init__ 方法的说明进行操作。如果您想完全覆盖一个方法,只需使用与 original.method 相同名称的 def 编写您的版本,它将完全替换原来的。

    因此,在您想要从数据框类继承的情况下,您必须选择是否需要自定义 __init__。如果您需要定义一个,但不要忘记在其中调用父级的原件。然后定义您的自定义方法,比如新的__add____sub__,它们可以替换或扩展原始方法(使用相同的技术)。 请注意不要定义您认为是新的但实际上存在于原始类中的方法,因为它们将被覆盖。这是继承的一个小不便,特别是如果原来的接口有很多方法。

    使用super() 扩展父类的行为

    class A:
        pass # has some original __init__
    
    class B(A): # B inherits A
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs) # preserves parent initialization, passing the same received arguments. If you don't do this parent __init__ will be overrided, not initialising parent object properly.
            # add your custom code
            pass
    

    super() 的问题在于它解决了在使用多重继承(从多个类继承)时出现的一些问题,在确定应该调用哪个父方法时,如果您有多个具有相同名称的方法;建议在子类中使用SomeParentClass.method() 调用父类的方法。

    此外,如果您因为需要某个类型而对它进行子类化,那么您不应该像您所说的那样害怕“无处不在”地使用它。确保您出于充分的理由对其进行自定义(因此请考虑将其设置为数据框是否合适,或者是否有另一种更简单的方法可以在不这样做的情况下实现相同的目标;在这里无法建议您,我没有使用熊猫的经验) ,然后用它代替原来的类。

    【讨论】:

    • 对不起,我听不懂。你能解释得更详细一点吗,因为我是编程新手
    • 哦,根据你写问题的方式,你看起来不像那样,抱歉。我试图更好地解释。请参阅我的答案编辑。
    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 2022-11-03
    • 2020-05-19
    • 2019-07-20
    • 2011-01-19
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多