Python 中基于参数的条件继承

问题描述 投票:0回答:3

作为 OOP 新手,我想知道是否有任何方法可以根据子类在 Python 中的调用方式继承多个类之一。我尝试这样做的原因是因为我有多个同名的方法,但在三个具有不同功能的父类中。对象创建时需要根据一定的条件继承相应的类。

例如,我尝试根据实例化时是否传递任何参数来让C类继承A或B,但没有成功。谁能建议更好的方法来做到这一点?

class A:
    def __init__(self,a):
        self.num = a

    def print_output(self):
        print('Class A is the parent class, the number is 7',self.num)

class B:
    def __init__(self):
        self.digits=[]

    def print_output(self):
        print('Class B is the parent class, no number given')

class C(A if kwargs else B):
    def __init__(self,**kwargs):
        if kwargs:
            super().__init__(kwargs['a'])
        else:
            super().__init__()

temp1 = C(a=7)
temp2 = C()

temp1.print_output()
temp2.print_output()

所需的输出将是“A类是父类,编号是7”,后面是“B类是父类,没有给出编号”。

谢谢!

python oop inheritance
3个回答
10
投票

无论您是刚刚开始接触 OOP 还是已经使用了一段时间,我都建议您阅读一本关于设计模式的好书。经典是 Gamma 的 Design Patterns。舵。约翰逊和弗利赛德斯。

除了使用继承之外,您还可以使用 compositiondelegation。例如:

class A:
    def do_something(self):
        # some implementation

class B:
    def do_something(self):
        # some implementation

class C:
    def __init__(self, use_A):
        # assign an instance of A or B depending on whether argument use_A is True
        self.instance = A() if use_A else B()

    def do_something(self):
        # delegate to A or B instance:
        self.instance.do_something()

更新

为了回应 Lev Barenboim 的评论,下面演示了如何使具有委托的组合看起来更像常规继承,因此,如果类

C
已经分配了类
A
的实例,例如,
self.instance
,那么
A
的属性(例如
x
)可以在内部访问为
self.x
以及
self.instance.x
(假设类
C
本身没有定义属性
x
),同样,如果您创建名为
C
c
实例,则可以将该
attribute
称为
 c.x
仿佛上课
C
继承自类
A

这样做的基础在于内置方法

__getattr__
__getattribute__
__getattr__
可以在类上定义,并且每当引用但未定义属性时都会调用。
__getattribute__
可以在对象上调用以按名称检索属性。

请注意,在下面的示例中,类

C
甚至不再需要定义方法
do_something
(如果它所做的只是委托给
self.instance
):

class A:
    def __init__(self, x):
        self.x = x

    def do_something(self):
        print('I am A')

class B:
    def __init__(self, x):
        self.x = x

    def do_something(self):
        print('I am B')

class C:
    def __init__(self, use_A, x):
        # assign an instance of A or B depending on whether argument use_A is True
        self.instance = A(x) if use_A else B(x)

    # called when an attribute is not found:
    def __getattr__(self, name):
        # assume it is implemented by self.instance
        return self.instance.__getattribute__(name)

    # something unique to class C:
    def foo(self):
        print ('foo called: x =', self.x)

c = C(True, 7)
print(c.x)
c.foo()
c.do_something()
# This will throw an Exception:
print(c.y)

打印:

7
foo called: x = 7
I am A
Traceback (most recent call last):
  File "C:\Ron\test\test.py", line 34, in <module>
    print(c.y)
  File "C:\Ron\test\test.py", line 23, in __getattr__
    return self.instance.__getattribute__(name)
AttributeError: 'A' object has no attribute 'y'

0
投票

我认为您不能从类内部将值传递给类的条件。

相反,您可以像这样定义工厂方法:

class A:
    def sayClass(self):
        print("Class A")
class B:
    def sayClass(self):
        print("Class B")


def make_C_from_A_or_B(make_A):
    class C(A if make_A else B):
        def sayClass(self):
            super().sayClass()
            print("Class C")
    return C()

make_C_from_A_or_B(True).sayClass()

哪个输出:

Class A
Class C

注意:您可以通过我在这篇文章(关于解析器工厂)

中发现足够好的示例来找到有关工厂模式的信息

0
投票

我想我会使用@Booboo的答案的变体,如下:

  1. 为实用函数定义一个单独的“公共”类。
  2. 普通类的子类 A 和 B。
  3. 创建一个工厂函数 C,根据条件返回 A 或 B。

例如这段代码:

# define a class of common utility functions for A and B
class CommonUtils:
    common_int: int = 123

    def common_util(self):
        print("This is a common utility function for A and B.")


# subclass A from CommonUtils
class A(CommonUtils):
    kwargs: dict = {}

    def __init__(self, **kwargs):
        super().__init__()
        self.kwargs = kwargs

    def print_output(self):
        print(f"Class A, {self.kwargs=}")


# subclass B from CommonUtils
class B(CommonUtils):
    def __init__(self):
        super().__init__()
        self.kwargs = None

    def print_output(self):
        print("Class B, no kwargs")


# define a factory function C that returns either A or B
def C(**kwargs) -> A | B:
    if kwargs:
        return A(**kwargs)
    else:
        return B()


# if arguments are passed, C() returns class A
temp1 = C(a=7)
temp1.print_output()
temp1.common_util()
print(temp1.common_int)

# if no arguments are passed, C() returns class B
temp2 = C()
temp2.print_output()
temp2.common_util()
print(temp2.common_int)

返回此输出:

Class A, self.kwargs={'a': 7}
This is a common utility function for A and B.
123

Class B, no kwargs
This is a common utility function for A and B.
123
© www.soinside.com 2019 - 2024. All rights reserved.