If you're in one of those predicaments where cls.__private
attributes just aren't enough since they can easily be accessed through inst._cls__private
, and you need something a little more secure, here's a method I've been playing with for a while that fills that gap.
Note that this uses __slots__
, more info on this below the code.
def private(): # no outside access
class A(object):
__slots__ = ['__private__']
def __init__( inst, val=None ):
setprivate( inst, val )
def __repr__( inst ): return '<A %s >' % getprivate( inst )
globals()['A'] = A # add our class to the public namespace
dsc = A.__private__
del A.__private__ # this makes your attribute realistically private as it removes all normal access from both the class and it's instances.
# this is how access to the "attribute" is maintained:
getprivate = dsc.__get__
setprivate = dsc.__set__
private()
del private
How this works is __slots__
creates member_descriptor objects (which can't be created normally) used by the class instances, which we want because they're valid spaces in memory for applying values per instance.
When we del class.attr
or alternatively delattr(class,'attr')
we're simply removing the link to the member_descriptor, not the member_descriptor itself which is still registered with the class even though it's link is gone.
So in the above code in the private namespace getprivate
and setprivate
can't be accessed anywhere but the class and anything else inside the private namespace.
Now that we have access to the getter and setter methods of the member_descriptor, we can use __closure__
in A's methods to add a layer of security through a form of obscurity that may change as the code is modified, leaving hackers in a sandbox where their code needs updating to match the program's changes.
(if you know what you're doing, you can still figure out how to access these attributes, but it's much harder to do than, and not as solid as the typical __private
approach)
So A.__init__
above calls setprivate
supplying the instance and the value it's given, where it can be seen with print(repr(inst))
like so:
>>> i = A(15)
>>> repr(i)
'<A 15 >'
Hope you enjoy this little hack :)
Might I note it's also performative ;D
EDIT:
here's a dir(i)
so you can see the __private__
attribute really doesn't exist in the instance (the same goes for the class).
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__']