在基础 class 或扩展 class - Python 之间进行选择

Choosing between base class or extended class - Python

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

class BaseClassA:
   

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

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

class ExtendClassA(BaseClassA):

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

以上是我的问题陈述的一个非常简单的例子。实际上我有 10ish classes 其中 extending/creation 发生。我想避免用户必须在扩展 BaseClassB 中重写 func0 以支持他们创建的新 ExtendClassA class。

我正在联系堆栈溢出社区,看看其他人针对此类问题实施了哪些解决方案。我最初的想法是有一个全局字典,'registers' class types/constructors 和 classes 将从全局字典中获取 class 构造函数。当用户想要扩展 class 时,他们会用新的 class.

替换字典中的 class

图书馆代码:

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:

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

我有这样的场景,其中方法调用深埋在库中,这使得很难从用户入口点传递 class -> 函数:

(用户将在下面的示例中调用 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()

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

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()

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

允许他们指定 class 用作 func0

的可选参数
def BaseClassB:
    def func0(self, objclass=BaseClassA):
        self.class_a_obj = objclass()

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

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

顺便说一下,无论它是否有效,我都强烈认为这实际上在几乎所有情况下都是一种不好的做法,因为它以一种模糊的、意想不到的方式改变了 class 行为很难调试。

例如,在下面的 PoC 中,如果您多次继承同一个 BaseClassA - 只有后一次继承应该写在 class 库中,这对于试图理解什么的程序员来说是一个巨大的痛苦地球上正在发生他的代码以及为什么。 但是,当然,在某些用例中,开枪打自己的腿比设计和使用适当的架构更不痛苦 :)

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

#################################
# 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>

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

#################################
# 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>