【问题标题】:Using base class for all object creation使用基类创建所有对象
【发布时间】:2017-05-05 23:02:17
【问题描述】:

一位高级开发人员希望我在 Python 中实现面向对象编程,其中我们使用 Base 类实例化所有对象创建。它不适合我,因为在派生类必须实现的基类中有抽象方法。他只使用 Base 类作为实例化对象的一种方式的理由是,当我们遍历对象列表时,我们可以以相同的方式访问它的变量和方法。由于基类的每个派生对象都具有比基类更多的实例化属性,因此他建议 init 函数将 *args 和 **kwargs 作为参数的一部分。

这是一个很好的方法吗?如果没有,你能帮忙推荐一个更好的选择吗?

这是一个简单的实现示例。

import abc
class Base(metaclass = abc.ABCMeta):
    def __init__(self, reqarg1, reqarg2, **kwargs):
        self.reqarg1 = reqarg1
        self.reqarg2 = reqarg2
        self.optarg1 = kwargs.get("argFromDerivedA", 0.123)
        self.optarg2 = kwargs.get("argFromDerivedB", False)
        self.dict = self.create_dict()

    @abstractmethod
    def create_dict(self):
        pass

    def get_subset_list(self, id):
        return [item for item in self.dict.values() if item.id == id] 

    def __iter__(self):
       for item in self.dict.values():
           yield item
    raise StopIteration()


class Derived_A(Base):
    def __init__(self, regarg1, regarg2, optarg1):
        super().__init__(regarg1, regarg2, optarg1)

    def create_dict(self):
        # some implementation
        return dict

class Derived_B(Base):
    def __init__(self, regarg1, regarg2, optarg2):
        super().__init__(regarg1, regarg2, optarg2)

    def create_dict(self):
        # some implementation
        return dict      

编辑:为了清楚起见,我不太清楚如何正确处理基类中的抽象方法,因为高级开发人员希望按如下方式使用它:

def main():
    b = Base(100, 200)
    for i in get_subset_list(30):
        print(i)

但是Base类中的dict没有定义,因为它是在派生类中定义的,因此会输出如下错误:

NameError: name 'abstractmethod' 未定义

【问题讨论】:

  • Python 不是强类型语言。 IE。您不必担心这类事情。
  • @IgnacioVazquez-Abrams:感谢您的建议;我会调查的
  • @AbdelhakimAkodadi:Python 不是强类型语言吗?你能扩展你的思维方式吗?
  • 我认为这实际上是SoftwareEngineering 的更多问题,因为它是关于软件架构的(我可能错了)。

标签: python python-3.x oop derived-class base-class


【解决方案1】:

我的建议是在Base 类中使用工厂类方法。您只需能够根据提供的输入确定您需要返回的Derived 类。我将复制一个实现,假设您需要 Derived_A(如果您提供关键字 optarg1)和 Derived_B(如果您提供关键字 optarg2)。当然,这完全是人为的,您应该根据自己的需要进行更改。

import abc
class Base(metaclass = abc.ABCMeta):
    @classmethod
    def factory(cls,reqarg1,reqarg2,**kwargs):
        if 'optarg1' in kwargs.keys():
            return Derived_A(reqarg1=reqarg1,reqarg2=reqarg2,optarg1=kwargs['optarg1'])
        elif 'optarg2' in kwargs.keys():
            return Derived_B(reqarg1=reqarg1,reqarg2=reqarg2,optarg2=kwargs['optarg2'])
        else:
            raise ValueError('Could not determine Derived class from input')
    def __init__(self, reqarg1, reqarg2, optarg1=0.123, optarg2=False):
        self.reqarg1 = reqarg1
        self.reqarg2 = reqarg2
        self.optarg1 = optarg1
        self.optarg2 = optarg2
        self.dict = self.create_dict()
    @abc.abstractmethod
    def create_dict(self):
        pass

    def get_subset_list(self, id):
        return [item for item in self.dict.values() if item.id == id] 

    def __iter__(self):
        for item in self.dict.values():
            yield item

class Derived_A(Base):
    def __init__(self, reqarg1, reqarg2, optarg1):
        super().__init__(reqarg1, reqarg2, optarg1=optarg1)

    def create_dict(self):
        # some implementation
        dict = {'instanceOf':'Derived_A'}
        return dict

class Derived_B(Base):
    def __init__(self, reqarg1, reqarg2, optarg2):
        super().__init__(reqarg1, reqarg2, optarg2=optarg2)

    def create_dict(self):
        # some implementation
        dict = {'instanceOf':'Derived_B'}
        return dict

这将允许您始终创建一个 Derived_X 类实例,该实例将在您 __init__ 时定义 create_dict 非抽象方法。

In [2]: b = Base.factory(100, 200)
ValueError: Could not determine Derived class from input

In [3]: b = Base.factory(100, 200, optarg1=1213.12)

In [4]: print(b.dict)
{'instanceOf': 'Derived_A'}

In [5]: b = Base.factory(100, 200, optarg2=True)

In [6]: print(b.dict)
{'instanceOf': 'Derived_B'}

此外,您可以拥有多个工厂方法。查看 here 获取简短教程。

【讨论】:

  • 谢谢卢西亚诺帕兹。我决定走这条路。
【解决方案2】:

您根本不必使用关键字参数;只需在函数的参数部分定义具有默认值的变量,然后只发送要从派生类发送的参数。

请注意,不必提供具有默认值的参数 - 这样您就可以拥有一个具有多个参数的函数(其中参数是唯一的,并且不能被视为列表)。

这是一个部分示例(取自您的代码):

import abc

class Base(metaclass = abc.ABCMeta):
    def __init__(self, reqarg1, reqarg2, optarg1 = 0.123, optarg2 = False):
        self.reqarg1, self.reqarg2 = reqarg1, reqarg2
        self.optarg1, self.optarg2 = optarg1, optarg2
    ...

class Derived_A(Base):
    def __init__(self, regarg1, regarg2, optarg1):
        super().__init__(regarg1, regarg2, optarg1=optarg1)
    ...

class Derived_B(Base):
    def __init__(self, regarg1, regarg2, optarg2):
        super().__init__(regarg1, regarg2, optarg2=optarg2)
    ... 

编辑:随着问题的更新,我只想做一个小说明 - 抽象方法是为了确保一些派生的 Base 对象的混合列表可以调用相同的方法。 Base 对象本身不能调用此方法 - 它对基类是抽象的,因此我们可以确保每个派生实例都必须实现此方法。

【讨论】:

  • 感谢您的建议。我最关心的是抽象方法。我不知道如何干净地处理它。我将改写我的问题以使其更清楚。
  • 嗯,我可以用一个词来说明这一点——是的,你这样做的方式很好。但是 - 您可能想使用 return self.dictdict 只是一个未声明的本地变量)。
  • 我和你思路一致。
猜你喜欢
  • 2020-08-06
  • 2011-07-20
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多