Найти - Пользователи
Полная версия: Прошу оценить\исправить\дополнить код.
Начало » Python для новичков » Прошу оценить\исправить\дополнить код.
1
UsCr
В свободное от учёбы\работы\чтения\гуляния\сна время читаю литературу по питону и учусь на программке, которая, по замыслу должна расти вместе со мной. Изначально я хотел дописать её до идеального, на мой взгляд, состояния и показать здесь. Сейчас я фактически в тупике, поэтому решил уже сейчас спросить совета.

Прошу указать на явные ошибки, которые в больших приложениях сильно сказались бы на скорости.
Прошу подсказать, как избавиться от global 'ов.
Ну и стиль… Хотя я старался соответствовать рекомендациям.

Это игра “Быки и коровы” со словами.
Написана на 2.6. GUI на TKinter.
Текущая версия вполне играбельна, хотя, возможно, имеет некоторое количество багов.
По ссылке ниже отдельно выложены файлы словарей + иконки для окон + исходник. Папка DATA должна лежать вместе со скриптом в одном каталоге.
Ну и для удобства оценки - исходник во врезке:

# -*- coding: utf-8 -*-
from Tkinter import *
import Tkinter
from ScrolledText import *
from random import randrange
import os
ALPHABET = [u'а', u'б', u'в', u'г', u'д', u'е', u'ё', u'ж', u'з', u'и', u'й', u'к', u'л',\
u'м', u'н', u'о', u'п', u'р', u'с', u'т', u'у', u'ф', u'х', u'ц', \
u'ч', u'ш', u'щ', u'ъ', u'ы', u'ь', u'э', u'ю', u'я']
SPECSYMB = [u'.', u':', u'!'] #символы командного режима

word_secret = ''#загаданное слово. get_new_word() - функция для дёрганья его из файла

bad_cha_show = ''#какие символы из алфавита показывать в качестве "плохих"

enter_temp = '' #тут храним последний ввод, при котором строка не начиналась c
#символа команды

hard_level = 4 #тут уровень сложности (количество букв в загаданном слове)

loop=1 #loop это номер хода (номер строки)


def start(event):
global enter_temp
global loop

#получаем слово, которое было введено
enterus = mainbox.get(str(loop)+'.0', END)[:-1]

if enterus in '!magic':
mainbox.insert(END,' --->'+word_secret+'<---') #чит :Р

if enterus[0] == '.': #если введённая строка начинается с точки
if enterus == '.': #и вообще, она и есть точка, то
bad.delete(1.0, END) #в качестве "плохих" выводим предыдущий ввод
bad.insert(END, badchars(enter_temp))
else:
bad.delete(1.0, END) #а если она не заканчивается точкой, то соответственно,
bad.insert(END, badchars(enterus[1:])) #текущий ввод суём в "плохие" буквы

if enterus[0] == ':':
good.delete(1.0, END)
#заменяем ' ' на '-' и суем в поле для хранения пользовательского варианта слова
good.insert(END, enterus[1:].replace(' ', ' - '))

if enterus[0] not in SPECSYMB: #если ввод не начинается с символа команды

enter_temp = enterus #тут храним последнюю введённую строку

#передаём в процедурину содержимое поля ввода в нижнем регистре,
#заменив "ё" на "е", это ведь ода буква...
cows, bulls = cow_bull(enterus.lower().replace(u'ё', u'е'))
cow_i, bull_i = len(cows), len(bulls)

if cow_i == 0: cow_st = '' # тут выбираем красивое окончание
elif cow_i == 1: cow_st = 'а'
elif cow_i <= 4: cow_st = 'ы'
elif cow_i >= 5: cow_st = ''

if bull_i == 0: bull_st = 'ов'
elif bull_i == 1: bull_st = ''
elif bull_i <= 4: bull_st = 'а'
elif bull_i >= 5: bull_st = 'ов'

#формируем вывод со всеми циферками и правильными окончаниями:
mainbox.insert(END, "--> Тут {cowi} коров{cowst} и {buli} бык{bulst}."\
.format(cowi = cow_i, cowst = cow_st, buli = bull_i, bulst = bull_st)) #красивенько все выводим в большое окно

