I've read various "there is no truly private data in Python instances" posts, but we're all aware of using closures in Perl and JavaScript to effectively achieve private data. So why not in Python?
For example:
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()
Now we do:
>>> 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']
What can you do to the instance thing to get read access to the original string (ignoring my weak encryption) or write access to it?
I'm teaching my students about Python classes this week and I'm trying to understand why, given closures, the techniques for JavaScript & Perl won't work for Python.
If you just want to access the original, it's not that hard, since Python function implement a rather thorough inspection api. You can access the original secret with something like this:
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
And, hey! We get the original value.
It is harder---but not impossible---to modify the value (see here). Modified for this setup:
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()
You wouldn't ordinarily do this but you can dig into the instance with module 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'
Given one of the functions within the closure, you can access the closure's variables. I haven't yet found a way to modify the variable.
So it's not impossible if you are willing to go to some effort. Why you would do that in the normal course of programming I don't know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With