作为学校信息和通信技术课程项目的一部分,我们班被要求按照既定的标准编写一个测试程序。第一部分是一个程序,通过向用户询问球员姓名、年龄、电子邮件和性别等细节来处理注册过程。
import getpass
import os
import pickle
import time
nope = "I didn't quite get that. Mind trying again?"
yn = {'y': True, 'n': False}
if os.path.exists('players'):
with open('players', 'rb') as f:
if os.stat('players').st_size == 0:
accounts = {}
else:
accounts = pickle.loads(f.read())
else:
f = open('players', 'wb')
f.close()
accounts = {}
class Player(object):
def __init__(self):
self.name = ""
self.email = ""
self.password = ""
self.age = ""
self.gender = ""
def write(self):
accounts[p.name] = [p.email, p.password, p.age, p.gender]
with open('players', 'wb') as f:
pickle.dump(accounts, f)
p = Player()
def askChoices(question, options, errorMessage):
while True:
answer = str(input(question)).lower()
if answer in options:
return options[answer]
else:
print(errorMessage)
def register():
print("\nWelcome, new player!")
while True:
p.name = input("What is your name? ")
if p.name in accounts:
print("Name already taken. Please try again.")
else:
break
isEmailValid = False
while not isEmailValid:
p.email = str(input("What is your email address? "))
if '@' in p.email and '.' in p.email:
isEmailValid == True
break
else:
print(nope, "(Input must contain '@' and '.')")
p.password = getpass.getpass('Please enter a password. ')
while True:
try:
p.age = int(input("How old are you? "))
break
except ValueError:
print(nope, "(Input must be an integer)")
p.gender = askChoices("What gender are you? ('m' for male, 'f' for female, 'o' for other) ", # it was not my idea to add the 'other' option
{'m': "Male", 'f': "Female", 'o': "Other"},
nope)
print("\nName:", p.name,
"\nEmail:", p.email,
"\nPassword:", ('*' * len(p.password)),
"\nAge:", p.age,
"\nGender:", p.gender)
correct = askChoices("Is this information correct? (y/n) ",
yn, nope)
if correct:
print("\nRegistration successful!")
p.write()
login()
elif not correct:
tryAgain = askChoices("\nWould you like to try again? (y/n) ",
yn, nope)
if tryAgain:
register()
else:
main()
def login():
while True:
name = input("What is your player name? ")
if name in accounts:
pw = getpass.getpass("Please enter your password. ")
if pw == accounts[name][1]:
print(accounts[name]) # debug placeholder until quiz functionality is added
break
else:
print("Account not found.")
registerNew = askChoices("Would you like to register a new account? (y/n) ",
yn, nope)
if registerNew:
register()
break
else:
pass
def main():
if os.stat('players').st_size == 0:
register()
else:
player = askChoices("Would you like to REGISTER or LOG IN? ",
{'register': True, 'log in': False, 'login': False},
nope)
if player:
register()
else:
login()
print("\nShutting down...")
time.sleep(3)
if __name__ == '__main__':
main()大量不同的参考资料和网页被用来实现程序中的一些特性,例如几个堆栈溢出帖子,甚至来自一般Arqade聊天室的常客的帮助。
回想起来,这与课堂上给出的原始示例有很大的偏离(并且有充分的理由;考试板设置了这个任务有很差的编码标准)。
我希望我能把这作为一个全面工作的智力游戏应用程序在某个时候。
发布于 2015-12-01 21:47:19
他们太难理了。
将p = Player()从全局范围移到register()的开头
在Player类中使用self:
def write(self):
accounts[self.name] = [self.email, self.password, self.age, self.gender]
with open('players', 'wb') as f:
pickle.dump(accounts, f)以下是实现细节:
print("\nName:", p.name,
"\nEmail:", p.email,
"\nPassword:", ('*' * len(p.password)),
"\nAge:", p.age,
"\nGender:", p.gender)更清楚的是:
print(p)要允许这样做,您应该在__str__类中实现一个Player方法。
所有用于输入验证的代码都会引起代码的噪音,而且很难遵循代码的高级别流程,编写general_input函数可能更好:
p.name = general_input(
"What is your name? ",
validation = lambda name: name not in accounts,
error_message = "Name already taken. Please try again."
)
p.email = general_input(
"What is your e-mail? ",
validation = lambda email: '@' in email or '.' in email,
error_message = "(Input must contain '@' and '.')"
)
p.password = getpass.getpass('Please enter a password. ')
p.age = general_input(
"How old are you? ",
validation = lambda age: all(i in '1234567890' for i in age)
error_message = 'Age should be a positive integer'
)register可以调用登录,login可以调用寄存器,它感觉不干净.用户交互菜单功能可以自由调用,只是美化而已。
我建议只有main可以给login和register打电话,他们可能不会打电话给对方,而只会给main回电话。它将简化代码,甚至可能使程序在/如果增长时使用更可预测(当您不确定如何执行操作时,只需返回主菜单,您就会知道如何使用)
例如,当您使login无条件地跳回main时,它变得多么简单:
def login():
while True:
name = input("What is your player name? ")
if name not in accounts:
print("Account not found.")
main()
pw = getpass.getpass("Please enter your password. ")
if pw == accounts[name][1]:
print(accounts[name]) # debug placeholder until quiz functionality is added不要为编写使您的代码更加可读性更高的小函数感到难过,它们是好的!例如:
os.stat('players').st_size == 0理解起来需要一些时间,阅读并不流利,但如果我定义:
def is_file_empty(filename):
return os.stat('players').st_size == 0读取is_file_empty('players')是即时的。
同样的情况也适用于:
f = open('players', 'wb')
f.close()打开一个文件再关闭一次是很奇怪的..。一个适当命名的小函数将使您的意图更加清晰。
发布于 2015-12-01 21:53:01
创建一个空的Player然后在外部填充它并不是一个好主意--我倾向于使用类方法作为替代构造函数,将所有与播放器相关的逻辑保持在一个地方,例如:
class Player(object):
def __init__(self, name, email, password, age, gender):
self.name = name
self.email = email
self.password = password
self.age = age
self.gender = gender
@classmethod
def from_input(cls):
# ... take user input
return cls(name, email, password, age, gender)然后你可以使用:
p = Player.from_input()尽量减少信息的重复。例如:
askChoices("What gender are you? ('m' for male, 'f' for female, 'o' for other) ",
{'m': "Male", 'f': "Female", 'o': "Other"},
nope)在第一个和第二个参数中有相同的信息;为什么不使用这些选项构建字符串呢?您还混合了单引号(')和双引号(");尽量保持一致。您可以将nope (它是一个常量,因此应该在UPPERCASE中)设置为默认参数值,以避免每次传递它。这样的话,一个更整洁的电话将是:
askChoices("What gender are you?",
{"m": "Male", "f": "Female", "o": "Other"})在你的控制流程中保持一致。比较:
while True:
p.name = input("What is your name? ")
if p.name in accounts:
print("Name already taken. Please try again.")
else:
break通过以下方式:
isEmailValid = False
while not isEmailValid:
p.email = str(input("What is your email address? "))
if '@' in p.email and '.' in p.email:
isEmailValid == True
break
else:
print(nope, "(Input must contain '@' and '.')")第二个变量有一个明显不必要的变量,因为不管怎么说,变量一发生变化,您就会break。此外,您的验证允许'@.'作为电子邮件地址。
https://codereview.stackexchange.com/questions/112494
复制相似问题