Developer

Less painful getters and setters using properties in Python

It's a popular design guideline to require class attributes to be managed by methods, usually referred to as getter and setter methods. These methods increase the safety of your attributes, but come at a cost of simplicity and verbosity. With Python properties, you can have it both ways.

It's a popular design guideline to require class attributes to be managed by methods, usually referred to as getter and setter methods. These methods increase the safety of your attributes, but come at a cost of simplicity and verbosity. With Python properties, you can have it both ways.

We'll start by writing a simple class with a few attributes:

class A(object):
    x = 2
    y = 3
    z = 4

It works as you'd expect:

>>> i = A()
>>> i.x, i.y, i.z
(2, 3, 4)
>>> i.x = 0
>>> i.x
0

That's fine so long as we're happy to use the class as a simple record, but what if we needed to make one of the fields read only — we could write a getter and a setter for it:

class B(object):
	y = 3
	z = 4

	def __init__(self):
		self.__x = 2

	def getx(self):
		return self.__x

	def setx(self, val):
		print "x is read only"

Then the usage becomes:

>>> i = B()
>>> i.getx(), i.y, i.z
(2, 3, 4)
>>> i.setx()
x is read only
>>> i.x
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'B' object has no attribute 'x'
>>> dir(i)
['_B__x', '__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__weakref__', 'getx', 'y', 'z']
>>> i._B__x = 0
>> i.getx()
0

This is a good result if you want to protect x from accidentally being overwritten — but it still allows x to be changed if you want to dig around for it. Class attributes beginning with two underscores in python are renamed to include the class name. This allows you to define pseudo-private variables, which are disguised, but still accessible.

The B class, obviously, has a problem with the interface. Accessing the x field is now completely different from the other fields. We could fix this by writing get and set methods for the other two fields, but there's better way: properties.

Properties is a way of defining the methods of setting, getting and deleting attributes of a function. They let you use methods to define how the field should be accessed and then allow you to use the standard interface for fields.

We'll implement the class in another way:

class C(object):
	y = 3
	z = 4

	def __init__(self):
		self.__x = 2

	def getx(self):
		return self.__x

	def setx(self, val):
		print "x is read only"

	x = property(getx, setx)

This makes the variable x a property accessed with the functions getx and setx (you can give it an optional third parameter to control deleting, and a fourth parameter for the docstring, but we'll leave those out for now).

The C class allows us to use it's fields more naturally:

>>> i = C()
>>> i.x, i.y, i.z
(2, 3, 4)
>>> i.x = 5
x is read only
>>> dir(i)
['_C__x', '__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__weakref__', 'getx', 'setx', 'x', 'y', 'z']
>>> i._C__x = 5
>>> i.x, i.y, i.z
(5, 3, 4)

You can still get to the underlying variable backing it, if you look hard enough.

There's another way of implementing this function without using properties which allows you to achieve the same affect — overriding the default attribute setting method:

class D(object):
    x = 2
    y = 3
    z = 4

    def __setattr__(self, name, val):
        if name != 'x':
            self.__dict__[name] = val
        else:
            print "%s is read only" % (name)

Usage is almost identical:

>>> i = D()
>>> i.x, i.y, i.z
(2, 3, 4)
>>> i.x = 5
x is read only
>>> dir(i)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__str__', '__weakref__', 'x', 'y', 'z']
>>> i.__dict__['x'] = 5
>>> i.x, i.y, i.z
(5, 3, 4)

Which method you choose is up to you, which is perhaps a departure from Python's "there's only one way to do it" philosophy. For small examples such as this, properties require more coding, since you've got to write a getter and a setter for each field — but if you're going to do different things then trying to squash them into the __setattr__ function could end up being much less readable and less maintainable.

Editor's Picks

Free Newsletters, In your Inbox