# 7.2. Accessor Property¶

## 7.2.1. Rationale¶

• myattribute = property() - creates property

• @myattribute.getter - getter for attribute

• @myattribute.setter - setter for attribute

• @myattribute.deleter - deleter for attribute

• Method name must be the same as attribute name

• myattribute has to be property

• @property - creates property and a getter

class MyClass:
myattribute = property()

@myattribute.getter
def myattribute(self):
return ...

@myattribute.setter
def myattribute(self):
...

@myattribute.deleter
def myattribute(self):
...


## 7.2.2. Example¶

class KelvinTemperature:
value: float

t = KelvinTemperature()
t.value = -2               # Should raise ValueError('Kelvin cannot be negative')

class KelvinTemperature:
value: float

def __init__(self, initialvalue):
self.value = initialvalue

t = KelvinTemperature(-1)   # Should raise ValueError('Kelvin cannot be negative')
t.value = -2                # Should raise ValueError('Kelvin cannot be negative')

class KelvinTemperature:
value: float

def __init__(self, initialvalue):
if initialvalue < 0:
raise ValueError('Negative Kelvin Temperature')
self.value = initialvalue

t = KelvinTemperature()
t.value = -1

class KelvinTemperature:
_value: float

def __init__(self, initialvalue):
self.set_value(initialvalue)

def set_value(self, newvalue):
if newvalue < 0:
raise ValueError('Negative Kelvin Temperature')
self._value = newvalue

class KelvinTemperature:
_value: float
value = property()

def __init__(self, initialvalue):
self.value = initialvalue

@value.setter
def value(self, newvalue):
if newvalue < 0:
raise ValueError('Negative Kelvin Temperature')
self._value = newvalue


## 7.2.3. Use Cases¶

class Astronaut:
def __init__(self, firstname, lastname):
self._firstname = firstname
self._lastname = lastname

@property
def name(self):
return f'{self._firstname} {self._lastname[0]}.'

astro = Astronaut('Mark', 'Watney')
print(astro.name)
# Mark W.

class Astronaut:
name = property()

def __init__(self, firstname, lastname):
self._firstname = firstname
self._lastname = lastname

@name.getter
def name(self):
return f'{self._firstname} {self._lastname[0]}.'

astro = Astronaut('Mark', 'Watney')
print(astro.name)
# Mark W.

class Temperature:
kelvin = property()
__value: float

def __init__(self, kelvin=None):
self.__value = kelvin

@kelvin.setter
def kelvin(self, newvalue):
if newvalue < 0:
raise ValueError('Negative Kelvin Temperature')
else:
self.__value = newvalue

t = Temperature()
t.kelvin = 10
t.kelvin = -1
# Traceback (most recent call last):
# ValueError: Negative Kelvin Temperature


## 7.2.4. Attribute Access¶

• Java way: Setter and Getter

• Pythonic way: Properties, Reflection, Descriptors

Accessing class fields using setter and getter:

class Astronaut:
def __init__(self, name=None):
self._name = name

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

def get_name(self):
return self._name

astro = Astronaut()
astro.set_name('Mark Watney')
print(astro.get_name())
# Mark Watney


Accessing class fields. Either put name as an argument for __init__() or create dynamic field in runtime:

class Astronaut:
def __init__(self, name=None):
self.name = name

astro = Astronaut()
astro.name = 'Jan Twardowski'
print(astro.name)
# Jan Twardowski


## 7.2.5. Property class¶

• Property's arguments are method pointers get_name, set_name, del_name and a docstring

• Don't do that

class Astronaut:
def __init__(self, name=None):
self._name = name

def get_name(self):
return self._name

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

def del_name(self):
del self._name

name = property(get_name, set_name, del_name, "I am the 'name' property.")


## 7.2.6. @property Decorator¶

• Prefer name = property()

class Astronaut:
name = property()

def __init__(self, name=None):
self._name = name

@name.getter
def name(self):
return self._name

@name.setter
def name(self, value):
self._name = value

@name.deleter
def name(self):
del self._name

class Astronaut:
def __init__(self, name=None):
self._name = name

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

@name.setter
def name(self, value):
self._name = value

@name.deleter
def name(self):
del self._name


## 7.2.7. Use Cases¶

Astronaut:

class Astronaut:
def __init__(self):
self._name = None

def set_name(self, name):
self._name = name.title()

