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

Dani AI

Generated

Nice, clever idea from . In plain terms: the technique captures a slot descriptor's accessors in a private scope and then removes the public name from the class so normal attribute lookup (and the usual mangled-name tricks) no longer expose the value. Moving the setup into __new__ (as points out) prevents accidental mutation by re-calling __init__.

Caveats and when not to use this: this is an implementation-dependent trick (it relies on how the interpreter arranges slot storage) and is obscurity, not real security. Introspection tools, C extensions, picklers/serializers or other Python implementations may behave differently or still be able to observe the underlying storage. If the code must be portable, robust, or security-sensitive, avoid relying on interpreter internals. Also consider thread-safety if the hidden-access logic mutates shared state.

A more portable pattern that keeps per-instance “private” data out of instance attributes is a closure + WeakKeyDictionary. The mapping is only reachable from the closure and values disappear when instances are collected:

from weakref import WeakKeyDictionary

def make_A():
    _priv = WeakKeyDictionary()
    class A(object):
        __slots__ = ('__weakref__',)   # allow weak refs without a __dict__
        def __init__(self, val=None):
            _priv[self] = val
        def get_private(self):
            return _priv.get(self)
        def __repr__(self):
            return "<A %r>" % _priv.get(self)
    return A

A = make_A()

Why this helps: it avoids interpreter-specific slot tricks, works across implementations, and keeps the per-instance values out of dir() and the usual attribute lookups. If you need immutability after creation, set the value in __new__ or override __setattr__ to reject later writes. If multiple threads will access the hidden map, protect it with a lock.

I want to note I'm only using __init__ above because it's standard, but honestly, I hate the method, here's why:

>>> i = A( 15 )
>>> i
<A 15 >
>>> i.__init__(20)
>>> i
<A 20 >

I learned this from hacking property() instances to actively update their getters, setters, and delerers, but it works as well with most anything else that sets it's attributes through __init__.
To prevent unwanted access like this, you should use __new__ which does the actual cration of, and returns the instance:

def private(): # no outside access

    class A(object):
        __slots__ = ['__private__']

        def __new__( cls, val=None ):
            inst = object.__new__( cls ) # since we're inheriting (object), we need to use it's __new__ method to create instances

            setprivate( inst, val ) # we can do everything here just like we do with __init__

            return inst

        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

now if we try to hack the values, nothing happens:

>>> i = A(15)
>>> i.__init__(20)
>>> i
<A 15 >
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.