【问题标题】:Python private instance data revisited重访 Python 私有实例数据
【发布时间】:2016-05-21 21:33:05
【问题描述】:

我已经阅读了各种“Python 实例中没有真正的私有数据”的帖子,但我们都知道在 Perl 和 JavaScript 中使用闭包来有效地实现私有数据。那么为什么不用 Python 呢?

例如:

import codecs

class Secret:
    def __private():
        secret_data = None

        def __init__(self, string):
            nonlocal secret_data
            if secret_data is None:
                secret_data = string
        
        def getSecret(self):
            return codecs.encode(secret_data, 'rot_13')

        return __init__, getSecret

    __init__, getSecret = __private()

现在我们做:

>>> thing = Secret("gibberish")
>>> thing.getSecret()
'tvoorevfu'
>>> dir(thing)
['_Secret__private', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getSecret']

您可以对实例事物做什么来获得对原始字符串的读取访问权限(忽略我的弱加密)或对其进行写入访问?

这周我正在教我的学生有关 Python 的课程,我试图理解为什么在给定闭包的情况下,JavaScript 和 Perl 的技术不适用于 Python。

【问题讨论】:

    标签: javascript python perl closures private


    【解决方案1】:

    如果你只是想访问原件,这并不难,因为 Python 函数实现了一个相当彻底的检查 api。您可以通过以下方式访问原始密钥:

    thing = Secret("gibberish")
    # __init__ doesn't need to be used here; anything defined within the closure will do
    thing.__init__.__func__.__closure__[0].cell_contents
    

    还有,嘿!我们得到原始值。

    修改值更难——但并非不可能——(参见here)。针对此设置进行了修改:

    import ctypes
    ...
    
    thing = Secret("gibberish")
    cell = ctypes.py_object(thing.__init__.__func__.__closure__[0])
    new_value = ctypes.py_object('whatever')
    ctypes.pythonapi.PyCell_Set(cell, new_value)
    
    thing.getSecret()
    

    【讨论】:

    • 谢谢@ig0774,我已将 init 修复为事后不再是二传手。您的链接似乎没有引导到适合该讨论的某个地方。我很高兴看到 Python 实现了它,“有不止一种方法可以做到这一点。”座右铭——哦,等等,没关系。
    • @cdlane:不知何故,我想要的链接不是最终出现的链接。我已将其更新为正确的链接。基本上你必须使用 C API,但仍然可以这样做。
    • @cdlane:查看我最近的编辑,它显示了如何将单元格值设置为几乎任何任意 Python 对象。
    • 如果你降到 C 级别,我不能通过将我的数据也隐藏在那里来反击,只是让你降到 C 级别以下以机器代码再次反击我。你和 mhawke 都很容易让我相信没有读保护,写保护可能是特定于实现的。也许 Perl 和 JavaScript 只是一个很好的自省包,因为它们也不能依赖通过闭包来隐藏数据。再次感谢您。
    • 我想按照这里使用的标准,即使 Java 或 C# 也没有“真正”拥有私有数据,因为您可以使用反射或下降到 IL 来覆盖几乎任何东西,但是这两种语言的标准环境提供控制该访问级别的方法(通过代码签名等)。我认为更广泛的观点是 Python 解释器不会隐藏任何正在执行的代码,事实上,它会竭尽全力公开有关解释器正在做什么的大量信息。
    【解决方案2】:

    您通常不会这样做,但您可以使用模块 inspect 深入研究实例。

    >>> thing = Secret("gibberish")
    >>> thing.getSecret()
    'tvoorevfu'
    >>> import inspect
    >>> inspect.getclosurevars(thing.getSecret).nonlocals['secret_data']
    'gibberish'
    >>> inspect.getclosurevars(thing.__init__).nonlocals['secret_data']
    'gibberish'
    

    给定闭包中的一个函数,您可以访问闭包的变量。我还没有找到修改变量的方法。

    因此,如果您愿意付出一些努力,这并非不可能。我不知道为什么你会在正常的编程过程中这样做。

    【讨论】:

    • 谢谢@mhawke,我使用inspect 模块来强化我原来的示例,但担心某些向导仍会使用它来闯入。
    猜你喜欢
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多