Home About Me

Using Python's property to Keep Class Attributes Under Control

Python's property lets a class expose something that looks like a normal attribute while running code when it is read or assigned. That makes it useful for computed values, read-only attributes, validation, and keeping a clean public interface.

A computed attribute

The Person class below adds full_name as a property. Reading full_name triggers a print, while the actual writable state remains first_name and last_name; to change the displayed name, you change those underlying attributes indirectly.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def full_name(self):
        print('hello, %s %s' % (self.first_name, self.last_name))


p = Person('xiao', 'ming')
p.full_name    #  hello, xiao ming

The property has no setter, so full_name is read-only.

Validation when reading and assigning

When a value should be checked while it is read or set, property can sit in front of a backing attribute. Define a method with the desired attribute name, decorate it with @property, and add a setter with @name.setter.

class Person2(object):
    def __init__(self, name):
        self.name = name
        #self._name = name

    @property
    def name(self):
        print self._name

    @name.setter
    def name(self, value):
        if value != 'gimp':
            self._name = value

p = Person2('xiao ming')
p.name                     # xiao ming
p.name = 'dongdong'
p.name                     # dongdong
p.name = 'gimp'
p.name                     # dongdong

The example keeps the real value in _name. Because __init__ assigns through self.name, the setter runs during construction, so the same validation applies then. The setter ignores the value gimp; any other value replaces the backing attribute. The getter prints _name. The commented-out self._name = name line would bypass the setter and skip validation.

Using existing getter and setter methods

If a class already has methods for getting and setting a value, property can connect them to an attribute-style interface without rewriting them as decorated methods.

class Person3(object):
    def __init__(self, name):
        self._name = name

    def get_name(self):
        print self._name

    def set_name(self, value):
        self._name = value

    name = property(get_name, set_name)


p = Person3('mr.liu')
p.name                 # mr.liu
p.name = 'mr.zhang'
p.name                 # mr.zhang

property(get_name, set_name) connects an existing method pair to an attribute-style interface. The argument order is fixed: getter first, setter second, and deleter third if one is provided.

A practical Circle example

A small Circle class shows how naturally properties express derived values. Both area and perimeter are computed from radius whenever they are accessed.

import math
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return math.pi * self.radius ** 2

    @property
    def perimeter(self):
        return 2 * math.pi * self.radius

Because no setters are defined, area and perimeter are read-only. Changing radius changes what the next access returns.

Use it with restraint

Adding properties everywhere can make a class feel bloated. In larger programs, clear method names often communicate intent better than turning every operation into an attribute. Use property where it genuinely simplifies the public interface or enforces a useful rule, not simply because it is available.