Python:创建一个将高分写入文件的字典

时间:2012-12-20 21:44:25

标签: python list dictionary pickle

首先:你不必为我编码,除非你是一个超级棒的好人。但是因为你们都非常擅长编程并且比我和所有人都更了解它,所以它可能更容易(因为它可能不是太多的代码行)而不是一段一段地写下来试图让我理解它。

所以 - 我需要列出一些在新条目上更新的高分。 所以这就是:

第一步 - 完成

我有玩家输入的输入,它被作为一些计算的数据:

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)是否(至少)比“高”中的最后一个分数更好分数“,如果是,则会提示您输入您的姓名。

输入您的姓名后,它会将您的姓名,abc和时间发布在高分列表中。

这就是我想出来的,它绝对不起作用:

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

知道我做了一件非常愚蠢的事情,但无论如何,感谢您阅读本文,我希望您可以帮助我解决这个问题。 : - )

4 个答案:

答案 0 :(得分:4)

首先,不要将list用作变量名。它会遮挡built-in 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

之后执行最低检查

答案 1 :(得分:2)

我注意到的第一件事是你没有告诉list.sort()排序应该基于每个条目的最后一个元素。默认情况下,list.sort()将使用Python的默认排序顺序,它将根据每个条目的第一个元素(即名称)对条目进行排序,然后依次模式为第二个元素,第三个元素,依此类推。因此,您必须告诉list.sort()要用于排序的项目:

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

这将根据每个元组中索引为3的项目(即第四个项目)对条目进行排序。

此外,print oldscores()肯定不会起作用,因为oldscores不是函数,因此您无法使用()运算符调用它。 print oldscores可能更好。

答案 2 :(得分:1)

你绝对不需要这里的字典。字典的重点是能够将键映射到值,而无需任何排序。你想要的是一个排序列表。你已经有了。

好吧,正如Tamás指出的那样,你实际上有一个按球员名称排序的列表,而不是得分。最重要的是,您希望按向下排序,而不是向上排序。你可以使用decorate-sort-undecorate模式,或者一个键函数,或者其他什么,但你需要做某事。此外,您已将其放在名为list的变量中,这是一个非常糟糕的主意,因为它已经是list类型的名称。

无论如何,您可以使用标准库中的bisect模块找出是否在排序的list中添加内容,以及在何处插入(如果是)。但是使用SortedCollectionblist之类的东西可能更简单。

以下是一个例子:

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

现在,当你完成游戏时:

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

就是这样。如果你实际上不在前10名,你将被添加到#11,然后被删除。如果你进入前10名,那么你将被添加,旧的#10现在将被#11删除。

如果你不想像旧的街机游戏那样用10个假分数预填充列表,只需将其更改为:

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

现在,如果已经有10个分数,当你被添加时,#11将被删除,但如果只有3个,则没有被删除,现在有4个。

与此同时,我不确定你为什么要将新分数写入pickle文件,然后再读回同样的东西。你可能想要在将高分数添加到列表,然后在添加后进行写作。

您还问过如何“美化列表”。那么,有三个方面。

首先,在代码中,(player, a, b, c, time1)不是很有意义。当然,给变量提供更好的名称会有所帮助,但最终你仍然可以得出这样的事实:当访问列表时,你必须entry[3]来获得分数或entry[4]来获得时间。< / p>

至少有三种方法可以解决这个问题:

  • 存储list(或SortedCollectiondict代替tuple s。代码变得更冗长,但更具可读性。您编写{'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1},然后在访问列表时,您执行entry['score']而不是entry[3]
  • 使用namedtuple的集合。现在您实际上只需插入ScoreEntry(player, a, b, c, time1),或者您可以插入ScoreEntry(player=player, height=a, weight=b, score=c, time=time1),在给定的情况下更具可读性,并且它们的工作方式相同。您可以再次使用更易读的内容访问entry.scoreentry[3]
  • 为分数条目写一个显式类。这与前一个非常类似,但是需要编写更多代码,并且您不能再进行索引访问,但从好的方面来说,您不必理解namedtuple

其次,如果您只是print条目,它们看起来就像一团糟。处理它的方法是字符串格式化。而不是print scores,你做的是这样的事情:

打印'\ n'。join(“{}:height {},weight {},得分{} at {}”。format(entry)                    进入高分榜)

如果您使用的是classnamedtuple而不只是tuple,您甚至可以按名称而不是按位置进行格式化,从而使代码更具可读性。

最后,高分文件本身是一个难以理解的混乱,因为pickle不适合人类消费。如果您希望它是人类可读的,您必须选择一种格式,并编写代码以序列化该格式。幸运的是,CSV格式非常易读,大部分代码都是在csv模块中为您编写的。 (您可能希望查看DictReaderDictWriter类,特别是如果您想要编写标题行。再次,需要更多代码的折衷以获得更多可读性。)

答案 3 :(得分:1)

以下是我注意到的事情。

这些行似乎顺序错误:

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

当用户输入高度和重量时,它们将作为字符串而非整数读入,因此您将在此行中获得TypeError:

c = a/b

您可以通过将a和b转换为浮动来解决此问题:

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的其他答案