2

可能的重复:
Python 中的“Least Astonishment”:可变默认参数

好的,所以我正在尝试学习 Python。几个学期前,我有一个朋友上过 Python 课程的介绍,他把他所有的旧作业都给了我。我有一个特别烦人的问题,我认为应该很简单,但似乎无法弄清楚我的问题出在哪里。看看大家怎么想的。

该程序应该从名为 grades.txt 的文件中读取。这是文件:

2
John Doe
82
100
57
0
Jane Smith
91
12
45
81
0

这个文件的格式是:第一行是学生人数。然后是学生的名字和他们的成绩。零表示学生成绩列表的结束。我知道,我知道……这样做没有多大意义,但这就是它的阅读方式。

无论如何,这是我到目前为止为它编写的代码......

#!/usr/bin/env python

class students():
        def __init__(self, fname='', lname='', grades=[]):
        self.firstName = fname
        self.lastName = lname
        self.gradeBook = grades

def lineCheck(lineText):
     try:
         int(lineText)
         return True
     except ValueError:
         return False

inFile = "grades.txt"
outFile = "summary.txt"

count = 0
numStudents = 0

studentList = []
check = False

with open(inFile, "r") as file: 
    for line in file:
        if(count == 0):
            numStudents = line.strip()
        else:
            lineRead = line.strip()
            check = lineCheck(lineRead)
            if(check == False):
                studentName = lineRead.split()
                fName = studentName[0]
                lName = studentName[1]
                temp = students(fName, lName)
            else:
                if(lineRead != '0'):
                    temp.gradeBook.append(lineRead)
                elif(lineRead == '0'):
                    studentList.append(temp)

        count += 1
file.close()

for student in studentList:
    print student.firstName + " " + student.lastName
    print student.gradeBook

使用此代码,我的预期输出是在最终 for 循环中的程序末尾。我希望看到这样的事情:

John Doe
['82', '100', '57']
Jane Smith
['91', '12', '45', '81']

但是,我得到的输出是这样的:

John Doe
['82', '100', '57', '91', '12', '45', '81']
Jane Smith
['82', '100', '57', '91', '12', '45', '81']

我已经盯着这个太久了,我觉得这很简单。但由于我是 python 新手,还没有完全习惯它的所有细微差别,也许有经验的人可以找出这里发生的事情。我真的很感激你能给我的任何帮助。谢谢。

4

2 回答 2

3

您的实际问题是您将[]其用作默认参数。默认参数创建一次并“附加”到函数中,因此每次创建新的学生对象时,都会重复使用相同的成绩列表。你想要这样的东西:

def __init__(..., grades=None):
    if grades is None:
        self.gradeBook = []
    else:
        self.gradeBook = grades

现在,请允许我给你一些其他的批评。:)

看起来您的代码中包含空格和制表符的组合;在像 Python 这样的语言中,空格很重要,这真的很糟糕。我不知道你使用的是什么编辑器,但你应该知道如何配置它,所以按下Tab总是会插入四个空格。

其余的内容不太重要,但它会帮助您与其他 Python 程序员相处,并且可能会帮助您的代码变得更简单。


您可以在几个地方执行此操作:

if(check == 0):

括号是不必要的。

if check == 0:

您有以下内容:

if(lineRead != '0'):
    ...
elif(lineRead == '0'):
    ...

但其中只有一个可能是真的——它要么相等,要么不相等——所以你不妨将 替换elif ...else。但这会导致双重否定(它不是!=......),所以让我们交换分支。

if lineRead == '0':
    ...
else:
    ...

check = lineCheck(lineRead)
if(check == False):

您只使用该check变量一次,因此不需要它。此外,更常见的是避免与TrueFalse直接比较:

if not lineCheck(lineRead):

不过,这读起来不太好。执行动作的函数最好命名为动词,而验证某些东西的函数(如这个)在命名时听起来不错is_whatever。变量,你想像你用英语谈论它一样命名;您不会经常谈论“读取行”,但您可能会谈论“读取行”,甚至只是“行”。

哦,还有Python 风格指南,PEP 8建议使用下划线而不是 camelCase。

if not is_numeric(line):

类名通常用大写骆驼大写,并且应该是单数,因为它们描述了一种事物。(“你有什么样的宠物?”“一只猫。”)而且你应该总是继承自object,否则你会得到一个“旧式类”,它来自 Python 的早期,有点笨拙。

虽然我在做,但学生没有名字没有多大意义。尽管您最好不要尝试将其拆分为 first 和 last

class Student(object):
    def __init__(self, name, grades=None):
        ...

现在没关系,但稍后将所有“主”代码放在一个调用的函数中main然后运行它会很有帮助。这样,您可以main从其他代码(例如测试)导入,而无需立即运行所有代码。

if __name__ == '__main__':
    main()

__name__只是当前“模块”的名称,'__main__'如果您直接使用python file.py.


经过一番重重的吹毛求疵,我最终得到了这个:

class Student(object):
    def __init__(self, name, grades=None):
        self.name = name
        if grades is None:
            self.gradebook = []
        else:
            self.gradebook = grades

def is_numeric(s):
    try:
        int(s)
        return True
    except ValueError:
        return False

def main(infile, outfile):
    count = 0
    num_students = 0

    students = []

    with open(infile, "r") as f:
        for line in f:
            line = line.strip()

            if count == 0:
                # This is the first line
                num_students = int(line)
            elif not is_numeric(line):
                # Non-numbers are names of new students
                cur_student = Student(line)
            elif line == '0':
                # Zero marks the end of a student
                students.append(cur_student)
            else:
                cur_student.gradebook.append(line)

            count += 1

    for student in students:
        print student.name
        print student.gradebook

if __name__ == '__main__':
    main("grades.txt", "summary.txt")

我做了一些上面没有提到的小事;我希望他们有意义。

  • file是内置函数的名称。Python 将允许您使用该名称,但如果您稍后尝试使用内置名称,您会感到惊讶。我将其替换为f,这是引用仅打开一小段时间的文件的常用方法。

  • 很难记住是什么temp意思!尽量不要给变量这样的名字。我将其更改为cur_student,其中“cur”是“current”的常见缩写。

  • 你不需要f.close();该with块为您完成。事实上,这就是 的全部意义所在with

  • line.strip()不管发生什么你都会打电话,所以我把它作为循环中的第一件事。这允许所有ifs 合并为一个,从而更容易跟踪正在发生的事情和时间。

  • 我添加了一些快速评论,因为正如您所指出的,这是一种非常奇怪的文件格式。

如果这意味着什么的话,这更接近我的写作方式。

总是很高兴看到有人新进入编程领域!希望你喜欢 Python :)

于 2013-01-17T07:50:19.353 回答
2

采用

class students():
    def __init__(self, fname='', lname='', grades=None):
        self.firstName = fname
        self.lastName = lname
        self.gradeBook = [] if grades is None else grades

问题是默认参数[]

于 2013-01-17T07:34:13.460 回答