为什么 tkinter 模块在通过命令行运行时会引发属性错误,但在通过 IDLE 运行时不会引发属性错误? [英] Why tkinter module raises attribute error when run via command line but not when run via IDLE?

查看:31
本文介绍了为什么 tkinter 模块在通过命令行运行时会引发属性错误,但在通过 IDLE 运行时不会引发属性错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

与通过 IDLE 的 run module f5 命令运行相比,通过命令行运行时代码会引发错误的原因是什么?

最近我一直在努力提高代码的可读性和健壮性.因此,我一直在尝试删除所有 from module import * 行.我曾经使用 from tkinter import * 并且我的这行代码运行得非常好:

self.path = filedialog.askdirectory()

但是现在我已经将 从 tkinter import * 更改为 import tkinter as tk 并且我已经相应地更改了代码:

self.path = tk.filedialog.askdirectory()

名为 GUI.py 的文件使用以下命令导入此文件:from lib.filesearch import *(我提到的代码行位于 filesearch 文件中.)

我通过 IDLE 运行我的代码,一切都很好.我的 GUI 仍然有效,并且 self.path = tk.filedialog.askdirectory() 行正常工作,但是,当我通过 Windows 命令行运行代码时,出现错误:

AttributeError: 'module' 对象没有属性 'filedialog'

以下是我的代码中的相关部分:

来自 filesearch.py​​

将 tkinter 导入为 tkdef get_path(self):"""存储用户选择的搜索路径"""self.paths = tk.filedialog.askdirectory(initialdir = FileSearch.DEFAULT)返回 self.paths

来自 GUI.py

from lib.filesearch import *def 浏览(自我):self.BrowseB['state']='disabled'self.p=self.CrawlObj.get_path()self.AddText('从路径搜索:' + str(self.p))self.BrowseB['state']='normal'

与此不同 问题 我只安装了一个版本的 python.即,Python34.

解决方案

我想首先说:如果您知道会使用子模块,请始终明确导入它们.这个答案的结尾有一个更引人注目的案例,说明这一点很重要.

由于 tkinter 的结构,您必须显式导入子模块才能加载:

将 tkinter 导入为 tkprint(hasattr(tk,filedialog")) # 在标准解释器中将打印 false导入 tkinter.filedialogprint(hasattr(tk,filedialog")) # 显式导入后应该总是打印真

您不需要在 IDLE 中执行此操作的原因是,在您的代码运行之前,IDLE 在后台设置了一些内容并最终导入了一些 tkinter 库.维护者之一 评论 这实际上是 IDLE 中的一个错误.

在 python 3.6.5 中(可能更早,只检查了这个版本)这个特定的差异已经被修复所以它不再发生在我下面展示的 2 个模块中.em>

在任何版本中,您都可以看到加载了一些代码的子模块列表,如下所示:

Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 03:03:55)# 标准解释器>>>导入系统>>>len(sys.modules) #自动加载的模块总数71>>>sorted(name for name in sys.modules.keys() if ("." in name)) #submodules加载['collections.abc'、'encodings.aliases'、'encodings.latin_1'、'encodings.utf_8'、'importlib._bootstrap'、'importlib._bootstrap_external'、'importlib.abc'、'importlib.machinery'、'importlib.util', 'os.path']>>>len(_) #子模块的数量10

在空闲状态:

Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 03:03:55)# 闲置的>>>导入系统>>>len(系统模块)152>>>排序(名称在 sys.modules.keys() 中,如果(."在名称中,而idlelib"不在名称中))['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(_) #与 idlelib 没有直接关系的子模块的数量.13

tkinter.constants 是在您 import tkinter 时加载的,所以在我测试的版本中,这个问题仍然只存在于 urllib.parseencodings.ascii(和 idlelib 模块,但通常生产代码不使用它)


但这不一定是 IDLE 特定的问题,更糟糕的问题是子模块是否由您使用的另一个库加载.以如下代码为例:

<预><代码>>>>进口大熊猫>>>导入http>>>客户端<来自'.../http/client.py'的模块'http.client'>

现在假设我们编写了一些其他代码,仍然使用 http.client 但没有使用 Pandas:

<预><代码>>>>导入http>>>客户端回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中.AttributeError: 模块 'http' 没有属性 'client'

通过这种方式,当使用它的代码加载 http.client 时,您可能会得到一个正常工作的子模块,这可能是通过使用碰巧使用它但会失败的库.

这让我回到了最初的观点 - 始终明确导入子模块.

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?

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()

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()

A file called GUI.py imports this file with: from lib.filesearch import * (the line of code I mentioned resides within the filesearch file.)

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'

Here are the relevant bits from my code:

From 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

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. Namely, 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:

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. One of the maintainers has commented that this is effectively a bug in 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.

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)


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'>

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'

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.

This takes me back to my initial point - always explicitly import submodules.

这篇关于为什么 tkinter 模块在通过命令行运行时会引发属性错误,但在通过 IDLE 运行时不会引发属性错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