Why does python not have a mechanism for data hiding?

Carl Banks pavlovevidence at gmail.com
Mon Jun 2 14:39:45 CEST 2008


On Jun 2, 8:14 am, Carl Banks <pavlovevide... at gmail.com> wrote:
> Fair enough, but I don't see anything in your example that suggests a
> way to discriminate between access from within the class and access
> from outside the class, which is the crucial aspect of data hiding.


And, if you want an example of something that does that, how about
this metaclass.  It creates a class that checks the stack frame to see
if the caller was defined in the same class.

Issues:
Classes are prevented from defining their own __setattr__ and
__getattribute__.
Classes and subclasses should not use the same names for their private
variables.
Private attribute access is pretty slow, but that's obvious.
Pretty easy to thwart.


#----------------------------------
import sys
import itertools

class PrivateAccessError(Exception):
    pass

class PrivateDataMetaclass(type):
    def __new__(metacls,name,bases,dct):

        function = type(lambda x:x)

        privates = set(dct.get('__private__',()))

        codes = set()
        for val in dct.itervalues():
            if isinstance(val,function):
                codes.add(val.func_code)

        getframe = sys._getframe
        count = itertools.count

        def __getattribute__(self,attr):
            if attr in privates:
                for i in count(1):
                    code = getframe(i).f_code
                    if code in codes:
                        break
                    if code.co_name != '__getattribute__':
                        raise PrivateAccessError(
                            "attribute '%s' is private" % attr)
            return super(cls,self).__getattribute__(attr)

        def __setattr__(self,attr,val):
            if attr in privates:
                for i in count(1):
                    code = getframe(i).f_code
                    if code in codes:
                        break
                    if code.co_name != '__setattr__':
                        raise PrivateAccessError(
                            "attribute '%s' is private" % attr)
            return super(cls,self).__setattr__(attr,val)

        dct['__getattribute__'] = __getattribute__
        dct['__setattr__'] = __setattr__

        cls = type.__new__(metacls,name,bases,dct)

        return cls

#----------------------------------
import traceback

class A(object):
    __metaclass__ = PrivateDataMetaclass
    __private__ = ['internal']

    def __init__(self,n):
        self.internal = n

    def inc(self):
        self.internal += 1

    def res(self):
        return self.internal


class B(A):
    __private__ = ['internal2']

    def __init__(self,n,m):
        super(B,self).__init__(n)
        self.internal2 = m

    def inc(self):
        super(B,self).inc()
        self.internal2 += 2

    def res(self):
        return self.internal2 + super(B,self).res()

    def bad(self):
        return self.internal2 + self.internal


a = A(1)
a.inc()

print "Should print 2:"
print a.res()
print

print "Should raise PrivateAccessError:"
try:
    print a.internal
except PrivateAccessError:
    traceback.print_exc()
print

b = B(1,1)
b.inc()

print "Should print 5:"
print b.res()
print

print "Should raise PrivateAccessError:"
try:
    print b.internal2
except PrivateAccessError:
    traceback.print_exc()
print

print "Should raise PrivateAccessError:"
try:
    print b.bad()
except PrivateAccessError:
    traceback.print_exc()
print
#----------------------------------



Carl Banks



More information about the Python-list mailing list