python - 在多个文件和目录中使用Python OOP时,有助于理解分层继承

标签 python oop inheritance

介绍

我是大学学习编程的学生。我的课程侧重于Java,C#和C ++,因此我对OOP有一定的经验。目前,我正忙于合作社,要求是使用Python和tkinter(我正在学习这两者)来创建GUI。

我的问题专门与Python OOP和跨多个目录中的多个文件的继承有关,我根本无法弄清楚(已经3天了)

如果您不想遍历多行缩短的文件和代码示例,则可以在此处找到实际的代码:

https://github.com/bkleynhans/Landsat-Buoy-Calibration.git

然后转到页面底部以获取问题的要点。

除非您安装了ModTran(这是一种昂贵的许可软件),否则您将无法运行校准系统。但是,GUI应该可以完全正常运行,唯一的非标准要求是tkcalendar。

我做了什么

我已经研究了以下链接的线程以及其他成员所包含的链接。

ImportError: cannot import name

ImportError: Cannot import name X

以及

https://www.digitalocean.com/community/tutorials/understanding-class-inheritance-in-python-3

https://www.python-course.eu/python3_inheritance.php

https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html

不幸的是,无论是因为我不够了解,还是因为我很慢,我似乎都无法将它们与我的问题联系起来。

资料夹结构

我的文件夹结构如下:

Z:.
│
│   tarca_gui
│
└───gui
    │
    │   __init__.py
    │   tarca_gui.py
    │
    │
    └───forms
        │
        │   input_notebook.py
        │   status_frame.py
        │   settings_frame.py
        │   example_date_picker_supplied.py
        │   settings_notebook.py
        │   input_frame.py
        │   help_menu.py
        │   header_frame.py
        │   progress_bar.py
        │   __init__.py
        │   menu_bar.py
        │   example_date_picker.py
        │   output_frame.py
        │   
        └─ 


文件摘要

根目录中的tarca_gui只是一个bash脚本,它会更改为工作目录并执行gui

cd ~/Landsat-Buoy-Calibration/gui
python3 tarca_gui.py


将文件及其代码放在这里会很混乱,但是我将粘贴标题和结构。上面介绍部分中有一个指向该项目的GitHub链接。

必需的非标准库:
tkcalendar-https://pypi.org/project/tkcalendar/

tarca_gui.py-从linux终端启动程序,并使用tkinter构建接口。所有其他文件均基于以下结构派生

基础类

tarca_gui.py

from tkinter import *
from tkinter import messagebox
import inspect
import sys
import os
import pdb

class Tarca_Gui:

    def __init__(self, master):

        # Import gui paths
        from forms import progress_bar
        from forms import menu_bar
        from forms import header_frame
        from forms import input_frame
        from forms import output_frame
        from forms import status_frame

        # Create the root Tkinter object
        master.title('CIS Top Of Atmosphere Radiance Calibration System')
        master.geometry('800x600')
        master.resizable(False, False)
        #master.configure(background = '#FFFFFF')

        master.option_add('*tearOff', False)

        # Create the Progressbar window - accessed via master.progressbar_window.progress_bar
        progress_bar.Progress_Bar(master)

        # Create the Menubar - accessed via master.menu_bar
        menu_bar.Menu_Bar(master)

        # Create the Header - accessed via master.header_frame
        header_frame.Header_Frame(master)

        # Create the Input Frame - accessed via master.
        input_frame.Input_Frame(master)

        # Create the Input Frame - accessed via master.
        output_frame.Output_Frame(master)

        # Create the Input Frame - accessed via master.
        status_frame.Status_Frame(master)



# Calculate fully qualified path to location of program execution
def get_module_path():

    filename = inspect.getfile(inspect.currentframe())
    path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

    return path, filename


# Set environment variables to locate current execution path
def set_path_variables():

    path, filename = get_module_path()

    # find the Calibration program path
    path_index = path.rfind('/')

    # append gui paths
    sys.path.append(path[:path_index])
    sys.path.append(path)
    sys.path.append(path + "/forms")


def on_closing(root):

    if messagebox.askyesno("Quit", "Do you really wish to quit?"):
        root.destroy()


def main():

    set_path_variables()

    root = Tk()
    root.protocol("WM_DELETE_WINDOW", lambda: on_closing(root))
    tarca_gui = Tarca_Gui(root)
    root.mainloop()


if __name__ == "__main__": main()


progress_bar,menu_bar,header_frame,input_frame,output_frame和状态帧均源自(扩展)targa_gui

派生类的摘要

progress_bar.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Progress_Bar(tarca_gui.Tarca_Gui):
    def __init__(self, master):


menu_bar.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui
import help_menu
from gui.forms import settings_frame

class Menu_Bar(tarca_gui.Tarca_Gui):
    def __init__(self, master):


header_frame.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
from forms import input_frame

class Header_Frame(input_frame.Input_Frame):
    def __init__(self, master):


input_frame.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui
from gui.forms import input_notebook

class Input_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):


output_frame.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Output_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):


status_frame.py扩展了tarca_gui.py

from tkinter import *
from tkinter import ttk
import tarca_gui

class Status_Frame(tarca_gui.Tarca_Gui):
    def __init__(self, master):


其余类:help_menu,settings_frame,settings_notebook,header_frame和input_notebook源自(扩展)其他类

header_frame.py扩展了input_frame.py

from tkinter import *
from tkinter import ttk
from forms import input_frame.py
import pdb
class Header_Frame(input_frame.Input_Frame):
    def __init__(self, master):


