[英]Why tkinter module raises attribute error when run via command line but not when run via IDLE?
Is there a reason why the code will raise an error when run via the command line compared to when run via IDLE's run module f5
command?与通过 IDLE 的run module f5
命令run module f5
时相比,通过命令行运行时代码会引发错误的原因是什么?
Recently I've been trying to improve the readability and robust-ness of my code.最近我一直在努力提高代码的可读性和健壮性。 As a result I've been trying to remove all the from module import *
lines.因此,我一直在尝试删除所有from module import *
行。 I used to use from tkinter import *
and this line of my code worked perfectly fine:我曾经使用from tkinter import *
并且我的这行代码运行得非常好:
self.path = filedialog.askdirectory()
But now I have changed from tkinter import *
to import tkinter as tk
and I have changed the code accordingly:但是现在我已经from tkinter import *
更改为import tkinter as tk
并且我已经相应地更改了代码:
self.path = tk.filedialog.askdirectory()
A file called GUI.py imports this file with: from lib.filesearch import *
(the line of code I mentioned resides within the filesearch file.)一个名为 GUI.py 的文件使用以下from lib.filesearch import *
此文件: from lib.filesearch import *
(我提到的代码行位于 filesearch 文件中。)
I run my code via IDLE and everything is fine.我通过 IDLE 运行我的代码,一切都很好。 My GUI still works and the line self.path = tk.filedialog.askdirectory()
works like normal however, when I run the code through windows command line I get the error:我的 GUI 仍然有效,并且self.path = tk.filedialog.askdirectory()
行正常工作,但是,当我通过 Windows 命令行运行代码时,出现错误:
AttributeError: 'module' object has no attribute 'filedialog'
Here are the relevant bits from my code:以下是我的代码中的相关部分:
From filesearch.py来自 filesearch.py
import tkinter as tk
def get_path(self):
"""Store user chosen path to search"""
self.paths = tk.filedialog.askdirectory(initialdir = FileSearch.DEFAULT)
return self.paths
From GUI.py来自 GUI.py
from lib.filesearch import *
def Browse(self):
self.BrowseB['state']='disabled'
self.p=self.CrawlObj.get_path()
self.AddText('Searching from Path: ' + str(self.p))
self.BrowseB['state']='normal'
Unlike this question I only have one version of python installed.与这个问题不同,我只安装了一个版本的 python。 Namely, Python34.即,Python34。
I want to start by saying: always explicitly import submodules if you know you will use them.我想首先说:如果你知道你会使用它们,总是显式导入子模块。 The end of this answer has a more compelling case where this is important.这个答案的结尾有一个更引人注目的案例,这很重要。
Because of the structure of tkinter
you must explicitly import submodules for them to load:由于tkinter
的结构,您必须显式导入子模块才能加载:
import tkinter as tk
print(hasattr(tk,"filedialog")) # in a standard interpreter will print false
import tkinter.filedialog
print(hasattr(tk,"filedialog")) # should always print true after explicit import
the reason you don't need to do this in IDLE is that before your code is run IDLE sets up some stuff in the background and ends up importing some of the tkinter libraries.您不需要在 IDLE 中执行此操作的原因是,在您的代码运行之前,IDLE 在后台设置了一些内容并最终导入了一些 tkinter 库。 One of the maintainers has commented that this is effectively a bug in IDLE.一位维护者评论说,这实际上是 IDLE 中的一个错误。
In python 3.6.5 (and possibly earlier, only checked this version) this specific discrepancy has been fixed so it no longer happens for all but 2 modules I show below.在 python 3.6.5 中(可能更早,只检查了这个版本)这个特定的差异已经被修复,所以除了我在下面展示的 2 个模块之外,它不再发生在所有模块中。
in any version you can see a list of submodules that are loaded with some code like this:在任何版本中,您都可以看到加载了一些代码的子模块列表,如下所示:
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 03:03:55)
# standard interpreter
>>> import sys
>>> len(sys.modules) #total number of modules automatically loaded
71
>>> sorted(name for name in sys.modules.keys() if ("." in name)) #submodules loaded
['collections.abc', 'encodings.aliases', 'encodings.latin_1', 'encodings.utf_8', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib.abc', 'importlib.machinery', 'importlib.util', 'os.path']
>>> len(_) #number of submodules
10
And in IDLE:在空闲状态下:
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 03:03:55)
# IDLE
>>> import sys
>>> len(sys.modules)
152
>>> sorted(name for name in sys.modules.keys() if ("." in name and "idlelib" not in name))
['collections.abc', 'encodings.aliases', 'encodings.ascii', 'encodings.latin_1', 'encodings.utf_8', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib.abc', 'importlib.machinery', 'importlib.util', 'os.path', 'tkinter.constants', 'urllib.parse']
>>> len(_) #number of submodules not directly related to idlelib.
13
tkinter.constants
is loaded when you just import tkinter
so as of the version I tested, this issue still exists for only urllib.parse
and encodings.ascii
(and idlelib
modules but generally production code doesn't use that)当您只import tkinter
tkinter.constants
会加载tkinter.constants
因此在我测试的版本中,此问题仍然仅存在于urllib.parse
和encodings.ascii
(和idlelib
模块,但通常生产代码不使用它)
This isn't necessarily an IDLE specific issue though, a worse issue is if the submodule is loaded by another library you use.不过,这不一定是 IDLE 特定的问题,更糟糕的问题是子模块是否由您使用的另一个库加载。 Take the following code as an example:以下面的代码为例:
>>> import pandas
>>> import http
>>> http.client
<module 'http.client' from '.../http/client.py'>
now lets say we wrote some other code that still used http.client
but didn't use pandas:现在假设我们编写了一些其他代码,仍然使用http.client
但没有使用 Pandas:
>>> import http
>>> http.client
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'http' has no attribute 'client'
This way you could end up with a submodule that works properly when the code that uses it loads http.client
possibly by using a library that happens to use it but will otherwise fail.这样,当使用它的代码加载http.client
时,您可能会得到一个正常工作的子模块,这可能是通过使用碰巧使用它但会失败的库。
This takes me back to my initial point - always explicitly import submodules.这让我回到了最初的观点——总是显式地导入子模块。
Actually it's true that the module has no attribute filedialog
, it's a submodule and you should import it as import tkinter.filedialog
before using it.实际上,该模块确实没有属性filedialog
,它是一个子模块,您应该在使用之前将其导入为import tkinter.filedialog
。 You can use tk.filedialog
without explicitly importing filedialog
in IDLE because it's already imported.您可以使用tk.filedialog
而无需在 IDLE 中显式导入filedialog
,因为它已经导入。
import sys
sys.modules['tkinter.filedialog']
The above code will raise a KeyError
in a standard python interpreter but it will return something like <module 'tkinter.filedialog' from '/usr/lib/python3.5/tkinter/filedialog.py'>
in IDLE.上面的代码将引发KeyError
在标准python解释但它会返回类似<module 'tkinter.filedialog' from '/usr/lib/python3.5/tkinter/filedialog.py'>
在IDLE。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.