On 8 May, 13:56, Peter Otten <__pete...@web.de> wrote: > Mark Summerfield wrote: > > On 8 May, 08:19, Peter Otten <__pete...@web.de> wrote: > >> MarkSummerfieldwrote: > >> > I had a quick search & didn't find anything _nice_ that produced > >> > attributes with really private data, so I came up with a possible > >> > solution---for Python 3. > > >> Do really you think what you suggest below is "nice"? > > > Well the code isn't ugly and doesn't mess with the call stack etc. > > >> By the way, your Attribute descriptor stores the value for all instances > >> of A in the same variable... > > > It seems like it does, but it doesn't. The hidden_value is an instance > > variable that is created every time an Attribute object is created. > > >>>> from Attribute import * > >>>> class A: > > a = Attribute("a", 5, lambda *a: True) > > b = Attribute("b", 5, lambda *a: True) > >>>> class B: > > a = Attribute("a", 5, lambda *a: True) > > b = Attribute("b", 5, lambda *a: True) > >>>> a = A() > >>>> b = B() > >>>> a.a,a.b,b.a,b.b > > (5, 5, 5, 5) > >>>> a.a=1;a.b=2;b.a=3;b.b=4 > >>>> a.a,a.b,b.a,b.b > > (1, 2, 3, 4) > > But attribute values are shared between all instances of the same class: > > >>> class A: > > ... x = Attribute("x", 42, lambda *a: True) > ...>>> a = A() > >>> b = A() > >>> a.x, b.x > (42, 42) > >>> a.x = "yadda" > >>> a.x, b.x > > ('yadda', 'yadda') > > Peter
OK, I couldn't quite give it up. But the solution isn't nice or good. It does work, but at the cost of an extra dictionary lookup on every get or set, plus a dictionary to hold all the getters & setters. I _don't recommend it_, but here it is anyway. I've done with it now:-) class Attribute: __accessors = {} def __init__(self, name, first_value=None, validator=None): self.name = name self.first_value = first_value self.validator = validator def __get__(self, instance, owner=None): if instance is None: return self if (id(instance), self.name) not in self.__accessors: self.__makeAccessors(instance) return self.__accessors[id(instance), self.name][0](instance) def __set__(self, instance, value): if (id(instance), self.name) not in self.__accessors: self.__makeAccessors(instance) setter = self.__accessors[id(instance), self.name][1] if setter is None: raise AttributeError("'{0}' is read-only".format( self.__name__)) return setter(instance, value) def __makeAccessors(self, instance): hidden_value = self.first_value getter = lambda self: hidden_value if self.validator is not None: def set(instance, value): if self.validator(instance, value): nonlocal hidden_value hidden_value = value else: raise ValueError("'{0}' is not valid for {1}" .format(value, name)) setter = set else: setter = None self.__accessors[id(instance), self.name] = (getter, setter) -- Mark Summerfield, Qtrac Ltd, www.qtrac.eu C++, Python, Qt, PyQt - training and consultancy "Programming in Python 3" - ISBN 0137129297 -- http://mail.python.org/mailman/listinfo/python-list