1

I was answering a past paper question and this was my answer for a class definition. My Code:

class ExaminationPaper:
    def __init__(self,Centre_number,Candidate_number):
        y = str(Centre_number) + str(Candidate_number)
        self.PaperID = y
        self.Grade = "Fail"
        self.FinalMark = 0

    def SetGrade(self,Dist,Merit,Pass):
        if self.FinalMark >= Dist:
            self.Grade = "Distinction"
        elif self.FinalMark >= Merit:
            self.Grade = "Merit"
        elif self.FinalMark >= Pass:
            self.Grade = "Pass"
        else:
            self.Grade = "Failure :("
    def SetFinalMark(self,Mark):
        if Mark >= 0 and Mark <= 90:
            self.FinalMark = Mark
            return(True)
        else:
            return(False)
    def GetFinalMark(self):
        return(self.FinalMark)
Chomba = ExaminationPaper(559,9022)
Chomba.SetFinalMark(80)

This is the answer that the mark scheme provided me with:

class ExaminationPaper:
    def __init__(self,Centre_number,Candidate_number):
        y = str(Centre_number) + str(Candidate_number)
        self.PaperID = y
        self.Grade = "Fail"
        self.FinalMark = 0

    def SetGrade(Dist,Merit,Pass):
        if FinalMark >= Dist:
            Grade = "Distinction"
        elif FinalMark >= Merit:
            Grade = "Merit"
        elif FinalMark >= Pass:
            Grade = "Pass"
        else:
            Grade = "Failure :("
    def SetFinalMark(Mark):
        if Mark >= 0 and Mark <= 90:
            FinalMark = Mark
            return(True)
        else:
            return(False)
    def GetFinalMark():
        return(FinalMark)
Chomba = ExaminationPaper(559,9022)
Chomba.SetFinalMark(80)

When I tried out the mark scheme code it gave me an error for each of the methods in the class stating that the number of positional arguments used were more than the methods take for each, after I commented them out one by one.

It seems to work with my code but the mark scheme specifically states that in the the GetFinalMark method there should be no presence of a parameter for example.

In the exam would I be better of using my own code or would I rather work the way the mark scheme says and disregard using the self parameter to allow the class access to variables? or is there another issue causing the positional argument error?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You are not showing us what errors you are getting, or how you call the function. Is this a method on a class? Then at least make sure your code here also uses a class. In short: we need to be able to *reproduce* your problem, with a good sense of what the inputs are and what your expected output is. – Martijn Pieters Nov 06 '19 at 12:53
  • Edited it, is this better? – BobbyBigProgramming Nov 06 '19 at 17:42
  • 1
    Make sure that the indentation is right, and that copying-and-pasting the code out of your question into an interpreter creates the same problem you're asking about without needing to make any changes. It might be easier to use triple-backticks to start and end code segments rather than four-space-indents. Right now, your class definitions are showing up at the same indentation layer as the methods that presumably are intended to be inside them. – Charles Duffy Nov 06 '19 at 17:44
  • By "the mark scheme code", do you refer to any of the two variants, or to the second variant specifically? As far as I understood, you wrote the first variant of the code, while the second variant was provided to you. And you are asking why the second variant (which you did not write yourself) does not work? Is my understanding correct? – mkrieger1 Nov 07 '19 at 13:04
  • Is this the Cambridge International A-level computer science 2019 exam (9608), paper #4? I can see a few more issues to pick at in that one. :-) – Martijn Pieters Nov 07 '19 at 13:37

1 Answers1

4

The 'answer' in the mark scheme is wrong. It is broken. It doesn't work. In other words, your code is correct. You should use your code, not the mark scheme. To me, it looks as if someone without actual Python programming experience wrote that up and didn't verify if it actually would run. I would not expect future exams to make such blatant errors, and if they did you would have plenty of ground to challenge the results.

For functions defined on a class, when you use the function name as an attribute on an instance and call it, Python will pass in the instance as the first positional argument to the function, automatically. We only use the name self for that argument by convention.

So for the SetFinalMark() method, for example, what will happen is that Python passes in the instance as the first argument, and so that's assigned to the name Mark. You can call it as Chomba.SetFinalMark(), at which point Mark is set to reference the instance you named Chomba. The method then will raise a different exception, because the ExamResult() class doesn't support comparisons so Mark >= 0 will raise a TypeError exception (TypeError: '>=' not supported between instances of 'ExaminationPaper' and 'int').

And that also means that the GetFinalMark() method will always fail, because it doesn't take any parameters (TypeError: GetFinalMark() takes 0 positional arguments but 1 was given).

There are other issues with the mark scheme code. Because there is no self parameter the implementation of SetGrade and SetFinalMark and GetFinalMark are instead treating Grade and FinalMark as global variables, and so will raise NameError exceptions if you didn't first run into all those TypeError exceptions. And in Python, the return statement is not a function. While return(<expression) works, it surely will confuse beginning programmers and not help when making a distinction between expressions and statements. The (..) parentheses are really part of the expression and would be redundant if there was a space after return; you should really use return <expression> without parentheses.

Finally, and this is an issue with the assignment, not just with the mark scheme, the whole example goes against the Python style guide naming conventions, which are there to help make it easy to spot classes versus other names. Ideally, only ExaminationPaper should use CamelCase naming, everything else should use snake_case (lower-cased words joined by underscores), including all attribute names, parameters, and method names.

I had a look around, and this looks like the UK May/June 2019 A-level Computer Science exam past paper (#4) (available at several sites, this is the mark scheme one one such site, to go with this past paper). If so, then it looks like the authors got their conventions mixed up. The paper allows for the student to choose between Python, Pascal and Visual Basic, and their code looks like the author applied Visual Basic principles to Python code. In Visual Basic, there is no self parameter. The choice to allow for multiple implementation languages then also limited how much room there is for applying the normal Python naming conventions.

But if Python conventions don't apply, then I also note that they are also using attribute names as __FinalMark in the marking scheme, showing a lack of understanding of how Python treats encapsulation. I'll not go into detail here, but I've written an answer before that explains why using double-underscore names is wrong here. That they then completely failed to use the correct attribute names makes their version of the 'correct' implementation for the three methods you were asked to write even more wrong.

If you wanted to implement everything 'correctly' according to the normal Python conventions and style guide (including using properties and a better name for how determining the grade based on 3 thresholds is handled), you'd end up with:

class ExaminationPaper:
    def __init__(self, centre_number, candidate_number):
        self.paper_id = f"{centre_number}{candidate_number}"
        self.grade = "Fail"
        self._final_mark = 0

    def determine_grade(self, dist_mark, merit_mark, pass_mark):
        if self._final_mark >= dist_mark:
            self.grade = "Distinction"
        elif self._final_mark >= merit_mark:
            self.grade = "Merit"
        elif self._final_mark >= pass_mark:
            self.grade = "Pass"
        else:
            self.grade = "Fail"

    @property
    def final_mark(self):
        return self._final_mark

    @final_mark.setter
    def final_mark(self, mark):
        if not (0 <= mark <= 90):
            raise ValueError(f"Final mark {mark} not in range 0-90")
        self._final_mark = mark


chomba = ExaminationPaper(559, 9022)
try:
    chomba.final_mark = 80
except ValueError as e:
    print("Could not set the final mark:", e)
else:
    chomba.determine_grade(80, 70, 55)
    print(f"For {chomba.paper_id}, the student was given a {chomba.grade}")

I fully admit that that's well out of reach of an A-level paper, however. :-) And given the mark scheme, you wouldn't even get your marks unless the person marking the paper actually knew enough about Python programming to recognize why the above deviates from the scheme.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343