input_notebook.py扩展了input_frame.py

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from gui.forms import input_frame
from tkcalendar import Calendar, DateEntry
from datetime import date

class Input_Notebook(input_frame.Input_Frame):
    def __init__(self, master):


help_menu.py扩展了menu_bar.py

from tkinter import *
from tkinter import ttk
import time
import threading
import menu_bar

class Help_Menu(menu_bar.Menu_Bar):
    def __init__(self, master):


settings_frame.py扩展了settings_notebook.py

from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from gui.forms import menu_bar
from gui.forms import settings_notebook

class Settings_Frame(menu_bar.Menu_Bar):
    def __init__(self, master):


settings_notebook.py扩展了settings_frame.py

from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from gui.forms import settings_frame

class Settings_Notebook(settings_frame.Settings_Frame):
    def __init__(self, master):


问题与疑问

首先,通过您可以在基类中查看的功能启动gui时,会更新PYTHONPATH。

其次,我了解到,目前这些类并不是全部以相同的方式导入,在寻找问题的过程中它们已经并且已经更改,并且在解决问题后将对其进行标准化。

问题

在尝试将Settings_Frame导入settings_notebook.py之前,整个GUI都可以正常工作

收到错误

% ./tarca_gui
Traceback (most recent call last):
  File "tarca_gui.py", line 104, in <module>
    if __name__ == "__main__": main()
  File "tarca_gui.py", line 100, in main
    tarca_gui = Tarca_Gui(root)
  File "tarca_gui.py", line 31, in __init__
    from forms import menu_bar
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/menu_bar.py", line 20, in <module>
    import help_menu
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/help_menu.py", line 21, in <module>
    import menu_bar
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/menu_bar.py", line 21, in <module>
    from gui.forms import settings_frame
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/settings_frame.py", line 21, in <module>
    from gui.forms import settings_notebook
  File "/cis/otherstu/bxk8027/Landsat-Buoy-Calibration/gui/forms/settings_notebook.py", line 25, in <module>
    class Settings_Notebook(settings_frame.Settings_Frame):
AttributeError: module 'gui.forms.settings_frame' has no attribute 'Settings_Frame'


发现

我相信这是由于循环导入引起的,因为settings_frame导入settings_notebook,反之亦然。

混乱

我了解应尽可能避免使用循环引用,但是,按照我目前了解Python OOP的方式,我需要将settings_notebook导入settings_frame以进行构造,但同时我也需要将settings_frame导入settings_notebook以便对其进行扩展(settings_notebook应该是settings_frame的子级)

问题

我不知道我在做什么错。


它与导入相关,还是我构建类的方式?
如果我对类结构(带有导入项)的理解不正确,请解释我做错了什么,以便我可以学习而不会再犯同样的错误。
任何其他有关的建议将不胜感激。


结论

如果您已经做到了这一点,首先,非常感谢。

如前所述,我在大学里同时学习C ++,C#,Java,Python,HTML,CSS,JavaScript,PHP,SQL,Unity和Unreal引擎(有时我对它们之间的概念感到困惑),因此有关的任何建议我的代码的任何部分将不胜感激。

最佳答案

这段代码确实很难解开,但是我认为问题的根源在于您试图使用继承在类之间共享数据。那不是继承的目的。

当B从A继承时,并不意味着B共享A的数据,这意味着B是A的精确副本,但具有一些增强功能。实际上,您有两个A-原始A和一个具有某些其他功能的新A。

例如,您有一个名为Tarca_Gui的类。这似乎是主要课程。它具有状态栏,菜单栏等。当您从Tarca_Gui继承时,该新类还具有状态栏,菜单栏等。这两个类不共享任何数据。

代替继承,您需要使用组合。区别在于,继承Menu_BarTarca_Gui,但是您真正想要的是Tarca_Gui具有Menu_Bar

因为您没有提供minimal reproducible example,所以我无法修复您的代码,但是您需要做的第一件事是从每个类的定义中删除Tarca_Gui。然后,将Tarca_Gui的实例传递给其他各种对象。

换一种说法:

class Tarca_Gui():
    def __init__(self, master):
        ...
        self.menu_bar = Menu_Bar(self)
        self.header_frame.Header_Frame(self)
        self.input_frame = Input_Frame(self)
        self.output_frame = Output_Frame(self)
        self.status_frame = Status_Frame(self)
        ...

class Menu_Bar():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
class Progress_Bar():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
class Header_Frame():
    def __init__(self, tarca_gui):
        self.tarca_gui = tarca_gui
        ...
... and so on ...


然后,在每个这些对象中,如果它们需要由Tarca_Gui管理的内容,则可以使用self.tarca_gui进行引用。例如,如果您需要访问master小部件,则可以使用self.tarca_gui.master

同样,Menu_BarInput_FrameSettings_Frame都不应继承任何内容。

有关继承与组合的更多信息,请参见Difference between Inheritance and Composition

关于python - 在多个文件和目录中使用Python OOP时,有助于理解分层继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56400317/

相关文章:

Angular/TypeScript 将服务注入(inject)基类而不是子类

python - Django admin.site.register 不添加我的应用程序管理员

python - 我在我的代码 python 中做错了什么,它不会打印总成本?

python - 通过按列值选择 SqlAlchemy 创建字典组

c++ - 在给定场景中访问类的公共(public)方法 - C++

Python super() 引发 TypeError

python - 如何在keras中记录每批处理的val_loss和loss

php - 哎呀,有表演吗?

c# - 何时调用 base.method() 以及 base.method() 中应包含哪些代码?

C++ 模板和继承