Public attributes with really private data

Carl Banks pavlovevidence at gmail.com
Fri May 8 04:52:31 EDT 2009


On May 7, 11:37 pm, Mark Summerfield <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 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.

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



More information about the Python-list mailing list