問題描述
與通過 IDLE 的 run module f5
命令運行相比,通過命令行運行代碼會引發(fā)錯誤是否有原因?
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?
最近我一直在努力提高我的代碼的可讀性和健壯性.結(jié)果,我一直在嘗試刪除所有 from module import *
行.我曾經(jīng)使用 from tkinter import *
并且我的這行代碼運行良好:
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. I used to use from tkinter import *
and this line of my code worked perfectly fine:
self.path = filedialog.askdirectory()
但現(xiàn)在我已將 from tkinter import *
更改為 import tkinter as tk
并相應(yīng)地更改了代碼:
But now I have changed from tkinter import *
to import tkinter as tk
and I have changed the code accordingly:
self.path = tk.filedialog.askdirectory()
名為 GUI.py 的文件使用以下命令導入此文件:from lib.filesearch import *
(我提到的代碼行位于文件搜索文件中.)
A file called GUI.py imports this file with: from lib.filesearch import *
(the line of code I mentioned resides within the filesearch file.)
我通過 IDLE 運行我的代碼,一切都很好.我的 GUI 仍然有效,并且行 self.path = tk.filedialog.askdirectory()
正常工作,但是,當我通過 windows 命令行運行代碼時,我收到錯誤:
I run my code via IDLE and everything is fine. 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:
AttributeError: 'module' object has no attribute 'filedialog'
以下是我的代碼中的相關(guān)位:
Here are the relevant bits from my code:
來自文件搜索.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
來自 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'
與此不同 問題 我只安裝了一個版本的 python.即 Python34.
Unlike this question I only have one version of python installed. Namely, Python34.
推薦答案
首先我想說:如果你知道你會使用子模塊,總是顯式地導入它們.這個答案的結(jié)尾有一個更令人信服的案例,這很重要.
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.
由于 tkinter
的結(jié)構(gòu),您必須顯式導入子模塊才能加載:
Because of the structure of tkinter
you must explicitly import submodules for them to load:
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
您不需要在 IDLE 中執(zhí)行此操作的原因是,在您的代碼運行之前,IDLE 在后臺設(shè)置了一些東西并最終導入了一些 tkinter 庫.維護者之一 已評論這實際上是 IDLE 中的一個錯誤.
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. One of the maintainers has commented that this is effectively a bug in IDLE.
在 python 3.6.5 中(可能更早,僅檢查此版本)此特定差異已得到修復,因此除了我在下面顯示的 2 個模塊外,其他所有模塊都不會出現(xiàn)此問題.em>
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.
在任何版本中,您都可以看到加載了如下代碼的子模塊列表:
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
在 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
是在您剛剛 import tkinter
時加載的,所以在我測試的版本中,這個問題仍然存在于僅 urllib.parse
和 encodings.ascii
(和 idlelib
模塊,但通常生產(chǎn)代碼不使用它)
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)
這不一定是 IDLE 特定的問題,更糟糕的問題是子模塊是否由您使用的另一個庫加載.以如下代碼為例:
This isn't necessarily an IDLE specific issue though, a worse issue is if the submodule is loaded by another library you use. Take the following code as an example:
>>> import pandas
>>> import http
>>> http.client
<module 'http.client' from '.../http/client.py'>
現(xiàn)在假設(shè)我們編寫了一些仍然使用 http.client
但沒有使用 pandas 的其他代碼:
now lets say we wrote some other code that still used http.client
but didn't use pandas:
>>> import http
>>> http.client
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'http' has no attribute 'client'
這樣,當使用它的代碼加載 http.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.
這將我?guī)Щ氐轿业某跏键c - 始終顯式導入子模塊.
This takes me back to my initial point - always explicitly import submodules.
這篇關(guān)于為什么 tkinter 模塊在通過命令行運行時會引發(fā)屬性錯誤,但在通過 IDLE 運行時不會?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!