【问题标题】:Choosing between base class or extended class - Python在基类或扩展类之间进行选择 - Python
【发布时间】:2021-12-06 17:13:02
【问题描述】:

我有一个可供其他人使用的 Python 库:

class BaseClassA:
   

class BaseClassB:
    def func0(self):
        this.class_a_obj = BaseClassA()

BaseClassB 创建一个 BaseClassA 对象并存储一个指针。这是一个问题,因为我想允许用户扩展我的库类:

class ExtendClassA(BaseClassA):

而且我的库应该在func0方法中选择扩展类(ExtendClassA)而不是基类(BaseClassA)。

上面是我的问题陈述的一个非常简单的例子。实际上,我有 10 个类进行扩展/创建。我想避免用户不得不在扩展的BaseClassB 中重写func0 以支持他们创建的新ExtendClassA 类。

我正在与堆栈溢出社区联系,以了解其他人针对此类问题实施了哪些解决方案。我最初的想法是拥有一个“注册”类类型/构造函数的全局字典,并且类将从全局字典中获取类构造函数。当用户想要扩展一个类时,他们会将字典中的类替换为新类。

库代码:

global lib_class_dict
lib_class_dict['ClassA'] = BaseClassA()
lib_class_dict['ClassB'] = BaseClassB()

class BaseClassA:
   

class BaseClassB:
    def func0(self):
        this.class_a_obj = lib_class_dict['ClassB']

用户代码:

lib_class_dict['ClassA'] = ExtendClassA():


class ExtendClassA:

编辑:添加有关我正在处理的复杂性的更多详细信息。

我有一些场景,方法调用被深埋在库中,这使得很难从用户入口点传递一个类 -> 函数:

(用户会在下面的例子中调用BaseClassB.func0()

class BaseClassA:
   

class BaseClassB:
    def func0(self):
        this.class_c_obj = BaseClassC()

class BaseClassC:
    def __init__(self):
        this.class_d_obj = BaseClassD()

class BaseClassD:
    def __init__(self):
        this.class_a_obj = BaseClassA()

多个类可以创建一种类型的对象:

class BaseClassA:
   

class BaseClassB:
    def func0(self):
        this.class_a_obj = BaseClassA()

class BaseClassC:
    def __init__(self):
        this.class_a_obj = BaseClassA()

class BaseClassD:
    def __init__(self):
        this.class_a_obj = BaseClassA()

出于这些原因,我希望拥有一个全球或中心位置,所有课程都可以获取正确的课程。

【问题讨论】:

  • 考虑包含实际类的精简版本。一些架构决策可以通过您期望您的类如何交互来决定(这反过来又由它们的功能决定)。名称中缺少该名称,例如ClassA, ClassB
  • 一个来自非专业人士的非常愚蠢的想法:是否可以使用 init_subclass 方法,并以某种方式重新定义一个函数?远射,但也许可以玩...stackoverflow.com/questions/45400284/…
  • 在您的第一个示例中,每个BaseClassB 都有自己的BaseClassA 对象,而在库代码中,它们都共享lib_class_dict 中引用的相同实例。正确的预期行为是什么?

标签: python oop inheritance


【解决方案1】:

所以,我尝试了一个 PoC,如果我理解正确,它可能会成功。看看吧。

顺便说一句,不管它是否有效,我有一种强烈的感觉,这实际上在几乎所有场景中都是一种不好的做法,因为它会以一种难以调试的晦涩、意想不到的方式改变类行为。

例如,在下面的 PoC 中,如果您多次继承同一个 BaseClassA - 只有后者的继承应写入类库中,这对于试图了解他的到底发生了什么的程序员来说将是一个巨大的痛苦代码和原因。 但是,当然,在某些用例中,用腿射击自己比设计和使用适当的架构更痛苦:)

所以,我们有继承的第一个例子(我指定了多个继承的类,只是为了表明只有最后一个继承的类会保存在库中):

#################################
# 1. We define all base classes

class BaseClassA:
    def whoami(self):
        print(type(self))
    def __init_subclass__(cls):
       omfg_that_feels_like_a_reeeeally_bad_practise['ClassA'] = cls
       print('Class Dict Updated:')
       print('New Class A: ' +  str(cls))

