Does altering a private member decouple the property's value?

Ethan Kennerly kennerly at finegamedesign.com
Mon Jun 18 22:49:04 EDT 2007


Hello,

There are a lot of Python mailing lists.  I hope this is an appropriate one
for a question on properties.

I am relatively inexperienced user of Python.  I came to it to prototype
concepts for videogames.   Having programmed in C, scripted in Unix shells,
and scripted in a number of proprietary game scripting languages, I'm
impressed at how well Python meets my needs.  In almost all respects, it
does what I've been wishing a language would do.

One example is properties.  I really like properties for readonly
attributes, and their ability to make the interface more elegant, by hiding
uninteresting methods (like dumb get/set accessors).  As a user interface
designer, I respect how this may prevent the programmer's deluge of
unimportant information.  I also value the ease of refactoring, which is a
frequent task in my prototypes.

But a gotcha bit me in the behavior of properties that I didn't expect.  
If another function accesses an underlying data member of a property, then 
the data member returned by the property is no longer valid.

Here is a session example.

PythonWin 2.4.3 - Enthought Edition 1.0.0 (#69, Aug  2 2006, 12:09:59) [MSC
v.1310 32 bit (Intel)] on win32.
Portions Copyright 1994-2004 Mark Hammond (mhammond at skippinet.com.au) - see
'Help/About PythonWin' for further copyright information.

>>> class a_class:
... 	def __init__( self ):  self.__p = None
... 	def __set_p( self, new_p ):  self.__p = new_p
... 	def reset( self ):  self.__p = None
... 	p = property( lambda self:  self.__p, __set_p )
... 
>>> a = a_class()
>>> a.p
>>> a.p = 1
>>> a.p
1
>>> a.reset()
>>> a.p
1

Although the underlying value was reset, the property was not reset!

Instead, if the property is edited, then all is fine.  

>>> class a_class:
... 	def __init__( self ):  self.__p = None
... 	def __set_p( self, new_p ):  self.__p = new_p
... 	def reset( self ):  self.p = None   # Property, not the private
member
... 	p = property( lambda self:  self.__p, __set_p )
... 
>>> a = a_class()
>>> a.p
>>> a.p = 1
>>> a.reset()
>>> a.p

I had wanted to access the private data member in the class to avoid
side-effects of the set method.

Can someone show me how to reference the data member underlying a property
and update the property without calling the property set method?

By the way, I thought maybe that a variable outside of an __init__ method
would be static, but as far as I can tell, it is dynamic.  For example, the
following class appeared equivalent to the above for tests.   

>>> class a_class:
... 	__p = None     # No __init__
... 	def __set_p( self, new_p ):  self.__p = new_p
... 	def reset( self ):  self.p = None
... 	p = property( lambda self:  self.__p, __set_p )
... 
>>> a = a_class()
>>> a.p
>>> a.p = 1
>>> a.reset()
>>> a.p
>>> 

I preferred not having the __init__ for this example and my prototype,
because I wasn't doing anything fancy, and it meant one less method that the
programmer needed to see.  Again, the interface is cleaner.  But does this
cause an error for derived classes that would use the private data member?
Without the __init__ method, is the derived class' __p equal to the base
class' __p?

I thought maybe the class was being referenced instead of the instance, but
a second object had a different value.

>>> b = a_class()
>>> b.p
>>> b.p = 2
>>> b.p
2
>>> b.reset()
>>> a.p
>>> a.p = 1
>>> a.p
1
>>> b.p
>>> b.p = 2
>>> b.reset()
>>> a.p
1
>>> b.p
>>> 

Also properties don't show up in the dictionary if no assignment has been
made, but once a property's assignment has been called, the property
appears.  An example follows:

>>> import pprint
>>> pprint.pprint( a.__dict__ )
{'p': 1}
>>> pprint.pprint( b.__dict__ )
{'p': None}
>>> c = a_class()
>>> pprint.pprint( c.__dict__ )
{}
>>> c.p
>>> pprint.pprint( c.__dict__ )
{}

Is that dictionary population behavior for detecting an uninitialized
property?

Thanks for your help.  When my feet are properly wet, I look forward to
contributing to the community.

-- Ethan Kennerly





More information about the Python-list mailing list