我正在为一个学校数学课程开设一个简单的数学课程,孩子们在那里写下自己的名字,然后程序会问10个简单的数学问题.之后,孩子们的姓名和分数将根据他们的班级存储在不同的文本文件中.
这是我的问题.
目前,他们的名字以这种格式存储:William W - 10
所以第一部分是他们的名字,最后一部分是他们的分数.我不确定是否有更好的方法.
我如何做到这一点,如果同一个孩子再次进行测试,它将与之前的分数平均?
另外,如果孩子获得更高的分数,我该怎么做才能让孩子的分数过高?(这将是不同的文本文件到平均分数之一)
我正在尝试制作一个排名板,其中最高点的孩子出现在顶部,最低点出现在底部.
当在排行榜上显示时,我希望它看起来像这样.
我到处搜索,似乎没有找到答案.如果有人可以分享他们的知识,这将是很好的.谢谢
这是我未完成的代码.我是python XD的新手我需要为排行榜添加额外的东西:平均,最高和按字母顺序排列,每个学生的测试成绩最高.
def menu():
print "Welcome to Simple-Maths Questions program"
print "Your options are the following:"
print "1 - Do a test"
print "2 - Leaderboard"
print "3 - Exit"
question()
def question():
ans = input("Please enter the number: ")
if ans ==1:
start()
elif ans ==2:
leaderboard()
elif ans ==3:
exitprogramme()
else:
print "Error - invalid choice"
print " "
menu()
def leaderboard():
menu()
def exitprogramme():
quit()
def start():
f = open('names.txt', 'a')
print "Here are 10 easy maths question for you to solve"
name = raw_input("Please enter in your name and surname enitial (e.g Willam G)")
import random
import re
import operator
n1 = random.randint(1,9)
n2 = random.randint(1,9)
ops = {'+': operator.add, '-': operator.sub, '*': operator.mul}
op = random.choice(ops.keys())
a = 1
count = 0
while a <=10:
question = input("What is " + str(n1) + str(op) + str(n2) + "?")
a = a+1
ans = ops[op](n1, n2)
n1 = random.randint(1,9)
n2 = random.randint(1,9)
op = random.choice(ops.keys())
if question == ans:
count = count + 1
print "Well done"
else:
count = count + 0
print "WRONG"
print " "
print "You got score of "+ str(count)+"."
f.write(name+' - '+str(count)+'\n')
f.close()
f1 = open("names.txt", "r")
sortToFile = open("sortedFile.txt", "w")
for line in sorted(f1, key = str.lower):
sortToFile.write(line)
f1.close()
sort_nicely()
def sort_nicely():
import re
''' this is my attempt to do sort the names with score with highest score at the top '''
convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
Leaderboard = open("Leaderboard.txt", "w")
f2 = open("names.txt", "r")
for line in sorted(f2, key=alphanum_key, reverse=True):
Leaderboard.write(line)
print ""
menu()
Run Code Online (Sandbox Code Playgroud)
好吧,我打算只编写程序的相关部分,但我最终编写了一个完整的工作程序.因为我不知道你知道多少Python我只是要解释整个程序; 对不起,如果我有一点迂腐和/或浮夸的气氛.随意跳到底部并复制代码.
我编写的代码演示了4个Python习语:面向对象设计,字节数组/字符串切片和格式化,xrange()函数和错误捕获.
我认为csv不是解决问题的好角色.我选择将数据存储为文本表.每行采用以下形式:30个字符名称| 10个字符的总分| 9个字符尝试| 1 newline这样做的好处是每一行都是50个字符宽.因此,与csv方法不同,我们不必扫描逗号来查找相关数据; 我们只需要以50个字符为增量来查看数据.这使得代码更具可读性,并且现在不像文本存储空间那么重要.
示例:Chell R在5次尝试中获得42分变为:
" Chell R 42 5\n"
Run Code Online (Sandbox Code Playgroud)
由于我们稍后将对这些数据进行排序,因此将学生的姓名和他/她的分数存储在对象中将是有利的.这个对象使排序更简单; 因为名称永久地附加到分数,所以我们不必担心在数据排序时确保名称数据跟随分数数据.它只是隐含地发生.
这是代码的第一部分:
import time, random, operator
directory_name = r"C:\Users\YOU\yourDirectoryHere\\"
#When you change the directory name, be SURE to include
#the double backslash at the end.
datafile = open(directory_name + "scores.txt")
data = bytearray(datafile.read())
datafile.close()
class entry():
def __init__(self, i):
#i is the location of the student's entry in the data.
global data
self.name = data[i:i + 30]
self.total = int(data[i + 30:i + 40])
self.attempts = int(data[i + 40:i + 49])
if self.attempts:
self.average = float(self.total) / self.attempts
else: self.average = 0.0
def update_score(self, score, i):
global data, directory_name
self.total += score
self.attempts += 1
self.average = float(self.total) / self.attempts
data[i + 30: i + 40] = "%10i" % self.total
data[i + 40: i + 49] = "%9i" % self.attempts
datafile = open(directory_name + "scores.txt",'w')
datafile.write(data)
datafile.close()
Run Code Online (Sandbox Code Playgroud)
init下定义的方法称为魔术方法.这意味着与常规方法update_score不同,您不会自己调用该方法.它由Python自动调用; 在这种情况下,在创建条目对象时调用它.
需要指出的五点:1:为了快速解释语法烦恼,每个方法必须将self作为第一个参数,并且存储在对象中的每个变量都需要一个self.字首.
2:请注意,从文件读取的数据将转换为bytearray类型.Bytearrays很有用,因为它们不像字符串那样是可变的,因此我们可以在必要时更新学生的分数,而不会低效地重新创建整个字符串.我选择将数据作为全局变量而不是常规参数传递,以避免必须通过几层函数传递它.
3:表达式foo = bar [x:y]是切片操作.这意味着"使用bar的字符x到y-1填充变量foo".我在init函数中使用它来初始化条目的名称和分数值.反过来也有效,如update_score方法中所示.
4:字符串或字节序格式化的另一个有用技巧是从C继承.由于每个总分条目必须占用10个字符以保持每个条目50个字符宽,使用字符串"%10i"%self.total以强制整数为10个字符宽度.
5:最后,只是快速记下我写的float(self.total)/ self.attempts.将整数除以整数会产生舍入的,不准确的整数.
该程序的下一部分是主play()函数,它调用find_entry():
def find_entry(name):
global data
for i in xrange(0,len(data),50):
if data[i:i + 30] == "%30s" % name:
return i
print "Name not found. Please check the spelling of your name."
print "If you spelled your name correctly please ask your teacher for help."
return None
Run Code Online (Sandbox Code Playgroud)
xrange函数首先出现在find_entry函数中.这提供了比while循环更可读,更pythonic的迭代方式."for x in xrange(0,len(data),50):"相当于."i = 0;虽然我<len(数据):... i + = 50.无论如何,它所做的只是一次通过50个字符的数据(因为每个条目是50个字符宽)并检查是否第一个30个字符与输入的名称匹配.(请注意,我使用数据[i:i + 30] =="%30s"%name.输入的名称必须最多30个字符,否则相等运算符将始终失败)如果成功则返回找到的条目的索引i,并返回None表示失败.
接下来我们将检查main play()函数:
def play():
global data
entry_num = None
while entry_num == None:
entry_num = find_entry(raw_input("Please enter your name and surname initial (e.g. William G): "))
student_entry = entry(entry_num)
score = 0
for i in xrange(10): #same as xrange(0,10,1)
score += question()
student_entry.update_score(score, entry_num)
print "Your score this time was %i / 10" % score
Run Code Online (Sandbox Code Playgroud)
我们在这里看到,在失败时find_entry返回None的事实得到了很好的利用.while循环意味着程序继续尝试,直到输入有效名称.另请注意raw_input函数的使用.这迫使Python将输入解释为字符串.这在下一个函数中更有意义,问题函数:
def question():
n1 = random.randint(1,9)
n2 = random.randint(1,9)
ops = {'+': operator.add, '-': operator.sub, 'x': operator.mul}
op = random.choice(ops.keys())
answer = raw_input("What is %i %s %i? " % (n1, op, n2))
try:
answer = int(answer)
if answer == ops[op](n1, n2):
print "Well done"
return 1
else:
print "incorrect, %i %s %i = %i" % (n1, op, n2, ops[op](n1,n2))
return 0
except ValueError:
print "'%s' does not appear to be a number." % answer
return 0
Run Code Online (Sandbox Code Playgroud)
我几乎只是逐字地复制了你的代码.唯一的主要区别是添加了try ... except语句和使用raw_input()代替input().input()的问题在于它非常不安全,因为Python可以自由地将其输入解释为任何类型,包括变量名,伪造输入可以破坏各种破坏.raw_input更安全:因为Python总是将输入解释为字符串,所以程序员有权解释输入并预测错误.在这种情况下,try ... except语句尝试将输入的字符串转换为带有int()的整数.int()如果无法将字符串转换为整数,则会引发ValueError,因此会在失败时执行除ValueError语句之外的操作,将失败传递给用户并允许程序恢复而不是崩溃.
既然已经处理了,我们可以继续讨论问题的实际有趣部分:对条目进行排序.
回到entry()类定义,有两个额外的魔术方法可以帮助创建排行榜,cmp和str:
class entry():
def __init__(self, i):
...
def __cmp__(self, other):
return self.average - other.average
def __str__(self):
return "%s | %2.2f" % (self.name, self.average)
def update_score(self, score, i):
...
Run Code Online (Sandbox Code Playgroud)
该CMP方法被调用时Python会需要比较两个条目.由于我们希望排行榜按平均分数排序,而不是按字母顺序排列或按总分排序,我们会返回此条目的平均值与其他条目的平均值的比较(Python将负值返回值解释为self <other和正值返回值为self >其他)
该海峡的方法是遇到STR(a)或打印(a)当什么叫做,其中a是一个条目类型.由于我们现在必须格式化两个数据,学生的名字和平均值,我们将两个变量放在%符号后面的元组中.%2.2f告诉Python打印一个小数点后只有2位数的浮点数; 任何其他数字都将被忽略.
所有的工作都写完了方法.因此编写leaderboard()函数变得微不足道:
def leaderboard():
global data
entries = []
for i in xrange(0, len(data),50):
entries += [entry(i)]
entries.sort()
entries.reverse()
for i in entries:
print i
Run Code Online (Sandbox Code Playgroud)
我们在这里看到了面向对象编程的美妙之处.xrange循环一遍又一遍地调用条目的init方法,用条目填充列表.这个有点复杂的函数安全地存放在入口类声明中,使排行榜功能保持干净且易于阅读.接下来,调用Python的内置列表对象的sort()方法,因此我们不需要重新发明轮子来编写另一个排序算法.这会调用我们的cmp函数,确保Python按照预期的平均分数正确地对条目进行排序.接下来,我们反转()列表,因为我们首先想要得分最高.同样,不需要自己编写这个函数; 它伴随着Python中的列表类型.最后,我们打印排行榜.这称为入口类 str函数,所以在排行榜函数本身中我们不需要担心在数据文件中找到正确的数据.干净整洁.
在这里做得非常多,现在所有要做的就是编写主循环:
while True:
print """\
Welcome to Simple-Maths Questions program
Please type one of the following:
Play | Leaderboard | Exit"""
command = raw_input()
command = command.upper()
if command in "PLAY":
play()
elif command in "LEADERBOARD":
leaderboard()
elif command in "EXIT":
break #You don't have to call quit() to terminate the program, just reach EOF.
elif command in "ADD NAME":
add_student()
else:
print "unknown command"
Run Code Online (Sandbox Code Playgroud)
同样,raw_input()比input()更安全.关键字将转换为全部大写,并且只查找输入是否在关键字中,因此输入不区分大小写,并且不需要键入完整关键字(这是调试的节省时间).还有隐藏的命令"ADD NAME"; 我不想解释这个功能.那就是,呃,几乎我只能说.
这是完成的代码:
import time, random, operator
directory_name = r"C:\Users\YOU\yourDirectoryHere\\"
#When you change the directory name, be SURE to include
#the double backslash at the end. (and keep the r)
datafile = open(directory_name + "scores.txt")
data = bytearray(datafile.read())
datafile.close()
backup_directory = directory_name
def backup():
#Note: I didn't explain this function. It just randomly
#makes backups of the data file in case something unexpected happens.
global data, backup_directory
datafile = open(backup_directory + str(int(time.time())) + '.txt', 'w')
datafile.write("Remove this timestamp, message, and newline before restoring this data %s\n" % time.asctime())
datafile.write(data)
datafile.close()
class entry():
def __init__(self, i):
#i is the location of the student's entry in the data.
global data
self.name = data[i:i + 30]
self.total = int(data[i + 30:i + 40])
self.attempts = int(data[i + 40:i + 49])
if self.attempts:
self.average = float(self.total) / self.attempts
else: self.average = 0.0
def __cmp__(self, other):
#BUGGED CODE return self.average - other.average
#FIXED BELOW
if self.average > other.average: return 1
elif self.average < other.average: return -1
else: return 0
def __str__(self):
return "%s | %2.2f" % (self.name, self.average)
def update_score(self, score, i):
global data, directory_name
self.total += score
self.attempts += 1
self.average = float(self.total) / self.attempts
data[i + 30: i + 40] = "%10i" % self.total
data[i + 40: i + 49] = "%9i" % self.attempts
datafile = open(directory_name + "scores.txt",'w')
datafile.write(data)
datafile.close()
if not random.randrange(5):
backup()
def find_entry(name):
global data
for i in xrange(0,len(data),50):
if data[i:i + 30] == "%30s" % name:
return i
print "Name not found. Please check the spelling of your name."
print "If you spelled your name correctly please ask your teacher for help."
return None
def question():
n1 = random.randint(1,9)
n2 = random.randint(1,9)
ops = {'+': operator.add, '-': operator.sub, 'x': operator.mul}
op = random.choice(ops.keys())
answer = raw_input("What is %i %s %i? " % (n1, op, n2))
try:
answer = int(answer)
if answer == ops[op](n1, n2):
print "Well done"
return 1
else:
print "incorrect, %i %s %i = %i" % (n1, op, n2, ops[op](n1,n2))
return 0
except:
print "'%s' does not appear to be a number." % answer
return 0
def play():
global data
entry_num = None
while entry_num == None:
entry_num = find_entry(raw_input("Please enter your name and surname initial (e.g. William G): "))
student_entry = entry(entry_num)
score = 0
for i in xrange(10):
score += question()
student_entry.update_score(score, entry_num)
print "Your score this time was %i / 10" % score
def leaderboard():
global data
entries = []
for i in xrange(0, len(data),50):
entries += [entry(i)]
entries.sort()
entries.reverse()
for i in entries:
print i
def add_student():
#This wasn't explained either. It just appends a properly formatted 50 character
#long entry to the bytearray, initialized with the inputted name and zeroes.
global data
while True:
student = raw_input("Add a student, press enter to quit: ")
if student:
cancel = raw_input("Confirm '%s'. Press enter to confirm, any key to cancel: " % student)
if not cancel:
data += bytearray("%30s%10i%9i\n" % (student,0,0))
else: print "cancelled"
else:
break
datafile = open(directory_name + "scores.txt",'w')
datafile.write(data)
datafile.close()
while True:
print """\
Welcome to Simple-Maths Questions program
Please type one of the following:
Play | Leaderboard | Exit"""
command = raw_input()
command = command.upper()
if command in "PLAY":
play()
elif command in "LEADERBOARD":
leaderboard()
elif command in "EXIT":
break
elif command in "ADD NAME":
add_student()
else:
print "unknown command"
Run Code Online (Sandbox Code Playgroud)
注意:我没有详尽地对此进行错误测试,因此请自行进行一些测试以确保其有效.
注2:修改后的cmp功能; .sort与近距离浮动的角色表现不正常.数字.
| 归档时间: |
|
| 查看次数: |
1986 次 |
| 最近记录: |