Python 3.x 此Python代码中多重继承的最佳实践

Python 3.x 此Python代码中多重继承的最佳实践,python-3.x,tkinter,multiple-inheritance,ttk,method-resolution-order,Python 3.x,Tkinter,Multiple Inheritance,Ttk,Method Resolution Order,我对某些Python类中的多重继承设计有一些疑问 问题是我想扩展ttk按钮。这是我最初的建议(除了init方法之外,我省略了缩短方法中的所有源代码): 但后来我意识到我还需要这个ImgButton的一个子类,如下所示: import tkinter as tk import tkinter.ttk as ttk class MyButton(ImgButton): """ ImgButton with specifical purpose """ IMG_NA

我对某些Python类中的多重继承设计有一些疑问

问题是我想扩展ttk按钮。这是我最初的建议(除了init方法之外,我省略了缩短方法中的所有源代码):

但后来我意识到我还需要这个ImgButton的一个子类,如下所示:

import tkinter as tk
import tkinter.ttk as ttk

class MyButton(ImgButton):
    """
    ImgButton with specifical purpose
    """

    IMG_NAME = 'filename{}.jpg'
    IMAGES_DIR = os.path.sep + os.path.sep.join(['home', 'user', 'myProjects', 'myProject', 'resources', 'images'])
    UNKNOWN_IMG = os.path.sep.join([IMAGES_DIR, IMG_NAME.format(0)])
    IMAGES = (lambda IMAGES_DIR=IMAGES_DIR, IMG_NAME=IMG_NAME: [os.path.sep.join([IMAGES_DIR, IMG_NAME.format(face)]) for face in [1,2,3,4,5] ])()

    def change_image(self, __=None):
        """
        Changes randomly the image in this MyButton
        :param __: the event, which is no needed
        """
        pass

    def __init__(self, master=None, value=None, **kw):
        # Default image when hidden or without value

        current_img = PhotoImage(file=MyButton.UNKNOWN_IMG)
        super().__init__(master, image=current_img, **kw)
        if not value:
            pass
        elif not isinstance(value, (int, Die)):
            pass
        elif isinstance(value, MyValue):
            self.myValue = value
        elif isinstance(value, int):
            self.myValue = MyValue(value)
        else:
            raise ValueError()
        self.set_background_color('green')
        self.bind('<Button-1>', self.change_image, add=True)


    def select(self):
        """
        Highlights this button as selected and changes its internal state
        """
        pass

    def toggleImage(self):
        """
        Changes the image in this specific button for the next allowed for MyButton
        """
        pass
将所有有助于小部件颜色的方法放入其中,然后我需要ImgButton从MyWidget和ttk继承。按钮:

class ImgButton(ttk.Button, MyWidget):  ???

or

class ImgButton(MyWidget, ttk.Button):  ???
编辑:我也希望我的对象可以记录,所以我做了这个类:

class Loggable(object):
    def __init__(self) -> None:
        super().__init__()
        self.__logger = None
        self.__logger = self.get_logger()

        self.debug = self.get_logger().debug
        self.error = self.get_logger().error
        self.critical = self.get_logger().critical
        self.info = self.get_logger().info
        self.warn = self.get_logger().warning


    def get_logger(self):
        if not self.__logger:
            self.__logger = logging.getLogger(self.get_class())
        return self.__logger

    def get_class(self):
        return self.__class__.__name__
所以现在:

class ImgButton(Loggable, ttk.Button, MyWidget):  ???

or

class ImgButton(Loggable, MyWidget, ttk.Button):  ???

or

class ImgButton(MyWidget, Loggable, ttk.Button):  ???

# ... this could go on ...
我来自Java,不知道多重继承的最佳实践。我不知道应该如何按最佳顺序对父代进行排序,也不知道其他对设计多重继承有用的东西

我搜索了这个主题,找到了很多解释MRO的资源,但没有找到关于如何正确设计多重继承的内容。我不知道我的设计是不是做错了,但我觉得感觉很自然

如果您能提供一些建议,以及关于这个主题的一些链接或资源,我将不胜感激


非常感谢。

原则上,多重继承的使用增加了复杂性,所以除非我确定它的需要,否则我会避免它。从您的帖子中,您已经了解了super()和MRO的用法

一个常见的建议是在可能的情况下使用组合而不是多重继承

另一种方法是仅从一个可实例化父类生成子类,使用抽象类作为其他父类。也就是说,他们将方法添加到这个子类中,但从未自己实例化过。就像在Java中使用接口一样。这些抽象类也称为mixin,但它们的使用(或滥用)也有争议。看


至于你的tkinter代码,除了日志代码缩进,我看不出有什么问题。也许小部件可以有一个记录器,而不是从中继承。我认为tkinter的危险在于它错误地覆盖了数百种可用方法中的一种。

这些天我一直在阅读有关多重继承的文章,我学到了很多东西。最后,我将我的资料来源、资源和参考资料链接起来

我的主要和最详细的资料来源是《流畅的python》,我发现这本书可以在网上免费阅读

这描述了方法解析顺序和多重继承的设计场景,以及确定的步骤:

  • 识别并分离接口的代码。定义方法的类,但不一定与实现一起定义(这些类应该被重写)。这些通常是ABC(抽象基类)。它们为创建“IS-a”关系的子类定义类型

  • 识别并分离混音器的代码。mixin是一个类,它应该在子类中使用一组相关的新方法实现,但不定义适当的类型。根据这一定义,ABC可以是一种混音,但不能反过来。mixin既不定义接口,也不定义类型

  • 当开始使用ABC或类和混合继承时,您应该只继承一个具体的超类,以及多个ABC或混合继承:

  • 例如:

    class MyClass(MySuperClass, MyABC, MyMixin1, MyMixin2):
    
     class Widget(BaseWidget, Pack, Grid, Place):
         pass
    
    就我而言:

    class ImgButton(ttk.Button, MyWidget):
    
  • 如果某些类的组合特别有用或频繁,则应在具有描述性名称的类定义下将它们联接起来。
  • 例如:

    class MyClass(MySuperClass, MyABC, MyMixin1, MyMixin2):
    
     class Widget(BaseWidget, Pack, Grid, Place):
         pass
    
    我认为Loggable应该是一个Mixin,因为它为一个功能收集了方便的实现,但没有定义真正的类型。因此:

    class MyWidget(ttk.Widget, Loggable): # May be renamed to LoggableMixin
    
  • 支持对象组合而不是继承:如果您可以通过将类保存在属性中而不是扩展或继承来使用它,那么您应该避免继承


  • 我已经想到了tkinter同样的危险,但是对于制作一个新的视觉控件来说,扩展一个现有控件感觉很自然。我知道mixin,我没有理由不使用它们。说句心里话,我想现在我应该使用继承,让我的Widget子代具有Loggable和ttk.Widget父代。ImgButton将是具有父母MyWidget和ttk.Button的子项。每个级别有两个父级,但再往下就不需要多重继承了。