突然收到AttributeError

时间:2016-01-24 19:54:52

标签: python list random tkinter label

我有一段代码,从列表中选择一个单词并将其显示在标签上,用户必须正确地重新键入才能继续前进。

import random
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

WORDS = ['Games', 'Development', 'Keyboard', 'Speed', 'Typer', 'Anything',
         'Alpha']
score = 0

def choose_word():
    global word
    entry.focus_set()
    word = random.choice(WORDS)
    label.config(text=str(word.lower()))

def check_entry(event):
    global score
    if entry.get().lower() == word.lower():
        score += 1
        print(score)
    elif entry.get().lower() != word.lower():
        score -= 1
        print(score)
    choose_word()
    entry.delete(0, tk.END)

root = tk.Tk()

label = tk.Label(root)
entry = tk.Entry(root)

label.pack()
entry.pack()

choose_word()
root.bind('<Return>', check_entry)
root.mainloop()

自从几个月前我开始研究代码以来,我在代码的所有版本中都使用了相同的代码。我没有改变它一点直到现在才完美地工作。错误是:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\ernxs\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
    return self.func(*args)
  File "C:\Users\ernxs\Downloads\speedtypr\Speedtypr FINAL\speedtyper.pyw", line 685, in choose_word
    label.config(text=str(word.lower()))
AttributeError: 'generator' object has no attribute 'lower'

我上周注意到这个错误,因为它很少发生,但现在我无法超越第一个单词而没有抛出这个错误。我的代码在过去几个月中经历了主要的变化,但是我已经将这些功能和与它们相关的任何内容完全保持不变,我不知道为什么它在3个月内完美运行,然后现在已停止工作。

我已经尝试了上面的代码并且它工作得很好但是当我在我的完整程序中运行它时,我得到错误,尽管没有其他任何与我提到的功能相关。

我已尝试包含更多我的程序(我希望不是太多)但它仍然不会抛出同样的错误:

try:
    import tkinter as tk

except ImportError:
    import Tkinter as tk

import time
import random

correct_words = []
WORDS = ['Basic', 'Christmas', 'Summer', 'Sports', 'Winter', 'Negative',
         'Beach', 'Country', 'Christmas', 'Food', 'Games', 'Music', 'Family']

time_score = 0 
word_count = 0
max_words = 12
skips = 0
total_words = 0
words_found = 0

def end_game():
    root.destroy()
def choose_word():
    global word, start_time
    go_btn.pack_forget()
    start_time = time.time()
    entry.focus_set()
    if word_count < max_words:

        word = random.choice(WORDS)

        label.config(text=str(word.lower()))
        time_score_label.config(text="Time: " + str(time_score) + "s")      

    else:
        end_game()

def check_entry(event):
    if entry.get().lower() == word.lower():
        update_right()

    elif entry.get().lower() != word.lower():
        update_wrong()

    if len(entry.get()) < 1:
        update_skip()

    update_time()    
    choose_word()
    entry.delete(0, tk.END)

def update_time():
    global time_score
    time_score += time.time() - start_time
    time_score = round(time_score,2)

def update_skip():
    global skips
    skips += 1
    skip_counter.config(text="Skips: " + str(skips))
    wrong_label.config(text="SKIPPED!", fg='red')
    time_score_label.config(text="Time: " + str(time_score) + "s")

def update_right():
    global word_count, words_found

    word_count += 1
    words_found += 1
    WORDS.remove(word)
    correct_words.append(word)

    time_score_label.config(text="Time: " + str(time_score) + "s")
    word_counter.config(text="Words: " + str(word_count))
    wrong_label.config(text="")

def update_wrong():    
    wrong_label.config(text="WRONG!", fg='red')
    time_score_label.config(text="Time: " + str(time_score) + "s")

def display():
    for i in (label, time_score_label, word_counter, skip_counter, wrong_label,
        entry):
        i.pack()
    choose_word()

root = tk.Tk()
go_btn = tk.Button(root, text="GO!", command=display, width=17)
go_btn.pack()
label = tk.Label(root, font=("Helvetica", 60))
time_score_label = tk.Label(root, text="Time: " + str(time_score) +
                                "s", font=('Helvetica', 14))
word_counter = tk.Label(root, text="Words: " + str(word_count),
                            font =("Helvetica", 14))
skip_counter = tk.Label(root, text="Skips: " + str(skips),
                            font =("Helvetica", 14))
wrong_label = tk.Label(root, text="", font =("Helvetica, 14"))
entry = tk.Entry()

root.bind("<Return>", check_entry)
root.mainloop()

这是与此功能相关的所有内容,我无法重现错误。我不会发布我的完整程序,因为它太长了所以我还能尝试其他任何东西吗?

2 个答案:

答案 0 :(得分:1)

正如您所提到的,上面的代码似乎运行正常,但是您收到的错误告诉我们,在其他地方,您的代码(或您在其他地方使用的代码)也声明了一个“全局词”并重新分配它到发电机。

编辑

阅读下面的评论引发了一个额外的想法;因为'word'未在check_entry中声明,并且check_entry然后绑定到tkinter事件,这可能表明问题而无需外部重新声明“word”变量。我不熟悉Tkinter的代码库,但是根据它们触发/存储事件的方式,可能会有一个promissory return(生成器的yield)调用,它代表绑定状态中的'word'。 您可以通过在check_entry函数中将'word'声明为全局来测试此理论,但我仍然强烈建议使用提议的OOPier解决方案而不是此快捷方式,即使它正常工作

我建议您重构一下这里使用类来避免使用全局变量来解决问题。见下文,但请注意,这不是一个完美的重构;您应该尝试根据良好实践将TK Inter工作分离到一个单独的类中:

    try:
    import tkinter as tk

except ImportError:
    import Tkinter as tk

import time
import random


class MyProgram(object):
    def __init__(self):
        self.word = None
        self.start_time = None
        self.correct_words = []
        self.WORDS = ['Basic', 'Christmas', 'Summer', 'Sports', 'Winter', 'Negative',
                 'Beach', 'Country', 'Christmas', 'Food', 'Games', 'Music', 'Family']

        #self.WORDS = ["test"]
        self.time_score = 0
        self.word_count = 0
        self.max_words = len(self.WORDS)
        self.skips = 0
        self.total_words = 0
        self.words_found = 0
        self.setup_tk_components()

    def setup_tk_components(self):
        self.go_btn = tk.Button(root, text="GO!", command=self.display, width=17)
        self.go_btn.pack()
        self.label = tk.Label(root, font=("Helvetica", 60))
        self.time_score_label = tk.Label(root, text="Time: " + str(self.time_score) +
                                        "s", font=('Helvetica', 14))
        self.word_counter = tk.Label(root, text="Words: " + str(self.word_count),
                                    font =("Helvetica", 14))
        self.skip_counter = tk.Label(root, text="Skips: " + str(self.skips),
                                    font =("Helvetica", 14))
        self.wrong_label = tk.Label(root, text="", font =("Helvetica, 14"))
        self.entry = tk.Entry()

    @staticmethod
    def end_game():
        root.destroy()

    def choose_word(self):
        self.go_btn.pack_forget()
        self.start_time = time.time()
        self.entry.focus_set()
        if self.word_count < self.max_words:

            self.word = random.choice(self.WORDS)

            self.label.config(text=str(self.word.lower()))
            self.time_score_label.config(text="Time: " + str(self.time_score) + "s")
            self.entry.delete(0, tk.END)
        else:
            MyProgram.end_game()

    def check_entry(self, event):
        if self.entry.get().lower() == self.word.lower():
            self.update_right()

        elif self.entry.get().lower() != self.word.lower():
            self.update_wrong()

        if len(self.entry.get()) < 1:
            self.update_skip()

        self.update_time()
        self.choose_word()

    def update_time(self):
        self.time_score += time.time() - self.start_time
        self.time_score = round(self.time_score, 2)

    def update_skip(self):
        self.skips += 1
        self.skip_counter.config(text="Skips: " + str(self.skips))
        self.wrong_label.config(text="SKIPPED!", fg='red')
        self.time_score_label.config(text="Time: " + str(self.time_score) + "s")

    def update_right(self):
        self.word_count += 1
        self.words_found += 1
        self.WORDS.remove(self.word)
        self.correct_words.append(self.word)

        self.time_score_label.config(text="Time: " + str(self.time_score) + "s")
        self.word_counter.config(text="Words: " + str(self.word_count))
        self.wrong_label.config(text="")

    def update_wrong(self):
        self.wrong_label.config(text="WRONG!", fg='red')
        self.time_score_label.config(text="Time: " + str(self.time_score) + "s")

    def display(self):
        for i in (self.label, self.time_score_label, self.word_counter, self.skip_counter, self.wrong_label, self.entry):
            i.pack()
        self.choose_word()

if __name__ == '__main__':
    root = tk.Tk()
    mp = MyProgram()

    root.bind("<Return>", mp.check_entry)
    root.mainloop()
    print mp.correct_words

编辑:一个更简单的例子,说明另一个函数将我的字符串更改为int的情况:

def asstr():
    global word
    word = "WORD"
def asint():
    global word
    word = 1

asstr()
print word
print word.lower()
asint()
print word
print word.lower()

输出:

WORD
word
1
Traceback (most recent call last):
    ...
AttributeError: 'int' object has no attribute 'lower'

答案 1 :(得分:0)

错误在word = random.choice(WORDS) label.config(text=str(word.lower())) 函数中,行:

WORDS

似乎可以使用字符串列表word.lower(),选择一个。 str()是一个字符串操作。我不知道为什么会增加word来电。

该错误表示generator不再是字符串,而是generatorWORDS就像一个列表,期望它一次返回一个值,并且你不能重复迭代它。

所以你需要关注random.choice。现在有什么不同。 global word也可能已被更改。

说到这个功能,为什么使用query.query ("select ESTREC,LOTE,FECREC from prueba.RECAUDO_ENC where NITREC = :P1 and ESTREC = :P2 ORDER BY FECREC DESC", rsh, new Object[]{"1234","PG"}); ?我不认为它会导致问题,但它看起来不像是一个好的编程实践。