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__']