4

I've read a number of SO threads about why Python doesn't have truly private variables, and I understand it for most applications.

Here's my problem: I'm creating a class project. In this class project, students design an agent that takes a multiple choice test. We want to be able to grade the agents' answers immediately so that the agents can learn from their wrong answers to previous problems. So, we need the correct answer to each problem to be stored in the program. Students are running these projects on their local machine, so they can see all the tester's code. They can't modify it -- we overwrite any of their changes to the tester code when they turn it in. They just turn in a new file representing their agent.

In Java, this is straightforward. In the Problem class, there is a private correctAnswer variable. correctAnswer always stores the correct answer to the problem. Agents can only read correctAnswer through a checkAnswer method, and in order to call checkAnswer, agents must actually give an answer to the question that cannot be changed later.

I need to recreate this behavior in Python, and so far I'm at a loss. It seems that no matter where in the program correctAnswer is stored, agents can access it -- I'm familiar with the underscore convention, but in this problem, I need agents to be incapable of accessing the right answer. The only thing I can think of is to name correctAnswer something different when we're testing students' code so that their agents can't anticipate what it will be called, but that's an inelegant solution.

Any suggestions? It's acceptable for agents to be able to read correctAnswer so long as we can detect when they read it (so we can set the 'agent's answer' variable to read-only after that... although then I'll need a way to do that, too, oof).

3
  • 4
    So is the goal here to prevent students from cheating by looking at the correctAnswer variable? Couldn't you determine that just by looking at the code they turn in? Commented Aug 5, 2014 at 2:39
  • 1
    In Java, if you relied on private to prevent access, you would be vulnerable to reflective field access such as Problem.class.getDeclaredField("correctAnswer").get(problem). Commented Aug 5, 2014 at 4:19
  • @dano There are hundreds of students and one grader. Inspecting each student's code isn't really an option. We could theoretically automatically search for the variable name in students' code, but that isn't exactly ideal. Commented Aug 19, 2014 at 23:06

5 Answers 5

5

Change the rules. Don't store the correct answer in the program. Store the correct answer on a secured server:

def checkAnswer(studentAnswer):
    response = requests.get('http://teacher-server.example.edu/checkAnswer?%s'%studentAnswer)
    return response.text == 'Yes'
Sign up to request clarification or add additional context in comments.

1 Comment

Many of our students are overseas with intermittent internet access. This wouldn't be acceptable.
3

You can change the name of the the correctAnswer attribute in the code that you overwrite the tester with. This will instantly break all solutions that rely on the name of correctAnswer.

Furthermore, you could run both versions of the tester and diff the outputs. If there is a difference, then their code relies on the attribute name in some way.

1 Comment

This is basically what we're going with. It's not ideal, but it's good enough. Thanks!
1

Look into overwriting the class's

__getattr__

method. This is what is called when the class attributes are referenced. You can control what the class does and does not return when statements like:

x = myClass.myAttribute

are used.

2 Comments

I think this is the best answer that duplicates the questioner's desire for privacy, but in my opinion private methods are not a good way to hide information unless you just want implicit hiding.
The student can always just bypass __getattr__ and look wherever __getattr__ would have looked for the data, so this doesn't really solve the problem.
1

Nothing you do will ever guarantee that student's Python code can't access or modify any object created by your Python code running in the same interpreter. Things like overriding __getattr__ can be bypassed by using object.__getattribute__. Your students can "monkey patch" the tester code to do what they want without having to modifying the source code. Unlike Java, Python makes no pretense at being a secure language. You'll either need to inspect the code or move the testers code to an other process.

The multiprocessing module may help you do this. Something like:

import multiprocessing as mp
import multiprocessing.managers

class Problem(object):
    def __init__(self, correctAnswer):
        self.answer = None
        self.correctAnswer = correctAnswer

    def checkAnswer(self, answer):
        if self.answer == None:
            self.answer = answer
        return self.answer == self.correctAnswer

    def __getstate__(self):
        raise ValueError("don't pickle me");

class Problem_proxy(mp.managers.BaseProxy):
    def checkAnswer(self, answer):
        return self._callmethod("checkAnswer", (answer,))

class mymanager(mp.managers.BaseManager):
    pass

mymanager.register("Problem", Problem, Problem_proxy)

def other_process(problems):
    # uncommenting either of these should cause an exception
    # print problems[0].correctAnswer
    # print problems[0]._getvalue().correctAnswer

    import agent       # module provide by the student
    agent.answer_problems(problems)

def main():
    manager = mymanager()
    manager.start()

    meaningoflife = manager.Problem(42)
    problems = [meaningoflife]

    proc = mp.Process(target = other_process,
              args = ([meaningoflife],))
    proc.start()
    proc.join()


if __name__ == "__main__":
    main()

Even with this there's still a bunch of holes. The student's code could just read in your tester code module an inspect it for the answers, for example. To be really secure you'd have to set up some sort of client/server model with the tester code running on different user's account.

Comments

0

On the tester, you can define correctAnswer as a property, and raise an exception if anything other than your known code calls it.

This can be achieved via introspection, but isn't pretty:

import inspect

class Problem(object):
    @property
    def correctAnswer(self):
        frm = inspect.currentframe()
        caller = inspect.getouterframes(frm)[0]

        _,_,_,name,_,_ = caller

        if name != YOUR_TESTING_FUNCTION:
            raise BadStudentError()

        return self._correctAnswer

Note that this does mean that you need to define _correctAnswer, but that can only be in your version of the code (you can use .correctAnswer in their version; the beauty of using a @property is that this replacement is transparent to the callee).


Of course, while the above solution works in that it allows you to tell when students have tried to access the correct answer, it's a bit hacky.

However, I personally feel that it would be much more elegant to check the student's answers on a server. By doing this, you control the entire process, and can easily ensure that the agent's answer is not changed after submission.

If writing a simple http query is beyond the students, you could include a helper method (checkAnswer or similar) which performed this process for them.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.