5.5. Properties

5.5.1. Accessing fields

5.5.1.1. Setter and Getter methods

  • Java way

  • don't do that in Python

Listing 5.70. 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

Then your code starts to look like this:

class MyClass:
    def __init__(self):
        self._x = None
        self._y = None

    def get_x(self):
        return self._x

    def set_x(self, value):
        self._x = value

    def del_x(self):
        del self._x

    def get_y(self):
        return self._y

    def set_y(self, value):
        self._x = value

    def del_y(self):
        del self._y

5.5.1.2. Rationale for setter and getter methods

Listing 5.71. HabitatOS Z-Wave sensor admin
from django.contrib import admin
from habitat._common.admin import HabitatAdmin
from habitat.sensors.models import ZWaveSensor


@admin.register(ZWaveSensor)
class ZWaveSensorAdmin(HabitatAdmin):
    change_list_template = 'sensors/change_list_charts.html'
    list_display = ['mission_date', 'mission_time', 'type', 'device', 'value', 'unit']
    list_filter = ['created', 'type', 'unit', 'device']
    search_fields = ['^date', 'device']
    ordering = ['-datetime']

    def get_list_display(self, request):
        list_display = self.list_display

        if request.user.is_superuser:
            list_display = ['earth_datetime'] + list_display

        return list_display

5.5.1.3. Direct attribute access

  • the Python way

Listing 5.72. Accessing class fields
class Astronaut:
    def __init__(self, name=None):
        self.name = name


astro = Astronaut()              # either put ``name`` as an argument for ``__init__()``
astro.name = 'Jan Twardowski'     # or create dynamic field in runtime

print(astro.name)

5.5.2. Properties

5.5.2.1. Rationale

  • Disable attribute modification

  • Logging value access

  • Check boundary

  • Raise exceptions (TypeError)

  • Check argument type

5.5.2.2. Property class

property()
# <property object at 0x10ff07940>

property().getter
# <built-in method getter of property object at 0x10ff07998>

property().setter
# <built-in method setter of property object at 0x10ff07940>

property().deleter
# <built-in method deleter of property object at 0x10ff07998>

5.5.2.3. Property decorator

  • @decorator syntax is just syntactic sugar; the syntax:

    class MyClass:
        @property
        def attribute(self):
            return self._attribute
    
  • really means the same thing as

    class MyClass:
        def attribute(self):
            return self._attribute
    
        attribute = property(attribute)
    

5.5.2.4. Creating properties with property class

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

Listing 5.73. Properties
class Astronaut:
    def __init__(self):
        self._protected = None

    def get_name(self):
        return self._protected

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

    def del_name(self):
        del self._protected

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

5.5.2.5. Creating properties with @property decorator

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

    @property
    def name(self):
        pass

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

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

    @name.deleter
    def name(self):
        del self._protected
Listing 5.74. Properties as a decorators
class Astronaut:
    def __init__(self):
        self._protected = None

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

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

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

5.5.3. Use Cases

5.5.3.1. Getter

  • @property - for defining getters

Listing 5.75. Using @property as a getter
class Temperature:
    def __init__(self, initial_temperature):
        self._protected = initial_temperature

    @property
    def value(self):
        pass

    @value.getter
    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
Listing 5.76. Using @property as a getter
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

5.5.3.2. Setter

  • @value.setter - defining setter for field value

  • Require value to be @property

Listing 5.77. @x.setter
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
# ValueError: Kelvin Temperature cannot be negative

5.5.3.3. Deleter

  • @value.deleter - for defining deleter for field value

  • Require value to be @property

Listing 5.78. @x.deleter
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

5.5.4. Assignments

5.5.4.1. Protocol Property

English
  1. Create class Point with x, y, z attributes

  2. Add position property which returns tuple (x, y, z)

  3. Deleting position sets all attributes to 0 (x=0, y=0, z=0)

  4. Prevent setting position

Polish
  1. Stwórz klasę Point z atrybutami x, y, z

  2. Dodaj property position, który zwraca tuple (x, y, z)

  3. Usunięcie position ustawia wszystkie atrybuty na 0 (x=0, y=0, z=0)

  4. Zablokuj edycję atrybutów