if len(bulls) == len(enterus):
mainbox.insert(END, "\n\n\nПоздравляю, вы отгадали слово!\n")
mainbox.see(END)
loop+=4

mainbox.see(END)
loop += 1

def get_new_word(hl): #hl = hard level фактически-количество букв в слове
with open('DATA\\dict\\'+str(hard_level)) as f:
wordlist = f.readlines()
return unicode(wordlist[randrange(0,len(wordlist))][:-1], "cp1251")


def cow_bull(word_enter):
#Количество быков и коров в введённом слове
#word_enter введённое слово
#word_secret загаданное слово
cows = [] #Это "буквы-коровы"
bulls = [] #Это "буквы-быки"
if len(word_enter) > len(word_secret): #если введённое больше загаданного
word_enter = word_enter[0:len(word_secret)] #отрезаем лишнее

for i in range(len(word_enter)): #перебор по всем буквам введенного

if word_enter[i] in word_secret: #если буква есть в загаданном слове

if (word_enter[i] == word_secret[i]): #и стоит на своем месте
bulls.append(word_enter[i]) #она бык
else: #если не на своем месте
cows.append(word_enter[i]) #корова

return cows, bulls

def badchars(word):
string = '' #тут будем хранить строку для возврата
global bad_cha_show
for i in word: #по всем буквам переданного слова
if not i in bad_cha_show: #если буква отсутствует среди "плохих" букв
bad_cha_show += i #то добавляем её туда
for i in ALPHABET: #теперь по алфавиту:
if i in bad_cha_show: #если буква есть среди "плохих"
string += i+' ' #добавим его в строку
return string

def print_badcha(event): #выводит буквы, которых в слове нет в окошко bad
if mainbox.get(1.0,END)[:0]:
bad.delete(1.0, END)
bad.insert(END, badchars(enterfill.get()))
else:
bad.delete(1.0, END)
bad.insert(END, badchars(enter_temp))
enterfill.delete(0, END)

def print_goodcha(event): #выводит вариант слова в окошко good
good.delete(1.0, END)
good.insert(END, enterfill.get().replace(' ', '-'))
enterfill.delete(0, END)

def start_new_game(): #диалоговое окошко для начала новой игры

def return_and_die(event): #типа начать игру
global hard_level
global word_secret
global loop

loop = 1
hard_level = hardlist.get() #устанавливает уровень сложности
word_secret = get_new_word(hard_level) #вызывает процедурку, которая и подсунет само слово в переменную word_secret
mainbox.delete(1.0, END)
bad.delete(1.0, END)
good.delete(1.0, END)
good.insert(END, 'Длинна слова {0} знаков'.format(hard_level)) #сообщает длинну слова
dialog.destroy() #героически умирает

dialog = Toplevel()

dialog.title("Новая игра")
label = Label(dialog, text=u"Выберите количество букв в слове:")
hardlist = Scale(dialog, orient=HORIZONTAL, from_=3, to=10,\
tickinterval=1, resolution=1)
btn_go = Button(dialog, text="Начать")

label.pack(side=TOP)
hardlist.pack(side=TOP)
btn_go.pack(side=RIGHT)

btn_go.bind("<Button-1>", return_and_die)

def info(): #окошко "о программе"
info = Toplevel()

info.title("Информация")
info.iconbitmap("DATA\\img\\info.ico")

infoblank = Canvas(info, width=370, height=200, bg="grey")
infoblank.create_text(190, 100, justify=CENTER, text="Text here")

infoblank.pack()
#---------------------------------------------------------------------------------------------
root = Tk() # окно
#root.style.theme_use("alt")
root.title("Быки и коровы")
root.iconbitmap("DATA\\img\\bull.ico")

frame_1 = Frame(root) #Рамки
frame_2 = Frame(root)

main_menu = Menu(root) #меню
root.config(menu=main_menu)
root_menu = Menu(main_menu)
main_menu.add_cascade(label="Меню", menu=root_menu)
root_menu.add_command(label="Новая игра", command=start_new_game) #Начать новую игру функцией start_new_game
root_menu.add_command(label="О программе", command=info)
root_menu.add_separator()
root_menu.add_command(label="Выход", command=root.destroy)

