python怎么显示分数,Python:如何将其他信息添加到包含名称和分数的文本文件中?...

i'm working on a simple math programme for a school maths class where kids writes their name in and then programme asks 10 easy maths questions. After that, kids' names and scores would be stored in different text file, according to their classes.

Here are my questions.

At the moment, their names are stored in this format: William W - 10

so first part is their name and last part is their score. I'm not sure if there's better way of doing this.

How do i make it so that if same kid does the test again, it would average with previous score?

Also, how do i make it so that the kid's score will over write if kid gets higher score? (which would be different text file to one with average score)

i'm trying to make a ranking board where kid with highest point shows up in the top and lowest on the bottom.

When shown on leader board, i want it to look like this.

William W - 10

Nikko E - 9

etc. i have searched everywhere and i don't seem to find a answer. It would be nice if someone could be share their knowledge on this. Thank you

Here is my unfinished code. I'm new to python XD

I need to add extra things to the leader board: average, highest and alphabetical order with each student's highest score for the tests.

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

解决方案

Well, I planned to just write the relevant parts of the program but I ended up writing an entire working program instead. Since I don't know how much Python you know I'm just going to explain the entire program; sorry if I have a somewhat pedantic and/or pompous air. Feel free to just skip to the bottom and copy the code.

The code I wrote demonstrates 4 Python idioms: object oriented design, bytearray/string slicing and formatting, the xrange() function, and error catching.

I think that csv would not be a good angle to approach the problem with. I opted to store the data as a text table. Each line takes the following form:

30 characters name | 10 characters total score | 9 characters attempts | 1 newline

The benefit of this is that each line is exactly 50 characters wide. So, unlike the csv approach, we don't have to scan through for commas to find relevent data; we just go through the data in increments of 50 characters. This makes for much more readable code, and it's not like text storage space is at a premium these days.

Example: Chell R earned 42 points in 5 attempts becomes:

" Chell R 42 5\n"

Since we're going to be sorting this data later, it would be advantageous to store both the student's name and his/her score in an object. This object makes sorting much simpler; because the name is permanently attatched to the score, we don't have to worry about making sure the name data follows the score data as the data is sorted. It just happens implicitly.

This is the first portion of the code:

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

The method defined under init is known as a magic method. This means that unlike the regular method update_score, you don't call the method yourself. It's automatically called by Python; in this case it's called when an entry object is created.

Five things to point out:

1: Just to quickly explain a syntax annoyance, each method must have self as the first argument and each variable stored in the object needs a self. prefix.

2: Notice that the data read from the file is converted to the bytearray type. Bytearrays are useful because unlike strings they are mutable, so we can update students' scores when neccessary without inefficiently recreating the entire string. I opted to pass data as a global variable instead of a regular argument in order to avoid having to pass it through several layers of functions.

3: The expression foo = bar[x:y] is a slice operation. This means "Fill variable foo with characters x through y-1 of bar". I used it in the init function in order to initialize the entry's name and score values. The reverse also works, as seen in the update_score method.

4: Another useful trick for string or bytearray formatting inherits from C. Since each total score entry must take up 10 characters in order to keep each entry 50 characters wide, use the string "%10i" % self.total in order to force the interger to a 10 character width.

5: Finally, just a quick note that I wrote float(self.total) / self.attempts. Dividing an integer by an integer results in a rounded, inaccurate integer.

The next part of the program is the main play() function, which calls 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

The xrange function is first seen in the find_entry function. This provides a much more readable, pythonic way to iterate than while loops. "for i in xrange(0,len(data),50):" is equivalent to. "i = 0; While i < len(data): ... i += 50. Anyway, all it does is go through the data 50 characters at a time (since each entry is 50 characters wide) and check if the first 30 characters match up with the inputted name. (Note that I use data[i:i + 30] == "%30s" % name. The inputted name must be pumped up to 30 characters, otherwise the equality operator will always fail). It returns the index i of the found entry if successful and returns None to indicate failure.

Next we'll examine the main play() function:

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

We see here that the fact that find_entry returns None upon failure is put to good use. The while loop means the program keeps on trying until a valid name is inputted. Also note the use of the raw_input function. This forces Python to interpret the input as a string. This'll make more sense in the next function, the question function:

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

I pretty much just copied your code verbatim for this part. The only major difference is the addition of the try... except statement and the use of raw_input() in lieu of input(). The issue with input() is that it's pretty unsafe, since Python is free to interpret its input as any type, including a variable name, a bogus input can wreck all sorts of havoc. raw_input is much safer: since Python always interprets the input as a string the programmer gets the right to interpret the input and anticipate errors. In this case, the try... except statement tries to convert the inputted string to an integer with int(). int() raises ValueError if it fails to convert the string to an integer, so an except ValueError statement would execute instead upon failure, communicating the failure to the user and allowing the program to recover instead of crashing.

Now that that's dealt with, we can move on to the actually interesting part of the question: sorting the entries.

Going back to the entry() class definition, there are two additional magic methods which facilitate leaderboard creation, cmp and 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):

...

The cmp method is called when Python needs to compare two entries. Since we want the leaderboards to be sorted by average score, not alphabetically or by total score, we return a comparison of this entry's average with the other entry's average (Python interprets a negative return value as self < other and a positive return value as self > other)

The str method is what's called when str(a) or print(a) is encountered, where a is an entry type. Since we now must format two pieces of data, the student's name and average, we put the two variables in a tuple after the % sign. The %2.2f tells Python to print a floating point number with only 2 digit after the decimal point; any further digits are ignored.

All the work was done writing the methods. Writing the leaderboard() function thus becomes a trivial affair:

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

We see the beauty of object oriented programming at work here. The xrange loop calls the entry's init method over and over again, populating the list with entries. This somewhat complex function is safely stowed away in the entry class declaration, keeping the leaderboard function clean and easy to read. Next, the sort() method of Python's built in list object is called, so we don't need to reinvent the wheel writing another sorting algorithm. This calls our cmp function, ensuring that Python will correctly sort the entries by average score as intended. Next, we reverse() the list, since we want the highest scores first. Again, no need to write this function ourselves; it comes along with the list type in Python. Finally, we print the leaderboard. This calls the entry class's str function, so in the leaderboard function itself we don't need to worry about finding the correct data in the data file. Nice and clean.

Pretty much done here, now all there's to do is write the main loop:

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"

Again, raw_input() is safer than input(). The keyword is converted to all caps and only finds whether the input is in the keyword, so the input is not case sensitive and there's no need to type the full keyword (This was a timesaver for debugging). Also, there's the hidden command "ADD NAME"; I didn't feel like explaining that function. And that's, uh, pretty much all I have to say.

And here is the completed code:

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"

Note: I haven't exactly exhaustively bug tested this, so please do a bit of testing yourself to make sure this works.

NOTE 2: Amended cmp function; .sort behaved erratically with close floating pt. numbers.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值