python - 在跨多个文件和目录使用 Python OOP 时帮助理解层次继承
问题描述
介绍
我是大学学习编程的学生。我的课程侧重于 Java、C# 和 C++,因此我对 OOP 有一定的经验。目前我正忙于我的合作社,要求是使用 Python 和 tkinter 创建一个 GUI(我正在学习这两者)。
我的问题与 Python OOP 和跨多个目录中的多个文件的继承特别相关,我根本想不通(已经 3 天了)
如果您不想浏览多行缩短的文件和代码示例,可以在此处找到实际代码:
https://github.com/bkleynhans/Landsat-Buoy-Calibration.git
然后转到页面底部以获取问题的要旨。
除非您安装了 ModTran,否则您将无法运行校准系统,这是一种昂贵的许可软件。然而,GUI 应该是完全可操作的,唯一的非标准要求是 tkcalendar。
我做了什么
我已经完成了下面链接的线程以及其他成员包含的链接。
也
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 和 status 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。
其次,我知道目前这些类并非都以相同的方式导入,它们曾经并且已经在我努力寻找问题的过程中进行了更改,并且一旦问题得到解决,它们就会被标准化。
问题
整个 GUI 工作正常,直到我尝试将 Settings_Frame 导入 settings_notebook.py
收到错误
% ./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。
例如,您有一个名为Tarca_Gui
. 这似乎是主要课程。它有一个状态栏、一个菜单栏等等。当您从 继承时Tarca_Gui
,该新类还具有状态栏、菜单栏等。这两个类不共享任何数据。
您需要使用组合而不是继承。不同之处在于,继承Menu_Bar
是 a Tarca_Gui
,但您真正想要的是Tarca_Gui
拥有a Menu_Bar
。
我不可能修复您的代码,因为您没有提供最小的可重现示例,但您需要做的第一件事是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_Bar
orInput_Frame
或Settings_Frame
。
有关继承与组合的更多信息,请参阅继承与组合之间的区别
推荐阅读
- reactjs - 如何在codesandbox中添加material-ui/Button
- python - 为什么在出现任何错误后不调用 __del__ 方法?
- javascript - 未捕获的类型错误:firebase.firestore().......set 不是 HTMLButtonElement 的函数。
- r - 是否可以在没有 root 访问权限的情况下在 Linux 上安装 Rstudio 服务器?
- c# - 如何从 JSON 对象返回列表?
- python - 如何在 django/python 中的 2 小时前向客户发送提醒消息
- uwp - BitmapPixelFormat.Rgba8 到 PixelFormat.Format24bppRgb
- c# - Devart.Data.Oracle.OracleConnection 在服务器上运行时无法转换为...
- javascript - 从 quill 中删除 HTML 代码
- ios - Appstore - 如何删除准备提交 tvOS