简体   繁体   English

为什么 tkinter 模块在通过命令行运行时会引发属性错误,但在通过 IDLE 运行时不会引发属性错误?

[英]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.parseencodings.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.

相关问题 能够在Eclipse中使用Unicode字符串运行Python代码,但在通过命令行或空闲运行时获取UnicodeEncodeError。 - Able to run Python code with Unicode string in Eclipse, but getting UnicodeEncodeError when running via command line or Idle. 当我从 IDLE 运行但不在命令行中运行时,Python 脚本有效吗? - Python script works when I run from IDLE but not in command line? 仅当从命令行运行时模块导入错误 - Module import error only when run from command line 从命令行运行 python 脚本时未找到模块错误 - module not found error when python script is run from command line 在IDLE中运行时,get-pip.py引发ValueError - get-pip.py raises ValueError when run in IDLE 通过IDLE gui运行pytest - Run pytest via IDLE gui 从命令行 python 脚本运行时 tkinter 不工作 - tkinter not working when run from command line python script 脚本导致python通过命令行运行时不会发生的错误 - Script causes errors that don't happen when python run via command line 当测试位于同级文件夹中时,通过命令行运行单个测试 - Run a single test via a command line when tests are located within a sibling folder 尝试通过解释器运行Python脚本时,为什么会出现“ ImportError:未命名模块”的提示? - Why do I get “ImportError: No module named” when I try to run my Python script via the Interpreter?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM