[Tutor] testing new style class properties using pyUnit ?

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Wed Jun 30 19:04:09 EDT 2004



On Wed, 30 Jun 2004, Duncan Gibson wrote:

> I had a class which used get and set methods to access a class variable.
> I set up some unit tests using pyUnit. After some fiddling about, I
> managed to get it all working.
>
> I've just upgraded the source to use the new style classes with
> properties instead of explicit get and set methods. However, so far I've
> been unable to convert the unit tests. I note the error message, but I
> don't understand why.

[some code cut]

Hi Duncan,


Ok,, what you're running into is actually not so much related properties
feature, but actually of the naming-mangling that Python does on any
property with leading double underscores.

In the 'A_new' class, you have the following methods:


> class A_New(object):
>     __slots__ = ('__x',)
>     def __init__(self, x=0):
>         self.x = x
>     def __get_x(self):
>         return self.__x
>     def __set_x(self, x):
>         assert isinstance(x, int)
>         self.__x = x


Let's concentrate on the names of the getters and setters that you've
chosen: '__get_x' and '__get_y'.  Python doesn't supports a strict way of
defining data privacy (i.e.  the 'private', 'protected' keywords in C++
and Java).  But Python does provide some token support for encapsulation
by making it very difficult to directly accessing attributes with double
underscores from outside the defining class.


Any method with double underscores in front will get named-manged, so that
accessing the methods from outside the class requires deliberate, abusive
effort.  We can see this by asking what A_New contains, once we've defined
it:

###
>>> dir(A_New)
['_A_New__get_x',
 '_A_New__set_x',
 '_A_New__x',
 ...
###


Note that the '__get_x' method has been name-mangled so that it includes
the class name with it.  So your test that tries to access the set method:


>     def testSet(self):
>         a = A_New()
>         self.assertEqual(0, a.x)
>         self.assertEqual(None, a.__set_x(1))
>         self.assertEqual(1, a.x)


needs to work extra hard to actually call this "private" setter:

###
     def testSet(self):
         a = A_New()
         self.assertEqual(0, a.x)
         self.assertEqual(None, a._A_New__set_x(1))
         self.assertEqual(1, a.x)
###



So I hope that explains why we're getting the AttributeError.  See:

http://docs.python.org/tut/node11.html#SECTION0011600000000000000000

and

http://www.python.org/doc/faq/programming.html#i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam

for more details about this name-mangling.




Something strikes me weird though: if you're testing the set/get stuff,
why not modify your test to:

###
     def testSet(self):
         a = A_New()
         self.assertEqual(0, a.x)
         a.x = 1
         self.assertEqual(1, a.x)
###


The point about properties is to make doing get/set transparent: what
looks like property access,

         a.x = 1

quietly translates to a call to the setter.  Isn't that what you want to
test?



Hope this helps!




More information about the Tutor mailing list