在Python中重新创建“私有”类变量

时间:2014-08-05 02:34:58

标签: python private

我已经阅读了很多关于为什么Python没有真正的私有变量的SO线程,而且我对大多数应用程序都理解它。

这是我的问题:我正在创建一个课程项目。在这个课程项目中,学生设计一个需要多项选择测试的代理。我们希望能够对代理商进行评级'立即回答,以便代理人可以从错误的答案中学习以前的问题。因此,我们需要将每个问题的正确答案存储在程序中。学生在本地计算机上运行这些项目,因此他们可以看到所有测试人员的代码。他们无法对其进行修改 - 当我们将测试人员代码更改时,我们会覆盖他们对测试人员代码的任何更改。他们只需输入代表其代理人的新文件。

在Java中,这很简单。在Problem类中,有一个私有的correctAnswer变量。 correctAnswer始终存储问题的正确答案。代理只能通过checkAnswer方法读取correctAnswer,并且为了调用checkAnswer,代理必须实际回答以后无法更改的问题。

我需要在Python中重新创建这种行为,到目前为止,我不知所措。似乎无论程序在哪里存储了correctAnswer,代理都可以访问它 - 我熟悉下划线约定,但在这个问题中,我需要代理无法访问正确的答案。我唯一能想到的就是在我们对学生进行测试时说出正确的答案。代码,以便他们的代理人无法预测它将被称为什么,但这是一个不优雅的解决方案。

有什么建议吗?只要我们能够检测到他们何时阅读它,我们就可以将代理人能够读取正确的答案(因此我们可以将代理人的答案变量设置为只读后) ......虽然那时我也需要一种方法来做到这一点,oof)。

5 个答案:

答案 0 :(得分:5)

更改规则。不要在程序中存储正确的答案。将正确答案存储在安全的服务器上:

def checkAnswer(studentAnswer):
    response = requests.get('http://teacher-server.example.edu/checkAnswer?%s'%studentAnswer)
    return response.text == 'Yes'

答案 1 :(得分:3)

您可以在覆盖测试人员的代码中更改correctAnswer属性的名称。这将立即打破依赖于correctAnswer名称的所有解决方案。

此外,您可以运行两个版本的测试仪并对输出进行差异化。如果存在差异,那么他们的代码会以某种方式依赖于属性名称。

答案 2 :(得分:1)

考虑覆盖班级的

__getattr__

方法。这是引用类属性时调用的内容。您可以控制类在以下语句中执行和不返回的内容:

x = myClass.myAttribute

被使用。

答案 3 :(得分:1)

您所做的任何事都不能保证学生的Python代码无法访问或修改在同一个解释器中运行的Python代码创建的任何对象。使用__getattr__可以绕过诸如覆盖object.__getattribute__之类的内容。您的学生可以“修补”测试人员代码以执行他们想要的操作而无需修改源代码。与Java不同,Python并不假装成为一种安全的语言。您需要检查代码或将测试人员代码移动到其他进程。

multiprocessing模块可以帮助您完成此操作。类似的东西:

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

即便如此,仍有一堆漏洞。例如,学生的代码可以在您的测试器代码模块中读取并检查它的答案。为了确保安全,您必须设置某种客户端/服务器模型,并在不同用户的帐户上运行测试人员代码。

答案 4 :(得分:0)

在测试人员上,您可以将correctAnswer定义为属性,如果已知代码以外的任何内容调用它,则引发异常。

这可以通过内省来实现,但并不漂亮:

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

请注意,这确实意味着您需要定义_correctAnswer,但这只能在您的代码版本中(您可以在其版本中使用.correctAnswer;使用{{的美妙之处1}}这个替换对被调用者是透明的。)


当然,虽然上述解决方案的工作原理是它允许您告诉学生何时尝试访问正确的答案,但这有点笨拙。

但是,我个人认为在服务器上查看学生的答案会更优雅。 通过这样做,您可以控制整个过程,并且可以轻松确保在提交后代理的答案不会更改。

如果编写一个简单的http查询超出了学生的范围,你可以包含一个辅助方法(@property或类似的),为他们执行这个过程。