Read Only attributes, auto properties and getters and setters

Discussion in 'Python' started by TechieInsights, Feb 12, 2009.

  1. Ok, so I noticed some of the modules (such as many of the datetime
    attributes) are read-only, which means the __setattr__ and __set__
    methods are intercepted... pretty easy. I am looking for a way to
    automate this. The real goal is not to have read only attributes, but
    to have getters and setters that are automatically called when a
    property is called. The getting is pretty easily achieved by setting
    the metaclass which also creates the properties. What I have so far
    will protect the get_ and set_ methods as well as the properties that
    call the get methods. The reasoning for this is that I don't want
    someone accidentally overwriting the property when the class uses a
    private class variable. What I have so far:



    class ReadOnlyMeta(type):
    def __new__(cls, name, bases, methoddict):
    pname = '_%s__protected_items' %(name)
    protected = [pname]
    methoddict[pname] = protected
    for key, value in methoddict.items():
    if key.startswith("get_"):
    methoddict[key[4:]] = property(value)
    protected.append(key[4:])
    protected.append(key)
    elif key.startswith("set_"):
    protected.append(key)
    return type.__new__(cls, name, bases, methoddict)

    class ReadOnly(object):
    __metaclass__ = ReadOnlyMeta

    def __check_private(self, name, msg = None):
    if name in self.__protected_items:
    if msg is None:
    msg = "'%s' of '%s' is Read only." %(name,
    self.__class__.__name__)
    raise AttributeError(msg)

    def __setattr__(self, name, value):
    self.__check_private(name, "attribute '%s' of '%s' objects is not
    writable" %(name, self.__class__.__name__))
    self.__dict__[name] = value

    def __delattr__(self, name):
    self.__check_private(name, "attribute '%s' of '%s' objects cannot be
    removed" %(name, self.__class__.__name__))
    self.__dict__.pop(name)

    def __set__(self, instance, value):
    self.__check_private(instance, "You cannot remap the method '%s'" %
    (instance))
    self.__dict__[instance] = value



    I then implement this by creating a subclass of the read only class:

    class MyClass(ReadOnly):
    def __init__(self, x):
    self.set_x(x)

    def get_x(self):
    return self.__x

    def set_x(self, x):
    print 'Hello the setter was called!'
    self.__x = x


    What I want is to be able to auto-call the set_ methods whenever
    someone tries to call a method that has a setter. This could be done
    by each class... tedious! I want automation! To do this I have tried
    adding a list of setters to the metaclass and then in the __set__ and
    __setattr__ methods I check to see if 'set_%s' %(variable) exists in
    the list... if it does I call the method. This would work, however
    there are two lists of setters (in the example above), the ReadOnly
    list and a MyClass list. When you print out a dir(self) IN the
    ReadOnly class from an implementation (MyClass) it will show the
    MyClass attributes, however they cannot be called from the parent
    class... so my real question... how can you (or can you ever) call a
    child method from a parent class without explicitly passing the
    callable?

    I hope this hasn't been too confusing... or made your head hurt like
    mine does... If you want more code examples of what I have tried and
    the out put... I'd be happy to give them.

    Greg
     
    TechieInsights, Feb 12, 2009
    #1
    1. Advertising

  2. Oh... one other thing that would be really cool is to do this with AOP/
    descriptors! I just haven't been able to get that to work either.
    Basics...

    @readonly
    class MyClass(object):
    def __init__(self, x):
    self.set_x(x)

    def get_x(self):
    return self.__x

    def set_x(self, x):
    print 'Hello the setter was called!'
    self.__x = x

    and it's done! I don't think this will work because (maybe I'm wrong)
    but I don't think you can set the metaclass in the descriptor??? If
    this is the case, it is more work to do the AOP and set a metaclass...
    better to just inherit. If it can be done... that would be awesome!

    Greg
     
    TechieInsights, Feb 12, 2009
    #2
    1. Advertising

  3. TechieInsights

    josh logan Guest

    On Feb 12, 10:58 am, TechieInsights <> wrote:
    > Oh... one other thing that would be really cool is to do this with AOP/
    > descriptors!  I just haven't been able to get that to work either.
    > Basics...
    >
    > @readonly
    > class MyClass(object):
    >         def __init__(self, x):
    >                 self.set_x(x)
    >
    >         def get_x(self):
    >                 return self.__x
    >
    >         def set_x(self, x):
    >                 print 'Hello the setter was called!'
    >                 self.__x = x
    >
    > and it's done!  I don't think this will work because (maybe I'm wrong)
    > but I don't think you can set the metaclass in the descriptor???  If
    > this is the case, it is more work to do the AOP and set a metaclass...
    > better to just inherit.  If it can be done... that would be awesome!
    >
    > Greg


    Are your needs not already satisfied by the Python built-in property?

    http://docs.python.org/dev/3.0/library/functions.html#property
     
    josh logan, Feb 12, 2009
    #3
  4. On Feb 12, 9:27 am, josh logan <> wrote:
    > On Feb 12, 10:58 am, TechieInsights <> wrote:
    >
    >
    >
    > > Oh... one other thing that would be really cool is to do this with AOP/
    > > descriptors!  I just haven't been able to get that to work either.
    > > Basics...

    >
    > > @readonly
    > > class MyClass(object):
    > >         def __init__(self, x):
    > >                 self.set_x(x)

    >
    > >         def get_x(self):
    > >                 return self.__x

    >
    > >         def set_x(self, x):
    > >                 print 'Hello the setter was called!'
    > >                 self.__x = x

    >
    > > and it's done!  I don't think this will work because (maybe I'm wrong)
    > > but I don't think you can set the metaclass in the descriptor???  If
    > > this is the case, it is more work to do the AOP and set a metaclass...
    > > better to just inherit.  If it can be done... that would be awesome!

    >
    > > Greg

    >
    > Are your needs not already satisfied by the Python built-in property?
    >
    > http://docs.python.org/dev/3.0/library/functions.html#property


    yea... just read the docs... I love reinventing the wheel (sarcasm)...

    Thanks for this... I just realized that you can set the fset and fdel
    to None... so everything can be done from the metaclass!
     
    TechieInsights, Feb 12, 2009
    #4
  5. Ok... for some closure I have written a class to automate the
    process. It takes getters and setters and deleters and then sets the
    property automatically. Sweet!


    class AutoProperty(type):
    def __new__(cls, name, bases, methoddict):
    processed = []
    getter = 'get_'
    setter = 'set_'
    deleter = 'del_'

    starters = {getter:propertyAttr(getter, PropertyAttr.FGET),
    setter:propertyAttr(setter, PropertyAttr.FSET),
    deleter:propertyAttr(deleter, PropertyAttr.FDEL)
    }
    for key, value in methoddict.items():
    var = None
    for start in starters.keys():
    if key.startswith(start):
    var = key[len(start):]
    break
    if var is None or var in processed:
    continue
    property_values = []

    for start in starters.keys():
    if '%s%s' %(start, var) in methoddict.keys():
    property_values.append(starters[start].tostring(var))
    else:
    property_values.append(starters[start].tostring(None))
    property_map = 'methoddict["%s"] = property(%s)' %(var, ','.join
    (property_values))
    exec(property_map)
    return type.__new__(cls, name, bases, methoddict)

    class PropertyAttr(object):
    FGET = 'fget'
    FSET = 'fset'
    FDEL = 'fdel'
    def __init__(self, start, type = FGET):
    self.start = start
    self.type = type

    def tostring(self, var):
    if self.type == self.FSET:
    vars = ['v']
    else:
    vars = []
    fullvar = ['self'] + vars
    if var is None:
    return '%s=None' %(self.type)
    return '%s=lambda %s: self.%s%s(%s)' %(self.type, ','.join(fullvar),
    self.start, var, ','.join(vars))

    class ReadOnly(object):
    __metaclass__ = AutoProperty


    class MyClass(ReadOnly):
    def __init__(self, x, y):
    self.__x = x
    self.__y = y

    def get_x(self):
    return self.__x

    def set_x(self, x):
    self.__x = x

    def get_y(self):
    return self.__y

    mc = MyClass(10, 100)
    print mc.x, mc.y
    mc.x = 10
    print mc.x
    try:
    mc.y = 100
    except AttributeError:
    print 'Yea it worked!'

    try:
    del mc.y
    except AttributeError:
    print "It's read only!"

    And the output:
    10 100
    10
    Yea it worked!
    It's read only!


    Now to work on descriptors doing it for you.
     
    TechieInsights, Feb 12, 2009
    #5
  6. TechieInsights

    josh logan Guest

    On Feb 12, 12:27 pm, TechieInsights <> wrote:
    > Ok... for some closure I have written a class to automate the
    > process.  It takes getters and setters and deleters and then sets the
    > property automatically.  Sweet!
    >
    > class AutoProperty(type):
    >         def __new__(cls, name, bases, methoddict):
    >                 processed = []
    >                 getter = 'get_'
    >                 setter = 'set_'
    >                 deleter = 'del_'
    >
    >                 starters = {getter:propertyAttr(getter, PropertyAttr.FGET),
    >                                         setter:propertyAttr(setter, PropertyAttr.FSET),
    >                                         deleter:propertyAttr(deleter, PropertyAttr.FDEL)
    >                                         }
    >                 for key, value in methoddict.items():
    >                         var = None
    >                         for start in starters.keys():
    >                                 if key.startswith(start):
    >                                         var = key[len(start):]
    >                                         break
    >                         if var is None or var in processed:
    >                                 continue
    >                         property_values = []
    >
    >                         for start in starters.keys():
    >                                 if '%s%s' %(start, var) in methoddict.keys():
    >                                         property_values.append(starters[start].tostring(var))
    >                                 else:
    >                                         property_values.append(starters[start].tostring(None))
    >                         property_map = 'methoddict["%s"] = property(%s)' %(var, ','.join
    > (property_values))
    >                         exec(property_map)
    >                 return type.__new__(cls, name, bases, methoddict)
    >
    > class PropertyAttr(object):
    >         FGET = 'fget'
    >         FSET = 'fset'
    >         FDEL = 'fdel'
    >         def __init__(self, start, type = FGET):
    >                 self.start = start
    >                 self.type = type
    >
    >         def tostring(self, var):
    >                 if self.type == self.FSET:
    >                         vars = ['v']
    >                 else:
    >                         vars = []
    >                 fullvar = ['self'] + vars
    >                 if var is None:
    >                         return '%s=None' %(self..type)
    >                 return '%s=lambda %s: self.%s%s(%s)' %(self.type, ','.join(fullvar),
    > self.start, var, ','.join(vars))
    >
    > class ReadOnly(object):
    >         __metaclass__ = AutoProperty
    >
    > class MyClass(ReadOnly):
    >         def __init__(self, x, y):
    >                 self.__x = x
    >                 self.__y = y
    >
    >         def get_x(self):
    >                 return self.__x
    >
    >         def set_x(self, x):
    >                 self.__x = x
    >
    >         def get_y(self):
    >                 return self.__y
    >
    > mc = MyClass(10, 100)
    > print mc.x, mc.y
    > mc.x = 10
    > print mc.x
    > try:
    >         mc.y = 100
    > except AttributeError:
    >         print 'Yea it worked!'
    >
    > try:
    >         del mc.y
    > except AttributeError:
    >         print "It's read only!"
    >
    > And the output:
    > 10 100
    > 10
    > Yea it worked!
    > It's read only!
    >
    > Now to work on descriptors doing it for you.


    I think I'll stick with the built-in, Thanks :)
     
    josh logan, Feb 12, 2009
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. George Armhold
    Replies:
    1
    Views:
    1,466
    Chris Riesbeck
    Sep 2, 2004
  2. Getters and Setters

    , Jun 9, 2006, in forum: Java
    Replies:
    7
    Views:
    692
    Eddy Young
    Jun 10, 2006
  3. Raaijmakers, Vincent (IndSys,GE Interlogix)

    Newbie in win32com: getters and setters

    Raaijmakers, Vincent (IndSys,GE Interlogix), Oct 23, 2003, in forum: Python
    Replies:
    0
    Views:
    283
    Raaijmakers, Vincent (IndSys,GE Interlogix)
    Oct 23, 2003
  4. Daniel Fetchinson

    setters and getters in python 2.6 and 3.0

    Daniel Fetchinson, Nov 29, 2007, in forum: Python
    Replies:
    2
    Views:
    590
    Daniel Fetchinson
    Nov 29, 2007
  5. Lachlan Hunt
    Replies:
    2
    Views:
    160
    Lachlan Hunt
    Mar 28, 2005
Loading...

Share This Page