Python: создание словаря, который записывает высокие оценки в файл

97
7

Во-первых: вам не нужно кодировать это для меня, если вы не супер удивительный приятный парень. Но так как вы отлично справляетесь с программированием и понимаете его намного лучше, чем я и все, это может быть проще (поскольку это, вероятно, не слишком много строк кода), чем писать абзац после абзаца, пытаясь заставить меня понять это.

Итак - мне нужно составить список высоких баллов, которые обновляются при новых записях. Итак, вот оно:

Первый шаг - сделано

У меня есть введенный пользователем вход, который был взят как данные для нескольких расчетов:

import time
import datetime

print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
a = raw_input("Enter weight: ")
b = raw_input("Enter height: ")
c = a/b

Второй шаг - создание списка высоких баллов

Здесь мне нужен какой-то словарь или вещь, которая будет читать предыдущие записи и проверять, является ли оценка (c) (по крайней мере) лучше, чем оценка последнего в "высоких баллах", и если она, он предложит вам ввести свое имя.

После того, как вы введете свое имя, оно опубликует ваше имя, ваш a, b, c и время в списке высоких баллов.

Это то, что я придумал, и это определенно не работает:

list = [("CPU", 200, 100, 2, time1)]
player = "CPU"
a = 200
b = 100
c = 2
time1 = "20.12.2012, 21:38"
list.append((player, a, b, c, time1))
list.sort()

import pickle
scores = open("scores", "w")
pickle.dump(list[-5:], scores)
scores.close()

scores = open("scores", "r")
oldscores = pickle.load(scores)
scores.close()
print oldscores()

Я знаю, что я сделал что-то ужасно глупое, но в любом случае спасибо за это, и надеюсь, вы сможете помочь мне с этим. :-)

спросил(а) 2012-12-21T00:44:00+04:00 7 лет, 9 месяцев назад
1
Решение
90

Во-первых, не используйте list как имя переменной. Он затеняет встроенный объект list. Во-вторых, избегайте использования простых строк даты, поскольку гораздо проще работать с объектами datetime, которые поддерживают правильные сравнения и простые преобразования.

Вот полный пример вашего кода с отдельными функциями, чтобы помочь разделить шаги. Я стараюсь не использовать какие-либо более продвинутые модули или функциональные возможности, поскольку вы, очевидно, просто учитесь:

import os
import datetime
import cPickle

# just a constants we can use to define our score file location
SCORES_FILE = "scores.pickle"

def get_user_data():
time1 = datetime.datetime.now()
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")

a = None
while True:
a = raw_input("Enter weight: ")
try:
a = float(a)
except:
continue
else:
break

b = None
while True:
b = raw_input("Enter height: ")
try:
b = float(b)
except:
continue
else:
break

c = a/b

return ['', a, b, c, time1]

def read_high_scores():
# initialize an empty score file if it does
# not exist already, and return an empty list
if not os.path.isfile(SCORES_FILE):
write_high_scores([])
return []

with open(SCORES_FILE, 'r') as f:
scores = cPickle.load(f)
return scores

def write_high_scores(scores):
with open(SCORES_FILE, 'w') as f:
cPickle.dump(scores, f)

def update_scores(newScore, highScores):
# reuse an anonymous function for looking
# up the 'c' (4th item) score from the object
key = lambda item: item[3]

# make a local copy of the scores
highScores = highScores[:]

lowest = None
if highScores:
lowest = min(highScores, key=key)

# only add the new score if the high scores
# are empty, or it beats the lowest one
if lowest is None or (newScore[3] > lowest[3]):
newScore[0] = raw_input("Enter name: ")
highScores.append(newScore)

# take only the highest 5 scores and return them
highScores.sort(key=key, reverse=True)
return highScores[:5]

def print_high_scores(scores):
# loop over scores using enumerate to also
# get an int counter for printing
for i, score in enumerate(scores):
name, a, b, c, time1 = score
# #1 50.0 jdi (20.12.2012, 15:02)
print "#%d\t%s\t%s\t(%s)" % \
(i+1, c, name, time1.strftime("%d.%m.%Y, %H:%M"))

def main():
score = get_user_data()
highScores = read_high_scores()

highScores = update_scores(score, highScores)

write_high_scores(highScores)
print_high_scores(highScores)

if __name__ == "__main__":
main()

Теперь он только добавляет новые баллы, если не было высоких баллов, или он превосходит самые низкие. Вы можете изменить его, чтобы всегда добавлять новый балл, если есть менее 5 предыдущих баллов, вместо того, чтобы требовать, чтобы он бил самый низкий. А затем просто выполните самую низкую проверку после размера рекордов> = 5

ответил(а) 2012-12-21T01:58:00+04:00 7 лет, 9 месяцев назад
70

Первое, что я заметил, это то, что вы не list.sort() что сортировка должна основываться на последнем элементе каждой записи. По умолчанию list.sort() будет использовать порядок сортировки по умолчанию Python, который будет сортировать записи на основе первого элемента каждой записи (то есть имя), затем перейти к второму элементу, третьему элементу и т.д. Итак, вы должны указать list.sort() какой элемент использовать для сортировки:

from operator import itemgetter
[...]
list.sort(key=itemgetter(3))

Это будет сортировать записи на основе элемента с индексом 3 в каждом кортеже, т.е. Четвертого элемента.

Кроме того, print oldscores(), безусловно, не будет работать, поскольку oldscores не является функцией, поэтому вы не можете вызвать его с помощью оператора (). print oldscores, вероятно, лучше.

ответил(а) 2012-12-21T00:48:00+04:00 7 лет, 9 месяцев назад
57