mainbox = ScrolledText(frame_1, width=47, height=25, font=('Tahoma', 10)) #Окно вывода
bad = Text(frame_2, width=8, height=10, font=('Tahoma',25,'bold')) #Буквы, которых нет в слове
good = Text(frame_1, height=1, width=50, font=('Tahoma', 10)) #Окошко для хранения варианта загаданного слова
btn_submit = Button(frame_2, width=8, text="Ввод")
btn_badchar = Button(frame_2, text="В список")
btn_goodchar = Button(frame_2, width=8, text="Вариант")

frame_1.pack(side = LEFT)
frame_2.pack(side = LEFT)

good.pack(side = TOP)
mainbox.pack(side = TOP)
bad.pack(side = TOP)
btn_submit.pack(side = LEFT)
btn_badchar.pack(side = RIGHT)
btn_goodchar.pack(side = RIGHT)

btn_submit.bind("<Button-1>", start)
btn_badchar.bind("<Button-1>", print_badcha)
btn_goodchar.bind("<Button-1>", print_goodcha)
mainbox.bind("<Return>", start)

word_secret = get_new_word(hard_level)
good.delete(1.0, END)
good.insert(END, 'Длинна слова '+str(hard_level)+' знаков')
root.mainloop()
Как работать с программой:
При запуске программа загадает слово из 4 букв.
Слова из другого количества букв доступны в менюшке “новая игра”
Ввод варианта и вывод осуществляется через основное окно. При нажатии кнопки “вариант” текущее введённое слово будет отображено в поле вверху окна, при этом пробелы в слове будут заменены на “ - ”. Это нужно для отображения вашего варианта слова (удобно, когда он перед глазами). При нажатии кнопки “в список” Текущее слово будет разбито на буквы и добавлено в поле с правой стороны. Это поле для букв, которых по вашему мнению в слове нет. Опять таки удобно держать их перед глазами. Функции кнопок продублированы командным режимом: ввод “:варинт” отобразит введённое слово в строке варианта. Ввод “.буквы” отобразит буквы “б, в, к, …” в поле для букв, которых в слове нет.

Коротко о правилах игры. Вы вводите слово. В ответ вы получаете сообщение о количестве быков и коров. Бык - буква которая присутствует в загаданном слове и В ВАШЕМ слове она присутствует и стоит на правильной (относительно загаданного слова) позиции. Корова - буква есть, но стоит на неверной позиции. Подробности в интернете…


http://slil.ru/29151670 - ZIP; 27 Кб
dartNNN
Я сделаю пару замечаний:
1. Код достаточно хороший, читаемый, кроме некоторых моментов
Вот эту строчку я бы разделил на две
cows, bulls = cow_bull(enterus.lower().replace(u'ё', u'е'))
enterus = enterus.lower().replace(u'ё', u'е')
cows, bulls = cow_bull(enterus)
Т.к. по-моему так проще понять.

unicode(wordlist[randrange(0,len(wordlist))][:-1], "cp1251")
Если бы файл был записан в unicode, то не пришлось бы его перекодировать (к этому совету отнестись скептически, т.к. я не очень хорошо знаком с кодировками в 2.6)

Формирование GUI надо бы вынести в отдельную функцию

Каждая программа должна начинаться словами (т.е. эти слова должный обрамлять начало программы):
if __name__ == '__main__':
#далее команды, с которых начинается выполнение программы
2. По поводу производительности: я лично особых ошибок не заметил

3. Можно попробовать сделать программу кроссплатформенной (чисто спортивный интерес). Например
'DATA\\dict\\'+str(hard_level) заменить на что-нибудь вроде os.path.join('DATA', ‘dict’, str(hard_level))

4. Если программа должна расти вместе с вами, то можно переписать ее в соответствии ООП, тогда все настройки (hard_level и др.) будут свойствами класса, что избавит от не очень красивой записи global (которая в данном случае вполне оправдана)

Ну вроде все сказал.
igor.kaist
Маленький хинт:
ALPHABET=map(unichr,(xrange(ord(u'а'),ord(u'я')+1)))
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB