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.