Read Only attributes, auto properties and getters and setters

T

TechieInsights

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
 
T

TechieInsights

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
 
J

josh logan

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
 
T

TechieInsights

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.
 
J

josh logan

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 :)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top