#################################
# 2. We define a class library

global omfg_that_feels_like_a_reeeeally_bad_practise
omfg_that_feels_like_a_reeeeally_bad_practise = {}
omfg_that_feels_like_a_reeeeally_bad_practise['ClassA'] = BaseClassA

#################################
# 3. We define a first class that refer our base class (before inheriting from base class)

class UserClassA:
    def __init__(self):
        self.class_a_obj = omfg_that_feels_like_a_reeeeally_bad_practise['ClassA']()

#################################
# 4. We inherit from the base class several times

class FirstExtendedClassA(BaseClassA):
    pass


class SecondExtendedClassA(BaseClassA):
    pass


class SuperExtendedClassA(FirstExtendedClassA):
    pass

#################################
# 5. We define a second class that refer our base class (after inheriting from base class)

class UserClassB:
    def __init__(self):
        self.class_a_obj = omfg_that_feels_like_a_reeeeally_bad_practise['ClassA']()

#################################
## 6. Now we try to refer both user classes

insane_class_test = UserClassA()
print(str(insane_class_test.class_a_obj))
### LOOK - A LAST INHERITED CHILD CLASS OBJECT IS USED!
# <__main__.SuperExtendedClassA object at 0x00000DEADBEEF>

insane_class_test = UserClassB()
print(str(insane_class_test.class_a_obj))
### LOOK - A LAST INHERITED CHILD CLASS OBJECT IS USED!
# <__main__.SuperExtendedClassA object at 0x00000DEADBEEF>

如果我们删除继承,将使用基类:

#################################
# 1. We define all base classes

class BaseClassA:
    def whoami(self):
        print(type(self))
    def __init_subclass__(cls):
       omfg_that_feels_like_a_reeeeally_bad_practise['ClassA'] = cls
       print('Class Dict Updated:')
       print('New Class A: ' +  str(cls))

#################################
# 2. We define a class library

global omfg_that_feels_like_a_reeeeally_bad_practise
omfg_that_feels_like_a_reeeeally_bad_practise = {}
omfg_that_feels_like_a_reeeeally_bad_practise['ClassA'] = BaseClassA

#################################
# 3. We define a first class that refer our base class

class UserClassA:
    def __init__(self):
        self.class_a_obj = omfg_that_feels_like_a_reeeeally_bad_practise['ClassA']()

#################################
# 5. We define a second class that refer our base class

class UserClassB:
    def __init__(self):
        self.class_a_obj = omfg_that_feels_like_a_reeeeally_bad_practise['ClassA']()

#################################
## 6. Now we try to refer both user classes

insane_class_test = UserClassA()
print(str(insane_class_test.class_a_obj))
### LOOK - A DEFAULT CLASS OBJECT IS USED!
# <__main__.BaseClassA object at 0x00000DEADBEEF>

insane_class_test = UserClassB()
print(str(insane_class_test.class_a_obj))
### LOOK - A DEFAULT CLASS OBJECT IS USED!
# <__main__.BaseClassA object at 0x00000DEADBEEF>

【讨论】:

  • 是的,这可能是不好的做法,但我能想出的唯一解决方案之一.. 这是在一个非常受控的环境中,所以即使它并不理想,我认为这是最好的我的解决方案。
  • 很高兴听到这个消息。祝你好运!
【解决方案2】:

允许他们指定要用作func0 的可选参数的类

def BaseClassB:
    def func0(self, objclass=BaseClassA):
        self.class_a_obj = objclass()

obj1 = BlassClassB()
obj1.func0()
obj2 = BassClassB()
obj2.func0(objclass = ExtendClassA)

【讨论】:

  • 虽然这可行,但我认为这对我的特定库来说不合理。有些方法是“私有的”,因此用户没有简单的方法将类传递给某些函数。让我更新显示这些复杂性的描述。
猜你喜欢
  • 2017-02-23
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 2014-08-30
  • 1970-01-01
  • 2014-10-31
  • 2014-08-03
  • 1970-01-01
相关资源
最近更新 更多