def get_name(self):
if self._name:
firstname, lastname = self._name.split()
return f'{firstname} {lastname[0]}.'

def del_name(self):
self._name = None

astro = Astronaut()

astro.set_name('JaN TwARdoWskI')
print(astro.get_name())
# Jan T.

astro.del_name()
print(astro.get_name())
# None

class Astronaut:
name = property()

def __init__(self):
self._name = None

@name.getter
def name(self):
if self._name:
firstname, lastname = self._name.split()
return f'{firstname} {lastname[0]}.'

@name.setter
def name(self, name):
self._name = name.title()

@name.deleter
def name(self):
self._name = None

astro = Astronaut()

astro.name = 'JAN TwARdoWski'
print(astro.name)
# Jan T.

del astro.name
print(astro.name)
# None


Temperature:

class Temperature:
def __init__(self, initial_temperature):
self._protected = initial_temperature

@property
def value(self):
print('You are trying to access a value')
return self._protected

t = Temperature(100)

print(t.value)
# You are trying to access a value
# 100

class Temperature:
def __init__(self, initial_temperature):
self._protected = initial_temperature

@property
def value(self):
return self._protected

@value.setter
def value(self, new_value):
if new_value < 0.0:
raise ValueError('Kelvin Temperature cannot be negative')
else:
self._protected = new_value

t = Temperature(100)
t.value = -10
# Traceback (most recent call last):
# ValueError: Kelvin Temperature cannot be negative

class Temperature:
def __init__(self, initial_temperature):
self._protected = initial_temperature

@property
def value(self):
return self._protected

@value.deleter
def value(self):
print('Resetting temperature')
self._protected = 0.0

t = Temperature(100)

del t.value
# Resetting temperature

print(t.value)
# 0.0


## 7.2.8. Assignments¶

"""
* Assignment: Accessor Property Getter
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min

English:
1. Define class Point with x, y, z attributes
2. Define property position in class Point
3. Accessing position returns (x, y, z)
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj klasę Point z atrybutami x, y, z
2. Zdefiniuj property position w klasie Point
3. Dostęp do position zwraca (x, y, z)
4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0

>>> pt = Point(x=1, y=2, z=3)
>>> pt.x, pt.y, pt.z
(1, 2, 3)
>>> pt.position
(1, 2, 3)
"""

class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z


"""
* Assignment: Accessor Property Setter
* Complexity: easy
* Lines of code: 9 lines
* Time: 5 min

English:
1. Define class Point with x, y, z attributes
2. Define property position in class Point
3. Setting position:
a. If argument is not list, tuple, set raise Type Error
b. If argument has length other than 3, raise Value
b. Else sets x, y, z attributes from sequence
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj klasę Point z atrybutami x, y, z
2. Zdefiniuj property position w klasie Point
3. Ustawianie position:
a. Jeżeli argument nie jest list, tuple, set podnieś TypeError
b. Jeżeli argument nie ma długości 3, podnieś ValueError
b. W przeciwnym wypadku ustaw kolejne atrybuty x, y, z z sekwencji
4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0

>>> pt = Point(x=1, y=2, z=3)
>>> pt.x, pt.y, pt.z
(1, 2, 3)
>>> pt.position = 4, 5, 6
>>> pt.x, pt.y, pt.z
(4, 5, 6)
>>> pt.position = [7, 8, 9]
>>> pt.x, pt.y, pt.z
(7, 8, 9)
>>> pt.position = 1, 2
Traceback (most recent call last):
ValueError
>>> pt.position = {'a':1, 'b':2}
Traceback (most recent call last):
TypeError
"""

class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z


"""
* Assignment: Accessor Property Deleter
* Complexity: easy
* Lines of code: 6 lines
* Time: 5 min

English:
1. Define class Point with x, y, z attributes
2. Define property position in class Point
3. Deleting position sets all attributes to 0 (x=0, y=0, z=0)
4. Run doctests - all must succeed

Polish:
1. Zdefiniuj klasę Point z atrybutami x, y, z
2. Zdefiniuj property position w klasie Point
3. Usunięcie position ustawia wszystkie atrybuty na 0 (x=0, y=0, z=0)
4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
>>> import sys; sys.tracebacklimit = 0

>>> pt = Point(x=1, y=2, z=3)
>>> pt.x, pt.y, pt.z
(1, 2, 3)
>>> del pt.position
>>> pt.x, pt.y, pt.z
(0, 0, 0)
"""

class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z