Python中的条件类继承

12

我正在尝试在Python中动态创建类,并且对于类和继承相对较新。基本上,我希望我的最终对象具有不同类型的历史记录,以满足不同的需求。我有一个解决方案,但我觉得一定有更好的方法。我想出了这样的东西。

class A:
    def __init__(self):
        self.history={}
    def do_something():
        pass

class B:
    def __init__(self):
        self.history=[]
    def do_something_else():
        pass

class C(A,B):
    def __init__(self, a=False, b=False):
        if a:
            A.__init__(self)
        elif b:
            B.__init__(self)

use1 = C(a=True)
use2 = C(b=True)

3
这里的语义是什么?你想要“条件继承”还是“条件初始化”? - Russia Must Remove Putin
2
如果 use3 = C(a=True, b=True),会发生什么? - erip
2
无论你在这里尝试做什么,要么你解释得很差,要么这是一个可怕的想法。说出你想要实现什么,很可能有更好的方法。 - msw
@msw 目标是动态创建一个类。这个类将从电子表格和其他来源评估数据,但我想要灵活性。例如,该类可能会使用字符串的反向索引来跟踪历史记录,或者它可能只需要成为一个 set([]) 来跟踪各种错误。当我使用参数初始化对象时,我希望最终的类具有这些属性。 - jamesRH
1
我怀疑如果你反转类层次结构,会有更好的运气。也就是说,让C成为一个基类(包含你想要一直使用的任何方法和属性),而AB应该是子类,用于特定情况下的类别化(例如self.history是一个特定类型)。按照你现在的设置,这没有任何意义。 - Blckknght
1
@user3790927 添加过多的灵活性通常意味着您没有很好地定义问题。我们编写代码是为了驯服手头任务的复杂性。请记住“你不会需要它”的原则;如果您忘记了YAGNI,那么倾向于让一个类做所有事情,然后没有人能够使用它,因为您使它变得太复杂了。 - msw
3个回答

18

你可能并不真正需要那个,这很可能是一个 XY 问题,但当你学习一门语言时,这种情况经常发生。你应该知道,通常情况下,你不需要像其他某些语言那样构建庞大的类层次结构。Python 使用“鸭子类型”——如果一个类拥有你想要使用的方法,只需调用它!

此外,在调用 __init__ 时,实例已经存在。此时你不能(容易地)将其替换为不同的实例(尽管任何事情都有可能)。

如果你真的希望能够实例化一个类,并根据传递给构造函数的内容获得本质上是完全不同对象的实例,则简单明了的做法是使用返回不同类的实例的函数。

然而,为了完整起见,你应该知道类可以定义一个__new__ 方法,在 __init__ 之前调用。这个方法可以返回类的实例、完全不同的类的实例或任何其他想要的东西。因此,例如,你可以这样做:

class A(object):
    def __init__(self):
        self.history={}
    def do_something(self):
        print("Class A doing something", self.history)

class B(object):
    def __init__(self):
        self.history=[]
    def do_something_else(self):
        print("Class B doing something", self.history)

class C(object):
    def __new__(cls, a=False, b=False):
        if a:
            return A()
        elif b:
            return B()

use1 = C(a=True)
use2 = C(b=True)
use3 = C()

use1.do_something()
use2.do_something_else()

print (use3 is None)

这适用于Python 2或3。在使用3时,它会返回:

Class A doing something {}
Class B doing something []
True

3
我假设由于某些原因您无法更改A和B,并且需要同时使用它们的功能。
也许您需要两个不同的类:
class CAB(A, B): 
    '''uses A's __init__'''

class CBA(B, A):
    '''uses B's __init__'''

use1 = CAB()
use2 = CBA()

目标是动态创建一个类。
我并不推荐动态创建类。你可以使用一个函数来完成这个任务,这样你就可以轻松地做一些事情,比如pickle实例,因为它们在模块的全局命名空间中可用。
def make_C(a=False, b=False):
    if a:
        return CAB()
    elif b:
        return CBA()

但如果您坚持要“动态创建类”
def make_C(a=False, b=False):
    if a:
        return type('C', (A, B), {})()
    elif b:
        return type('C', (B, A), {})()

两种用法如下:

use1 = make_C(a=True)
use2 = make_C(b=True)

谢谢。我可能在多重继承方面漏掉了什么。我感觉我正在处理的东西会朝着很多不同的方向发展,我不想让它变得难以理解。例如,将会有许多不同类型的历史记录,以及这些历史记录的许多不同类型的结果,我想组装它们,同时还要留出空间进行定制。我应该搜索什么? - jamesRH
1
@user3790927 我不知道你应该搜索什么。我假设由于某种原因,你不能改变A和B,并且你需要两者的功能。你应该选择最能回答你问题的答案(这样你的声望会增加2分),并且再思考一下你想要做什么,并尽量在下一个问题中更好地表达出来,或者当你的声望足够时,可以访问我们的Python聊天室。http://chat.stackoverflow.com/rooms/6/python - Russia Must Remove Putin

0
我在思考同样的事情,并想出了一个帮助方法,用于返回一个从提供的类型继承的类。
这个帮助函数定义并返回了该类,它是从提供的类型继承而来的。
当我正在工作一个“命名值”类时,解决方案呈现出来。我想要一个可以拥有自己名称的值,但它可以像普通变量一样运行。我认为这个想法主要可以用于调试过程。这里是代码:
def getValueClass(thetype):
    """Helper function for getting the `Value` class

    Getting the named value class, based on `thetype`.
    """

    # if thetype not in (int, float, complex):  # if needed
        # raise TypeError("The type is not numeric.")

    class Value(thetype):

        __text_signature__ = "(value, name: str = "")"
        __doc__ = f"A named value of type `{thetype.__name__}`"

        def __init__(self, value, name: str = ""):
            """Value(value, name) -- a named value"""

            self._name = name

        def __new__(cls, value, name: str = ""):

            instance = super().__new__(cls, value)

            return instance

        def __repr__(self):

            return f"{super().__repr__()}"

        def __str__(self):

            return f"{self._name} = {super().__str__()}"

    return Value

一些例子:
IValue = getValueClass(int)
FValue = getValueClass(float)
CValue = getValueClass(complex)

iv = IValue(3, "iv")
print(f"{iv!r}")
print(iv)
print()

fv = FValue(4.5, "fv")
print(f"{fv!r}")
print(fv)
print()

cv = CValue(7 + 11j, "cv")
print(f"{cv!r}")
print(cv)
print()

print(f"{iv + fv + cv = }")

输出:

3
iv = 3

4.5
fv = 4.5

(7+11j)
cv = (7+11j)

iv + fv + cv = (14.5+11j)

工作在IDLE时,变量的行为似乎像内置类型一样,除了打印之外。
>>> vi = IValue(4, "vi")
>>> vi
4
>>> print(vi)
vi = 4
>>> vf = FValue(3.5, 'vf')
>>> vf
3.5
>>> vf + vi
7.5
>>>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接