11

I know that the attributes of class which are declared by double underscore __ prefix may or may not visible outside the class definition. As we can still access those attributes by object._className__attrName.

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
        ----
        ----
        self.z = 26
        self.catch = 100

Now to protect all attributes except the attribute catch, I have to declare them with double underscore which is quite messy. Is there a way I can say in my class definition that only self.catch can be accessed outside the class?

Apologies if this is answered somewhere else or discussed earlier.

Teo Klestrup Röijezon
  • 5,097
  • 3
  • 29
  • 37
Aamir Rind
  • 38,793
  • 23
  • 126
  • 164
  • 1
    No. However, unless you're really paranoid, the fact that someone *can* access an object using `object._className__attrName` isn't a big deal. You really just want to prevent people from accidentally accessing variables they shouldn't when using your code. The obfuscation achieves that goal. – Chris Jan 26 '12 at 20:45
  • 2
    possible duplicate of [Does python have 'private' variables in classes?](http://stackoverflow.com/questions/1641219/does-python-have-private-variables-in-classes) – Björn Pollex Jan 26 '12 at 20:46
  • @BjörnPollex, this isn't what he's asking for. He wants the need for an explicit "public" "operator", not the opposite. – Teo Klestrup Röijezon Jan 26 '12 at 20:48
  • Do you really need double underscore? Why not just use the more standard single underscore `self._a`? – Bi Rico Jan 26 '12 at 21:46

4 Answers4

20

Yes, it is possible to hide private data in a closure -- at least, if there is a way to access private from outside make_A, I haven't found it:

def make_A():
    private = {
        'a' : 1,
        'b' : 2,
        'z' : 26,
        }
    class A:
        def __init__(self):
            self.catch = 100
            private['a'] = 2    # you can modify the private data
        def foo(self):
            print(private['a']) # you can access the private data 
    return A

A = make_A()

a=A()

a.foo()
# 2

Notice that private is not in dir(a)

print('private' in dir(a))
# False

Although this is possible, I do not think this is recommendable way to program in Python.


Above, private is shared by all instances of A. To use private data on a per-instance basis, add self to the dict key:

def make_A():
    private = {}
    class A:
        def __init__(self):
            self.catch = 100
            private[self,'a'] = 1    # you can modify the private data
            private[self,'b'] = 2    
            private[self,'z'] = 26    
        def foo(self):
            print(private[self,'a']) # you can access the private data 
    return A
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Thumbs up for ***closures**. This may be of interest: [Secret Variables i Python](http://www.jotflow.com/jot/Secret-Variables-In-Python/5). It compares functional data hiding in Python with private variables in Java. – Sumukh Barve Jan 08 '15 at 10:23
  • Looks good, but it will lead to memory leak, because once an instance of `A` is created, it will be always referenced by the dict `private`. Python cannot GC it until the `private` is GC'ed. – Bruce Oct 18 '19 at 19:22
5

While the accepted answer by unutbu looks like a good idea to hide data, the private dictionary still accessible using __closure__ (this attribute cannot be removed):

def make_A():
    private = {}
    class A:
        def __init__(self):
            self.catch = 100
            private[self,'a'] = 1    # you can modify the private data
            private[self,'b'] = 2    
            private[self,'z'] = 26    
        def foo(self):
            print(private[self,'a']) # you can access the private data
    return A


A = make_A()
a = A()
a.foo()  # 1
a.foo.__closure__[0].cell_contents[(a, 'a')] = 42
a.foo()  # 42

Or following the link provided by Sumukh Barve in the comments:

def createBankAccount():
    private = {'balance': 0.0}
    def incr(delta):
        private['balance'] += delta;
    account = {
        'deposit': lambda amount: incr(amount),
        'withdraw': lambda amount: incr(-amount),
        'transferTo': lambda otherAccount, amount: (
            account['withdraw'](amount),
            otherAccount['deposit'](amount)
        ),
        'transferFrom': lambda otherAccount, amount: (
            otherAccount['transferTo'](account, amount)
        ),
        'getBalance': lambda : private['balance']
    }
    return account;


account = createBankAccount()

print(account['getBalance']())
account['getBalance'].__closure__[0].cell_contents['balance'] = 1000**1000
print(account['getBalance']())  # I can buy a couple of nations

I bevile the only way to create private attributes is to write some kind of CPython extension.

Ceppo93
  • 1,026
  • 10
  • 11
1

Nope, since it's part of the name, you can't do that.

Well, actually you can through hacking with the getters/setters, but you generally shouldn't.

Teo Klestrup Röijezon
  • 5,097
  • 3
  • 29
  • 37
1

Why might you want to protect attributes?

If you want to protect attributes from inadvertent overwriting, use double underscores and, probably, ready-only properties to make access easier. But most of the time, you aint't gonna need it. Just do yourself a favor and leave the attributes open, this makes debugging far easier.

If you want to protect your object from tampering by third parties, you can play with __getattr__ and __setattr__, but probably you should not pass to an untrusted client objects with protected data at all, use the facade pattern instead and hide your valuables more opaquely. You might want to rethink your architecture, too.

9000
  • 39,899
  • 9
  • 66
  • 104
  • 2
    *Why might you want to protect attributes?* I could just point you to the wiki entry on encapsulation, but I will give you a practical example. Let's say you have a large and complex python application in which you have a number of classes with the attribute 'name'. A bug is found in which the class administrator's name is being incorrectly overridden. If you have left the name attribute open as you suggest, how will you find the offending line of code? If you have hidden the attribute and used accessors, you can put a breakpoint on the getter and find the culprit fast. – dar512 Aug 02 '13 at 20:44
  • @dar512: if your class is a part of a public interface, and clients might tamper with attributes, by all means guard them by accessors! The number of public classes is rather limited in a typical application, though. Also, using immutable data has numerous advantages; I often use subclasses of `namedtuple` to represent data in a bullet-proof way. – 9000 Aug 02 '13 at 22:29
  • 1
    As a programmer, if I chose to shoot off my own leg with a shotgun, so be it. You are limiting my ability to fix / alter your code by hiding things. The 'protected' and 'private' keywords are just anti-patterns. Please abandon the notion that you are doing anyone a favour by 'hiding' things from other programmers. If you want to do something like this, use `_variable` and provide a `variable` getter property and no setter. I can still manually modify the `_variable` this way if I so wish. But do not use language hacks to obfuscate your internals from other programmers. – Rebs Oct 19 '15 at 03:08