Methods that look like attributes: compute values, validate assignments, and make data read-only.
# @property for a getter
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
Circle(5).radius
# ans: 5 (accessed like an attribute)
_radius
# @radius.setter
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Negative")
self._radius = value
c = Circle(5)
c.radius = 10
c.radius =
Assigning to the property runs the setter, which can reject bad values.
# property with a calculation
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def area(self):
return 3.14 * self._radius ** 2
Circle(5).area
# ans: 78.5
# property without a setter
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
person = Person("Alice")
# ans: person.name = "Bob" raises AttributeError
# @name.deleter
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.deleter
def name(self):
del self._name
person = Person("Alice")
del person.name
del person.name runs the deleter, which removes _name.
Predict each result, then press → to check.
# property vs method?
class MyClass:
@property
def value(self):
return 5
obj = MyClass()
obj.value
# ans: 5 (no parentheses)
# setter without a getter?
class MyClass:
@value.setter
def value(self, x):
pass
# ans: NameError (@property must define value first)
# assigning to a property?
class MyClass:
@property
def value(self):
return 5
obj = MyClass()
obj.value = 10
# ans: AttributeError (no setter)
# is a property cached?
class MyClass:
@property
def value(self):
print("Computing")
return 5
obj = MyClass()
obj.value
obj.value
# ans: prints "Computing" twice
# property validation?
class MyClass:
@property
def x(self):
return self._x
@x.setter
def x(self, value):
if value < 0:
raise ValueError()
self._x = value
# ans: obj.x = -1 raises ValueError; obj.x = 3 stores 3 in _x
# property in __init__?
class MyClass:
def __init__(self, x):
self.x = x # calls the setter
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
# ans: MyClass(3) goes through the setter and stores 3 in _x
So validation in a setter also protects the constructor.
# delete without a deleter?
class MyClass:
@property
def value(self):
return 5
obj = MyClass()
del obj.value
# ans: AttributeError
# property inheritance?
class Base:
@property
def x(self):
return 5
class Derived(Base):
pass
Derived().x
# ans: 5
# overriding a property?
class Base:
@property
def x(self):
return 5
class Derived(Base):
@property
def x(self):
return 10
Derived().x
# ans: 10
# property with arguments?
class MyClass:
@property
def value(self, arg):
return arg
# ans: TypeError when obj.value is read (a getter takes only self)
@property: read with obj.name, no parentheses.
@name.setter: runs on obj.name = v; validate here.
@name.deleter: runs on del obj.name.
No setter means assignment raises AttributeError.