Python @ DjangoSpin

Object Oriented Python: Enforcing Encapsulation - The property class

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon
Reading Time: 2 minutes

property class in Python

property class in Python

Getters and setters in Python can be used to set and get attributes. However, they don't strictly impose encapsulation, since attributes can be get and set without these getter-setter methods as well. Pythonic convention is that the users are trusted to access the attributes via getters-setters rather than mangling them directly. It is the Pythonic way.

An anti-Pythonic way to enforce encapsulation is to use the builtin property class and its decorator @property. The property class returns a property object. It is used to manage attributes. Its constructor takes four arguments:

  1. getter method of attribute x (keyword argument 'fget', defaults to None)
  2. setter method of attribute x (keyword argument 'fset', defaults to None)
  3. deleter method of attribute x (keyword argument 'fdel', defaults to None)
  4. docstring (help text) of attribute x (keyword argument 'doc', defaults to None)

The help(property) reveals a handy example. Consider a class C with an attribute 'x':

>>> class C(object):
	def getx(self): return self._x
	def setx(self, value): self._x = value
	def delx(self): del self._x
	x = property(getx, setx, delx, "I'm the 'x' property.")

	
>>> c = C()
>>> c.x = 5					# invokes the setter setx()
>>> c.x						# invokes the getter getx()
5
>>> del c.x					# invokes the deleter delx()
>>> help(C)
# Among other help information
 |  x
 |      I'm the 'x' property.

The @property decorator offers an easy-on-the-eye way of achieving the above functionality:

>>> class C(object):
	@property
	def x(self):
		"I am the 'x' property."
		return self._x
		
	@x.setter
	def x(self, value):
		self._x = value
		
	@x.deleter
	def x(self):
		del self._x
		
# @property implies that the function declared below (x) is passed to the property constructor as its first argument, return value (a property object) of which is assigned to the function itself i.e. x = property(fget = x). @x.setter translates to x = property(fset = x), the x on the right is the function declared after the @x.setter notation. Similarly, @x.deleter translates to x = property(fdel = x). 

# The 'doc' keyword argument of the property constructor gets set to the docstring of the function after the @property notation. So, in actuality, the @property gets translated to x = property(fget = x, fset = None, fdel = None, doc = "I am the 'x' property.")

Don't get confused by the attribute x and the property object x. The example in the documentation is ambiguous. The property object provides us with a handle to the attribute x. Here's a much cleaner example:

>>> class C(object):
	@property
	def propertyObjectOfAttrX(self):
		"I'm the 'x' property."
		return self._x
	@propertyObjectOfAttrX.setter
	def propertyObjectOfAttrX(self, value):
		self._x = value
	@propertyObjectOfAttrX.deleter
	def propertyObjectOfAttrX(self):
		del self._x

		
>>> c = C()
>>> c.propertyObjectOfAttrX = 5
>>> c.propertyObjectOfAttrX
5
>>> del c.propertyObjectOfAttrX

Note that in order for the @property decorator to work, the names of additional functions (setter and deleter) must be the same as the name of the property object i.e. the getter propertyObjectOfAttrX. Otherwise, you'll get an Attribute Error(s). I'll leave that to you to explore.

Read-only attribute

To make an attribute read-only, only specify the getter method of the attribute.

>>> class C(object):
	def __init__(self):
		self._x = 2
	@property
	def propertyObjectOfAttrX(self):
		"I'm the 'x' property."
		return self._x

>>> c = C()
>>> c.propertyObjectOfAttrX
2
>>> c.propertyObjectOfAttrX = 10
Traceback (most recent call last):
    c.propertyObjectOfAttrX = 10
AttributeError: can't set attribute

This happens because the @property decorator call translates to propertyObjectOfAttrX = property(fget = propertyObjectOfAttrX, fset = None, fdel = None, doc = "I'm the 'x' property."). Unless the user knows the variable name( i.e. _x), he/she cannot set it. This is how the property objects enforce encapsulation.


See also:

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon

Leave a Reply