【问题标题】:Python - Child Class to call a function from another Child ClassPython - 子类从另一个子类调用函数
【发布时间】:2020-09-13 10:02:47
【问题描述】:

我有一个相当大的班级,我想将其分解为较小的班级,每个班级处理整体的一个部分。所以每个孩子只关心整体的一个方面。

这些子类中的每一个仍然需要相互通信。 例如,数据访问创建了一个绘图控制器需要访问的字典。 然后绘图控制器需要更新主 GUI 控制器上的内容。但是这些孩子有更多的相互交流的功能。

我如何做到这一点?

我读过MetaclassesCooperative Multiple InheritenceWonders of Cooperative Multiple Inheritence,但我不知道该怎么做。

我最接近的是以下代码:

class A:
    def __init__(self):
        self.myself = 'ClassA'

    def method_ONE_from_class_A(self, caller):
        print(f"I am method ONE from {self.myself} called by {caller}")
        self.method_ONE_from_class_B(self.myself)

    def method_TWO_from_class_A(self, caller):
        print(f"I am method TWO from {self.myself} called by {caller}")
        self.method_TWO_from_class_B(self.myself)


class B:
    def __init__(self):
        self.me = 'ClassB'

    def method_ONE_from_class_B(self, caller):
        print(f"I am method ONE from {self.me} called by {caller}")
        self.method_TWO_from_class_A(self.me)

    def method_TWO_from_class_B(self, caller):
        print(f"I am method TWO from {self.me} called by {caller}")


class C(A, B):

    def __init__(self):
        A.__init__(self)
        B.__init__(self)

    def children_start_talking(self):
        self.method_ONE_from_class_A('Big Poppa')


poppa = C()
poppa.children_start_talking()

正确的结果是:

I am method ONE from ClassA called by Big Poppa
I am method ONE from ClassB called by ClassA
I am method TWO from ClassA called by ClassB
I am method TWO from ClassB called by ClassA

但是……即使 Class B 和 Class A 正确地调用了其他子函数,它们实际上并没有找到它们的声明。我在输入代码时也没有“看到”它们,这既令人沮丧又担心我可能做错了什么。

有没有好的方法来实现这一点?或者这实际上是个坏主意?

编辑:Python 3.7 如果有任何区别。

【问题讨论】:

  • 找到他们的声明是什么意思?我不明白你的解决方案有什么问题。
  • 表示在Class B中我写self.除了Class B,没有显示其他任何类的功能。在第二张图中可以清楚地看到它显示该函数的“未解析的属性引用”,该函数存在于类 A 中。这是一张图像,显示它无法“看到”其他子类的方法。 i.imgur.com/YkmFb92.png 甚至认为它存在于那里,并且正确地调用它。
  • 为什么你认为 self 应该显示其他类方法?您是打算继承这些类,还是应该包含彼此的实例?
  • 第一个模式是关于协作对象的高级建模。美好的。但是你想把什么遗产放在那里?请记住,继承是一个 is a 关系,看起来你在这里使用了错误的工具。

标签: python class design-patterns multiple-inheritance


【解决方案1】:

继承

当打破这样的类层次结构时,单独的“部分”类,我们称之为“mixins”,将“看到”直接在它们及其基类上声明的内容。在您的示例中,在编写 A 类时,它对 B 类一无所知 - 作为作者,您可以知道 B 类的方法将存在,因为 A 类的方法只会从 C 类调用,它继承了.

您的编程工具(包括 IDE)不知道这一点。 (你应该比你的编程助手更了解,这是一条支线)。如果运行它会起作用,但这是一个糟糕的设计。

如果所有方法都直接存在于最终类的单个实例上,则所有方法都必须“存在”在一个超类中——你甚至可以在不同的文件中编写独立的子类,然后将继承所有这些的单个子类:

from abc import abstractmethod,  ABC

class Base(ABC):

    @abstractmethod
    def method_A_1(self):
        pass

    @abstractmethod
    def method_A_2(self):
        pass

    @abstractmethod
    def method_B_1(self):
        pass

class A(Base):
    def __init__(self, *args, **kwargs):
        # pop consumed named parameters from "kwargs"
        ...
        super().__init__(*args, **kwargs)
        # This call ensures all __init__ in bases are called
        # because Python linearize the base classes on multiple inheritance

    def method_A_1(self):
        ...

    def method_A_2(self):
        ...


class B(Base):
    def __init__(self, *args, **kwargs):
        # pop consumed named parameters from "kwargs"
        ...
        super().__init__(*args, **kwargs)
        # This call ensures all __init__ in bases are called
        # because Python linearize the base classes on multiple inheritance

    def method_B_1(self):
        ...

    ...


class C(A, B):
    pass

(“ABC”和“abstractmethod”有点糖——它们会起作用,但这种设计没有这些也能起作用——认为它们的存在有助于任何正在查看你的代码的人弄清楚发生了什么,如果您错误地创建了不完整基类之一的实例,则会引发早期的运行时错误)

复合

这可行,但如果您的方法实际上适用于截然不同的域,则改为 对于多重继承,你应该尝试使用"composite design pattern"

如果不是自然产生的,则不需要多重继承。

在这种情况下,您在 shell 类的 __init__ 上实例化驱动不同域的类的对象,并将其自己的实例传递给这些子级,这些子级将保留对它的引用(在 self.parent属性,例如)。很有可能您的 IDE 仍然不知道您在说什么,但您的设计会更合理。


class Parent:
    def __init__(self):
        self.a_domain = A(self)
        self.b_domain = B(self)

class A:
    def __init__(self, parent):
        self.parent = parent
        # no need to call any "super...init", this is called
        # as part of the initialization of the parent class

    def method_A_1(self):
        ...

    def method_A_2(self):
        ...


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

    def method_B_1(self):
        # need result from 'A' domain:
        a_value = self.parent.a_domain.method_A_1()
        ...


此示例使用基本的语言功能,但如果您决定 要在复杂的应用程序中使用它,您可以对其进行复杂化 - 有 接口模式,可以让你交换使用的类 对于不同的领域,在专门的子类中,等等。但通常 上面的模式就是你需要的。

【讨论】:

  • 非常感谢您如此详细的回复。我理解的继承部分。基本上我缺少的是基类,它包含可能被其他孩子调用的所有子方法。复合部分可能是我想到的,但我不知道它是如何调用的。我确实使用过 parent ,但这似乎是个坏主意,因为调用其他子函数需要 parent.child_a.function() ,这在我看来就像意大利面条代码。明天我可能会同时尝试这两种方法,看看哪一种感觉更清醒,并且将来不会让任何人感到困惑。
  • parent.child_a.method_a 还不错 - 它比在同一个命名空间中拥有数百个不相关的方法要好。 “适配器”模式可以缓解这种情况 - 本质上,您可以通过执行 adapt(self, "domain_a", method_a)() 来创建一个获取和调用 self.parent.child_a.method_a() 的函数
  • 但是请注意,使用和适配器,您可能需要输入更少的内容,可能有语义上更好看的代码,但您的 IDE 知道您在说什么的机会将变为零。跨度>
  • 是的,似乎同时拥有一个干净的解决方案并使其不那么冗长几乎是不可能的。顺便说一句,我尝试了继承部分,它表明 abstractmethod 在 Python 3.3 中已被弃用,它建议使用 classmethod 代替。这似乎也可以在没有 ABC 进口的情况下工作。无论如何,再次感谢您的帮助。明天试试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-26
  • 1970-01-01
  • 2019-07-25
  • 2020-04-16
  • 1970-01-01
相关资源
最近更新 更多