Restricted attribute writing

Thomas Jollans t at
Wed Aug 10 11:38:46 CEST 2011

On 07/08/11 17:35, John O'Hagan wrote:
> I'm looking for good ways to ensure that attributes are only writable such that they retain the characteristics the class requires. 
> My particular case is a class attribute which is initialised as a list of lists of two integers, the first of which is a modulo remainder. I need to be able to write to it like a normal list, but want to ensure it is only possible to do so without changing that format. 
> Below is a what I eventually came up with; a container class called OrderElement for the inner lists, and subclass of list called Order for the main attribute, which is a property of the main class, simplified below as SeqSim.  It works, but seems like a hell of a lot of code for a simple idea. I'm interested in ideas for simpler solutions, and general advice on how to do this kind of thing in a straightforward way.  

What I think you're looking for is properties:

So: with a property, channel getting and setting through functions that
can check the value for validity.
Secondly, use tuples instead of lists where possible: they're not
mutable, which makes controlling their value a lot easier.
If your attribute is a list that users may append elements to, consider
adding an addelement(tpl) method to your class that checks the value
and appends it to the list.

Lastly, consider the option of not checking vigorously at all at that
point: If the value is invalid, it will raise an exception later, when
it is used. Depending on what you're doing, this might be all the
checking you need. If you're going to serialise the data, or if you want
to use the property setter to check user input directly, that's probably
not a strong option.


> class OrderElement():
>     """Container class which can only hold two integers
>        the first of which is a modulo of the 'length' arg"""
>     def __init__(self, lis, length):
>         self.__data = [None, None]
>         self.__length=length
>         self[:] = lis    
>     def __setitem__(self, index, item):
>         if isinstance(index, slice):
>             inds = range(*index.indices(2))
>             for k, v in enumerate(item):
>                 self[inds[k]] = v
>         elif isinstance(item, int):
>             if index == 0:
>                 item %= self.__length
>             self.__data[index] = item
>         else:
>             raise TypeError("OrderElement takes two integers")
>     def __getitem__(self, index):
>         return self.__data[index]
> class Order(list):
>     """Can only contain OrderElements"""
>     def __init__(self, lis, length):
>         self.__length = length
>         self[:] = lis
>     def __setitem__(self, index, item):
>         if isinstance(index, slice):
>             item = [i if isinstance(i, OrderElement)
>                 else OrderElement(i, self.__length)
>                 for i in item]
>         elif not isinstance(item, OrderElement):
>             item = OrderElement(item, self.__length)           
>         list.__setitem__(self, index, item)
>     def __getitem__(self, index):
>         """Ensure slices are of the same class"""
>         if isinstance(index, slice):
>             return self.__class__(list.__getitem__(self, index),
>             self.__length)
>         return list.__getitem__(self, index)
> class SeqSim():
>     """Just the relevant bits of the main class"""
>     def __init__(self, lis, length):
>         self.__order = Order(lis, length)
>         self.length = length        
>     @property
>     def order(self):
>         return self.__order
>     @order.setter
>     def order(self, lis):
>         if not isinstance(lis, Order):
>             lis = Order(lis, self.length)
>         self.__order = lis

More information about the Python-list mailing list