Public attributes with really private data

Mark Summerfield list at qtrac.plus.com
Fri May 8 08:44:19 EDT 2009


On 8 May, 09:52, Carl Banks <pavlovevide... at gmail.com> wrote:
> On May 7, 11:37 pm, MarkSummerfield<l... at qtrac.plus.com> wrote:
>
>
>
> > Hi,
>
> > 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.
> > (For Python 2 there does seem to be an approach although I'm not
> > keen on it:http://www.builderau.com.au/blogs/byteclub/viewblogpost.htm?p=339270875
> > )
>
> > Here's a standard class with one read-only and one writable
> > property that has a tiny bit of validation.
>
> > class P:
> >     def __init__(self, w):
> >         self.w = w
> >     @property
> >     def r(self): return 5
> >     @property
> >     def w(self): return self.__w
> >     @w.setter
> >     def w(self, value):
> >         if value > 0: # Only +ve values allowed
> >             self.__w = value
> >         else:
> >             raise ValueError("'{0}' is not valid for w".format(value))
>
> > The read-only property is completely private because it isn't
> > actually stored as such.
>
> > But if we do dir() on an instance, in addition to 'r' and 'w', we
> > also have '_P__w'. So the writable property's data is easily
> > accessible, and the validation can be got around:
>
> > >>> p = P(9)
> > >>> p.r, p.w
> > (5, 9)
> > >>> p.w = 43
> > >>> p.r, p.w
> > (5, 43)
> > >>> p.w = -7
>
> > Traceback (most recent call last):
> > ...
> > ValueError: '-7' is not valid for w>>> p._P__w = -7
> > >>> p.r, p.w
>
> > (5, -7)
>
> > Here's a class where I can't think of a way to access the private
> > data and set invalid values.
>
> > class A:
> >     r = Attribute("r", 5)
> >     w = Attribute("w", None, lambda self, value: value > 0)
> >     def __init__(self, w):
> >         self.w = w
>
> > The Attribute class is a descriptor that takes three arguments:
> > name of attribute, initial value (essential for read-only
> > attributes!), and a validator function (which could be "lambda
> > *a: True" if any value is accepatble).
>
> > >>> a = A(9)
> > >>> a.r, a.w
> > (5, 9)
> > >>> a.w = 43
> > >>> a.r, a.w
> > (5, 43)
> > >>> a.w = -7
>
> > Traceback (most recent call last):
> > ...
> > ValueError: '-7' is not valid for w
>
> > If we do a dir(a) the only attributes we get (beyond those from
> > object) are 'r' and 'w', so it shouldn't be possible to get
> > around the validation---at least not easily.
>
> Ok, I'll bite.
>
> A.w = -7
>
> A.__class__ = A_without_validation
> a.w = -7
>
> Attribute.__set__ = function_that_ignores_validation
> a.w = -7

I don't see how that can work? Sure you could replace the __set__
method, or the Attribute.__setter method. But neither can actually
change the hidden_value because AFAIK that only exists within the
scope of a closure and so isn't accesssible. (Well, maybe if you
started using the inspect module you'd find it.)

> I am not opposed to adding a little well-considered protection to some
> attributes where mistakes are prone to happen and/or dangerous, but it
> is futile to try to stop access entirely.  There's just too many back
> doors.

Sure, but I like the fact that there is no "shadow" attribute, so just
"w" not "w" _and_ "_A__w".

> I would suggest that there really isn't much point in anything more
> complex than your first solution, which was to validate with
> properties and to store the value in a separate attribute.
> Respectable programmers won't lightly bypass your validation if they
> see that you've taken steps to enforce it.  OTOH, once a programmer,
> respectable or not, decides to override your protection, they are not
> likely to be stopped by something more complex.  So there is no point
> in making it more complex.
>
> Carl Banks

I think that's a fair viewpoint, but I still like the idea of there
being no _A__w available for property w.

--
Mark Summerfield, Qtrac Ltd, www.qtrac.eu
    C++, Python, Qt, PyQt - training and consultancy
        "Programming in Python 3" - ISBN 0137129297



More information about the Python-list mailing list