加载 py 文件并在不知道存在多少文件的情况下从目录的 for 循环中对 class 执行某些操作?
Load py files and do something with the class in a for loop from directory without knowing how many files exist?
我正在尝试使用一个目录来保存我不断增长的框架列表,当我的 MAIN.py
文件被执行时,我希望代码将每个框架加载到 Tkinter 笔记本小部件中。
我可以通过从我的 MAIN 文件夹的子目录中导入每个文件来手动执行此操作,但是我正在尝试以实用的方式加载它们,而不必知道每个文件名,也不必自己导入它们。
我取得了一些进展,但我在实际加载文件中的 class 时卡住了。我可以获得所有文件的列表,我相信我正在导入它们,我只是无法加载它们,因为它一直告诉我模块不存在。
我一定是误解了什么,因为我不知道如何从导入的文件中正确调用 class。
错误:
C:\Users\USER\PycharmProjects\TEST\venv\Scripts\python.exe C:/Users/USER/PycharmProjects/TEST/MAIN/MAIN.py
# This below list is a result of my print statement to see if I got all the file names.
['TaskFrame', 'TestFrame1', 'TestFrame2', 'TestFrame3']
Traceback (most recent call last):
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 46, in <module>
ScriptManager().mainloop()
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 39, in __init__
self.add_frame_to_book(foo.tab_title)
AttributeError: module 'ScriptFrames.TaskFrame' has no attribute 'tab_title'
主要代码:
import tkinter as tk
import tkinter.ttk as ttk
from os.path import dirname, basename, isfile, join
import glob
import importlib.util
modules = glob.glob(join(dirname('.\ScriptFrames\'), "*.py"))
__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
print(__all__)
class ScriptManager(tk.Tk):
def __init__(self):
super().__init__()
self.book = ttk.Notebook(self)
self.book.grid(row=0, column=0, sticky='nsew')
for fn in __all__:
spec = importlib.util.spec_from_file_location('ScriptFrames.{}'.format(fn),
'.\ScriptFrames\{}.py'.format(fn))
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
# Here is my problem I do not know if I am attempting to get the class
# attribute correctly here. I am sure that I am calling this wrong.
self.add_frame_to_book(foo.tab_title)
def add_frame_to_book(self, fname):
self.book.add(fname, text='test')
if __name__ == '__main__':
ScriptManager().mainloop()
每个测试文件都是一个简单的 tkinter 框架。
测试帧代码:
import tkinter as tk
import tkinter.ttk as ttk
class TabFrame(ttk.Frame):
def __init__(self):
super().__init__()
self.tab_title = 'Test Frame 1'
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
tk.Label(self, text='test').grid(row=0, column=0, sticky='nsew')
如果对我有帮助,这是我的文件结构:
我就是这样做的。
def load(self, file=None):
file_type = path.splitext(file)[1].lstrip('.').lower()
if file_type == 'py' and path.exists(file):
spec = spec_from_file_location("module.name", file)
module = module_from_spec(spec)
spec.loader.exec_module(module)
self.nodes = module.config
您正在尝试访问模块的 tab_title,而不是从 (Frame) class里面。
加载模块后,使用 [Python 3.Docs]: Built-in Functions:
获取其属性
- 获取模块属性名称(使用dir)
- 对于每个名字,获取对应的属性(使用getattr)
- 测试属性是否为
ttk.Frame
(因为模块还有其他属性)
- 如果是帧class,实例化它以便访问tab_title,否则就丢弃它
示例:
# ...
# The rest of your code
spec.loader.exec_module(foo)
# At this point, 'foo' module is loaded. Get its attributes
module_attrs = [getattr(foo, attr_name, None) for attr_name in dir(foo)]
# Filter out the attributes only keeping the ones that we care about
frame_classes = [attr for attr in module_attrs if isinstance(attr, (type,)) and issubclass(attr, (ttk.Frame,))]
# Then handle the module classes (it will probably be only one)
for frame_class in frame_classes:
if frame_class.__name__.startswith("Tab"): # Or any other filtering
# It would have been much more efficient to filter names when computing attr_names, but it's more consistent to be here (especially if other filtering beside name is needed)
frame_class_instance = frame_class() # Instantiate the class, as tab_title is an instance attribute
print(frame_class.__name__, frame_class_instance.tab_title)
我正在尝试使用一个目录来保存我不断增长的框架列表,当我的 MAIN.py
文件被执行时,我希望代码将每个框架加载到 Tkinter 笔记本小部件中。
我可以通过从我的 MAIN 文件夹的子目录中导入每个文件来手动执行此操作,但是我正在尝试以实用的方式加载它们,而不必知道每个文件名,也不必自己导入它们。
我取得了一些进展,但我在实际加载文件中的 class 时卡住了。我可以获得所有文件的列表,我相信我正在导入它们,我只是无法加载它们,因为它一直告诉我模块不存在。
我一定是误解了什么,因为我不知道如何从导入的文件中正确调用 class。
错误:
C:\Users\USER\PycharmProjects\TEST\venv\Scripts\python.exe C:/Users/USER/PycharmProjects/TEST/MAIN/MAIN.py
# This below list is a result of my print statement to see if I got all the file names.
['TaskFrame', 'TestFrame1', 'TestFrame2', 'TestFrame3']
Traceback (most recent call last):
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 46, in <module>
ScriptManager().mainloop()
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 39, in __init__
self.add_frame_to_book(foo.tab_title)
AttributeError: module 'ScriptFrames.TaskFrame' has no attribute 'tab_title'
主要代码:
import tkinter as tk
import tkinter.ttk as ttk
from os.path import dirname, basename, isfile, join
import glob
import importlib.util
modules = glob.glob(join(dirname('.\ScriptFrames\'), "*.py"))
__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
print(__all__)
class ScriptManager(tk.Tk):
def __init__(self):
super().__init__()
self.book = ttk.Notebook(self)
self.book.grid(row=0, column=0, sticky='nsew')
for fn in __all__:
spec = importlib.util.spec_from_file_location('ScriptFrames.{}'.format(fn),
'.\ScriptFrames\{}.py'.format(fn))
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
# Here is my problem I do not know if I am attempting to get the class
# attribute correctly here. I am sure that I am calling this wrong.
self.add_frame_to_book(foo.tab_title)
def add_frame_to_book(self, fname):
self.book.add(fname, text='test')
if __name__ == '__main__':
ScriptManager().mainloop()
每个测试文件都是一个简单的 tkinter 框架。
测试帧代码:
import tkinter as tk
import tkinter.ttk as ttk
class TabFrame(ttk.Frame):
def __init__(self):
super().__init__()
self.tab_title = 'Test Frame 1'
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
tk.Label(self, text='test').grid(row=0, column=0, sticky='nsew')
如果对我有帮助,这是我的文件结构:
我就是这样做的。
def load(self, file=None):
file_type = path.splitext(file)[1].lstrip('.').lower()
if file_type == 'py' and path.exists(file):
spec = spec_from_file_location("module.name", file)
module = module_from_spec(spec)
spec.loader.exec_module(module)
self.nodes = module.config
您正在尝试访问模块的 tab_title,而不是从 (Frame) class里面。
加载模块后,使用 [Python 3.Docs]: Built-in Functions:
获取其属性- 获取模块属性名称(使用dir)
- 对于每个名字,获取对应的属性(使用getattr)
- 测试属性是否为
ttk.Frame
(因为模块还有其他属性)- 如果是帧class,实例化它以便访问tab_title,否则就丢弃它
示例:
# ...
# The rest of your code
spec.loader.exec_module(foo)
# At this point, 'foo' module is loaded. Get its attributes
module_attrs = [getattr(foo, attr_name, None) for attr_name in dir(foo)]
# Filter out the attributes only keeping the ones that we care about
frame_classes = [attr for attr in module_attrs if isinstance(attr, (type,)) and issubclass(attr, (ttk.Frame,))]
# Then handle the module classes (it will probably be only one)
for frame_class in frame_classes:
if frame_class.__name__.startswith("Tab"): # Or any other filtering
# It would have been much more efficient to filter names when computing attr_names, but it's more consistent to be here (especially if other filtering beside name is needed)
frame_class_instance = frame_class() # Instantiate the class, as tab_title is an instance attribute
print(frame_class.__name__, frame_class_instance.tab_title)