ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: Public and protected attribute access
Submitter: kay schluehr (other recipes)
Last Updated: 2006/08/02
Version no: 1.2
Category: OOP

 

3 stars 1 vote(s)


Description:

This recipe presents a way to introduce proper attribute access protection levels using a generic proxy object. By default all attributes are "protected" in the C++ sense and can be declared as public using either a function decorator or a class attribute.

Source: Text Source

def public(f):
    '''
    Decorator used to assign the attribute __public__ to methods.
    '''
    f.__public__ = True
    return f

class Protected(object):
    '''
    Base class of all classes that want to hide protected attributes from
    public access.
    '''
    def __new__(cls, *args, **kwd):
        obj = object.__new__(cls)        
        cls.__init__(obj, *args, **kwd)
                                
        def __getattr__(self, name):            
            attr = getattr(obj, name)
            if hasattr(attr, "__public__"):
                return attr
            elif hasattr(cls, "__public__"):
                if name in cls.__public__:
                    return attr                            
            raise AttributeError, "Attribute %s is not public."%name
        
        def __setattr__(self, name, value):
            attr = getattr(obj, name)                
            cls.__setattr__(self, name, value)    

        # Magic methods defined by cls must be copied to Proxy.
        # Delegation using __getattr__ is not possible.

        def is_own_magic(cls, name, without=[]):
            return name not in without and\
                   name.startswith("__") and name.endswith("__")

        Proxy = type("Protected(%s)"%cls.__name__,(),{})   

        for name in dir(cls):
            if is_own_magic(cls,name, without=dir(Proxy)):
                try:
                    setattr(Proxy, name, getattr(obj,name))
                except TypeError:
                    pass
                    
        
        Proxy.__getattr__ = __getattr__
        Proxy.__setattr__ = __setattr__
        return Proxy()

#
# Example
#

class Counter(Protected):
    __public__ = ["y"]
    def __init__(self, start=0):
        self.n = start
        self.y = 0
                    
    @public            
    def inc(self):
        self.n+=1
        return self.n
        
    @public        
    def dec(self):
        self.n-=1
        return self.n
    
    @public    
    def value(self):
        return self.n


#
# Enhanced example using inheritance
#

class SteppedCounter(Counter):
    def __init__(self, start=0, step=1):
        super(SteppedCounter,self).__init__(start=start)
        self.step = step

    @public            
    def inc(self):
        self.n+=self.step
        return self.n
    
    @public            
    def dec(self):
        self.n-=self.step
        return self.n
    
    def steps(self):
        return self.n/self.step


################################################################
#   Python Session
#################################################################

>>> c = Counter()
>>> c.inc()
1
>>> c.dec()
0
>>> c.n
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "C:\Python24\Lib\site-packages\pythonwin\private.py", line 17, in __getattr__
    raise AttributeError, "Attribute %s is not public."%name
AttributeError: Attribute n is not public.
>>> c.y
0


>>> sc = SteppedCounter(step=2)
>>> sc.inc()
2
>>> sc.value()
2
>>> sc.steps()  
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
  File "C:\Python24\Lib\site-packages\pythonwin\private.py", line 17, in __getattr__
    raise AttributeError, "Attribute %s is not public."%name
AttributeError: Attribute steps is not public.

Discussion:

Generations of Pythonistas have mangled their variables with a double underscore "__" to enable data hiding as in C++ or Java. But this was always only an adaequate solution for preventing name clashes between a class and its superclass. The term "privacy" had a very different meaning. Opposite to the standard semantics where each Python variable is essentially public, applying the above recipe it becomes basically "protected" i.e. visible only to the class where it is defined and to subclasses. In order to make an attribute public it has to be made public explicitely using either the public() decorator ( for methods only ) or the class attribute __public__.


Remark (v1.2): handling of magic methods is added.



Add comment

No comments.



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. a friendly mkdir()

4. SOLVING THE METACLASS ...

5. Povray for python

6. Changing return value ...

7. Implementation of sets ...

8. bag collection class

9. deque collection class

10. Floating Point Simulator




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.