【问题标题】:Python: creating a dictionary that writes high scores to a filePython:创建一个将高分写入文件的字典
【发布时间】:2012-12-20 21:44:24
【问题描述】:

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

所以 - 我需要制作一个高分列表,以便在新条目时自行更新。 就这样吧:

第一步 - 完成

我有玩家输入的输入,已作为数据进行一些计算:

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

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

【问题讨论】:

  • 您的代码完全乱序且重复。你不能用time1 创建list(顺便说一句,你不应该把它称为list 是一个内置的),因为你直到5 行之后才初始化time1。而且由于您已经有了列表,因此明确地重新声明所有这些值只是为错误引入了一个向量;如果您需要分配给唯一标签的值,请解压缩 list 中的元组。您可能还想open并将旧的高分读入list,然后再添加和排序新的分数,否则您将践踏现有的分数列表。
  • 标题说字典,但是这里没有字典。
  • 我现在明白我应该说一个排序列表,而不是字典,我是一个新手初学者。所以我应该做一个 open("..." "r") 然后 open("..." "w") 一切都应该没问题?

标签: python list dictionary pickle


【解决方案1】:

首先,不要使用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 个,您可以修改它以始终添加新分数,而不是要求它击败最低的分数。然后在 highscores >= 5 的大小后执行最低检查

【讨论】:

  • HOLLY MOLLY 你做了一个完整的代码,一切都很好。你有一个贝宝帐户或什么这个东西是我需要的。耶为你哇
  • :-) 好吧,我不打算写出代码,但我开始看到将您引向更复杂解决方案的答案。我想我最好用一堆 cmets 写一些东西,并保持非常简单。不,我不能拿你的钱。这是一个免费的社区!享受吧。
【解决方案2】:

我注意到的第一件事是你没有告诉list.sort() 排序应该基于每个条目的最后一个元素。默认情况下,list.sort() 将使用 Python 的默认排序顺序,它将根据每个条目的第一个元素(即名称)对条目进行排序,然后对第二个元素、第三个元素等进行排序。所以,你必须告诉list.sort() 使用哪个项目进行排序:

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

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

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

【讨论】:

  • 我认为“分数”是 c,元组的索引 3。所以我认为key=itemgetter(3) 是 OP 想要的。
  • 很好,但你知道我对 python 和编程真的很陌生,我什至不知道你的代码是什么意思。就像从运算符导入 itemgetter
  • [...] list.sort....除了更正 oldscores 和 list.sort() 之外,我只是不知道我应该做什么。感谢您的回答虽然
  • @academeniaan:您确实要求编写代码而无需冗长的解释 :-)
  • 是的,我知道你这个混蛋 :D 但我还是遇到了这个问题。如果他“击败得分”,我将如何插入人类玩家?我将如何美化我的清单?因为我的文件 ATM 看起来非常难以理解
【解决方案3】:

这是我注意到的事情。

这些行似乎顺序错误:

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 的另一个答案

【讨论】:

  • 嗨,我绝对想要一个包含 3 个或更多人的高分名单,他们可以进入。如果这对你有任何帮助。
  • 然后像@Tamás 提到的那样使用 itemgetter 对列表进行排序。 high_score.sort(key=itemgetter(3)) 然后将列表修剪为 5 人,执行以下操作:high_score = high_score[:5]
  • 是的,所以 Tamas 所做的就是要走的路,我忽略了 abarnert?
  • 两者都有一些优点,但看起来@jdi 将您想要使用的综合解决方案放在一起。
【解决方案4】:

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

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

无论如何,您可以使用标准库中的bisect 模块来确定是否将某些内容添加到已排序的list 中,以及在哪里插入。但是使用SortedCollection 或blist 之类的东西可能更简单。

这是一个例子:

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] 获得时间。

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

  • 存储dicts 的list(或SortedCollection)而不是tuples。代码变得更加冗长,但更具可读性。你写{'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1},然后在访问列表时,你写entry['score']而不是entry[3]。
  • 使用namedtuples 的集合。现在您实际上可以只插入ScoreEntry(player, a, b, c, 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(entry) 进入高分)

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

最后,高分文件本身是一团乱七八糟的东西,因为pickle 不适合人类消费。如果您希望它是人类可读的,您必须选择一种格式,并编写代码来序列化该格式。幸运的是,CSV 格式非常易于阅读,并且大部分代码已经在 csv 模块中为您编写好了。 (您可能需要查看 DictReader 和 DictWriter 类,尤其是如果您想编写标题行。同样,为了提高可读性,需要权衡更多代码。)

【讨论】:

  • 当 OP 可以继续从末尾开始索引 (myList[-5:]) 时,是否真的需要按降序排序? sort 也需要 reverse=True 如果确实需要倒退
  • @jdi: bisect 不接受reverse。 SortedCollection 也没有。不,您不需要需要按降序排序,但是“删除掉到最后的人”变得更加复杂,而且在概念上更难将 highscores 视为倒置前 10 名而不是前 10 名。
  • 嗨,我好像不能使用这个乱七八糟的 SortedCollection“东西”。我需要先导入某个库还是什么?请帮忙
  • @academeniaan:要使用来自 ActiveState 的配方,如 SortedCollection,您必须下载它并将其保存在您可以导入的文件中,或者将其直接添加到您自己的文件中。我相信食谱网站上有一些东西解释了如何做到这一点。 (并且要使用像blist这样的PyPI库,你需要pip install blist,然后你可以导入它。)
  • 好吧,我不认为这会削减它,因为不幸的是,我必须使程序易于移植。只是为了想象,我们只是说某个教授在他的时代没有时间这样做,但谢谢
猜你喜欢
  • 2020-08-26
  • 1970-01-01
  • 1970-01-01
  • 2016-03-08
  • 2020-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-18
相关资源
最近更新 更多