Вам определенно не нужен словарь здесь. Весь смысл словаря состоит в том, чтобы иметь возможность сопоставлять ключи со значениями без какой-либо сортировки. Вы хотите отсортированный список. И у тебя это уже есть.

Ну, как указывает Тамас, у вас есть список, отсортированный по имени игрока, а не счет. Кроме того, вы хотите сортировать в нисходящем порядке, а не вверх. Вы можете использовать шаблон decorate-sort-undecorate, или ключевую функцию, или что-то еще, но вам нужно что-то сделать. Кроме того, вы поместили его в list имен переменных, который представляет собой очень плохую идею, потому что это уже имя типа list.

В любом случае, вы можете узнать, нужно ли добавлять что-то в отсортированный list и куда его вставить, если так, используя модуль bisect в стандартной библиотеке. Но, вероятно, проще просто использовать что-то вроде SortedCollection или blist.

Вот пример:

highscores = SortedCollection(scores, key=lambda x: -x[3])

Теперь, когда вы закончите игру:

highscores.insert_right((player, a, b, newscore, time1))
del highscores[-1]

Это. Если вы на самом деле не вошли в топ-10, вас добавят в # 11, а затем удаляют. Если бы вы были в первой десятке, вы будете добавлены, а старая # 10 теперь будет # 11 и будет удалена.

Если вы не хотите заранее заполнять список 10 поддельными баллами, к которым привыкли старые аркадные игры, просто измените их на это:

highscores.insert_right((player, a, b, newscore, time1))
del highscores[10:]

Теперь, если уже было 10 баллов, когда вы добавите, # 11 будет удалено, но если их было всего 3, ничего не будет удалено, а теперь их будет 4.

Между тем, я не уверен, почему вы пишете новые оценки в файл pickle, а затем снова читаете то же самое. Возможно, вы захотите сделать чтение, прежде чем добавлять рекорды в список, а затем написать после добавления.

Вы также спросили, как "украсить список". Ну, есть три стороны.

Прежде всего, в коде, (player, a, b, c, time1) не очень (player, a, b, c, time1). Разумеется, предоставление переменных лучше имен, но в конечном итоге вы догадываетесь о том, что при доступе к списку вам нужно сделать entry[3] чтобы получить оценку или entry[4] чтобы получить время.

Существует как минимум три способа решить эту проблему:

    Сохраните list (или SortedCollection) dict вместо tuple s. Код становится немного более подробным, но более читаемым. Вы пишете {'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1}, а затем при доступе к списку вы делаете entry['score'] entry[3]. Используйте коллекцию namedtuple s. Теперь вы можете просто вставить ScoreEntry(player, a, b, c, time1), или вы можете вставить ScoreEntry(player=player, height=a, weight=b, score=c, time=time1), в зависимости от того, что более ScoreEntry(player=player, height=a, weight=b, score=c, time=time1) в данном случае, и оба они работают одинаково. И вы можете получить доступ к entry.score или как entry[3], снова используя то, что более читаемо. Напишите явный класс для записей баллов. Это очень похоже на предыдущий, но там больше кода для записи, и вы больше не можете делать индексированный доступ, но с плюсом вы не должны понимать namedtuple.

Во-вторых, если вы просто print записи, они выглядят как беспорядок. Способ справиться с этим - форматирование строк. Вместо показателей print scores вы делаете что-то вроде этого:

print '\n'.join("{}: height {}, weight {}, score {} at {}". format (запись) для записи в рекордах)

Если вы используете class или namedtuple вместо одного tuple, вы можете даже форматировать по имени вместо позиции, делая код более читаемым.

Наконец, файл рекорда сам по себе является нечитаемым беспорядком, потому что pickle не предназначен для потребления человеком. Если вы хотите, чтобы он был читаемым человеком, вам нужно выбрать формат и написать код для сериализации этого формата. К счастью, формат CSV довольно удобочитаемый, и большая часть кода уже написана для вас в модуле csv. (Возможно, вы захотите взглянуть на DictReader и DictWriter, особенно если вы хотите написать строку заголовка. Опять же, компромисс немного большего количества кода для большей удобочитаемости.)

ответил(а) 2012-12-21T01:26:00+04:00 7 лет, 9 месяцев назад
57

Вот что я замечаю.

Кажется, что эти строки находятся в неправильном порядке:

print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()

Когда пользователь вводит высоту и вес, они будут считаться строками, а не целыми числами, поэтому вы получите TypeError в этой строке:

c = a/b

Вы могли бы решить эту проблему, наложив a и b на float следующим образом:

a = float(raw_input("Enter weight: "))

Но вам, вероятно, придется обернуть это в блок try/catch, если пользователь ставит мусор, в основном все, что нельзя отнести к поплавку. Положите все это на некоторое время, пока они не поправятся.

Итак, что-то вроде этого:

b = None
while b == None:
try:
b = float(raw_input("Enter height: "))
except:
print "Weight should be entered using only digits, like '187'"

Итак, во второй части вы не должны использовать list как имя переменной, так как он встроен, я буду использовать high_scores.

# Add one default entry to the list
high_scores = [("CPU", 200, 100, 2, "20.12.2012, 4:20")]

Вы говорите, что хотите проверить счет игрока на высокий балл, чтобы посмотреть, лучше ли это, но если это так, то почему список? Почему не только одна запись? Во всяком случае, это меня сбивает с толку, не уверенный, действительно ли вам нужен список высоких баллов или только один высокий балл.

Итак, пусть просто добавьте оценку, независимо от того, что:

Предположим, вы получили свое имя в переменной name.

high_score.append((name, a, b, c, time1))

Затем примените другой ответ от @Tamás

ответил(а) 2012-12-21T01:26:00+04:00 7 лет, 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема