5.2. Access Modifiers

5.2.1. Rationale

  • Attributes and methods are always public

  • No protected and private keywords

Attributes:

  • __name__ - system attribute or method

  • __name - private attribute

  • _name - protected attribute (by convention)

  • name_ - used while name collision

Methods:

  • __name__(self) - system method

  • __name(self) - private method

  • _name(self) - protected method (by convention)

  • name_(self) - used while name collision

5.2.2. Protected Attribute

  • _name - protected attribute (by convention)

Listing 5.87. Access modifiers
class Temperature:
    pass


temp = Temperature()
temp._value = 10

print(temp._value)  # IDE should warn, that you access protected member
# 10
Listing 5.88. Access modifiers
class Astronaut:
    def __init__(self, firstname, lastname):
        self._firstname = firstname
        self._lastname = lastname
        self.publicname = f'{firstname} {lastname[0]}.'


mark = Astronaut('Mark', 'Watney')

print(mark._firstname)  # IDE should warn: "Access to a protected member _firstname of a class "
# Mark

print(mark._lastname)  # IDE should warn: "Access to a protected member _lastname of a class "
# Watney

print(mark.publicname)
# Mark W.

print(mark.firstname)
# Traceback (most recent call last):
#     ...
# AttributeError: 'Astronaut' object has no attribute 'firstname'

print(mark.lastname)
# Traceback (most recent call last):
#     ...
# AttributeError: 'Astronaut' object has no attribute 'lastname'

5.2.3. Private Attribute

  • __name - private attribute

class Astronaut:
    def __init__(self, firstname, lastname):
        self.__firstname = firstname
        self.__lastname = lastname
        self.publicname = f'{firstname} {lastname[0]}.'


astro = Astronaut('Mark', 'Watney')

print(astro.publicname)
# Mark W.

print(astro.__firstname)
# Traceback (most recent call last):
#     ...
# AttributeError: 'Astronaut' object has no attribute '__firstname'

print(astro.__lastname)
# Traceback (most recent call last):
#     ...
# AttributeError: 'Astronaut' object has no attribute '__firstname'

print(astro.__dict__)
# {'_Astronaut__firstname': 'Mark',
#  '_Astronaut__lastname': 'Watney',
#  'publicname': 'Mark W.'}

5.2.4. System Attributes

  • __name__ - system attribute

Listing 5.89. obj.__dict__ - Getting dynamic fields and values
class Astronaut:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname


astro = Astronaut('Mark', 'Watney')

print(astro.__dict__)
# {'firstname': 'Mark',
#  'lastname': 'Watney'}
Listing 5.90. obj.__dict__ - Getting dynamic fields and values
class Astronaut:
    def __init__(self, firstname, lastname):
        self._firstname = firstname
        self._lastname = lastname
        self.publicname = f'{firstname} {lastname[0]}.'


astro = Astronaut('Mark', 'Watney')

print(astro.__dict__)
# {'_firstname': 'Mark',
#  '_lastname': 'Watney',
#  'publicname': 'Mark W.'}

public_attributes = {attribute: value
                     for attribute, value in astro.__dict__.items()
                     if not attribute.startswith('_')}

print(public_attributes)
# {'publicname': 'Mark W.'}

5.2.5. Protected Method

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

    def _get_fullname(self):
        return f'{self._firstname} {self._lastname}'

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


astro = Astronaut('Mark', 'Watney')

print(dir(astro))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
# '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_firstname',
# '_get_fullname', '_lastname', 'get_publicname']

public_methods = [method
                  for method in dir(astro)
                  if not method.startswith('_')]

print(public_methods)
# ['get_publicname']

5.2.6. Private Method

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

    def __get_fullname(self):
        return f'{self._firstname} {self._lastname}'

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


mark = Astronaut('Mark', 'Watney')

print(dir(mark))
# ['_Astronaut__get_fullname', '__class__', '__delattr__', '__dict__',
#  '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
#  '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
#  '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
#  '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
#  '__weakref__', '_firstname', '_lastname', 'get_publicname']

public_methods = [method
                  for method in dir(astro)
                  if not method.startswith('_')]

print(public_methods)
# ['get_publicname']

mark.__get_fullname()
# Traceback (most recent call last):
#     ...
# AttributeError: 'Astronaut' object has no attribute '__get_fullname'

5.2.7. System Method

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

    def __str__(self):
        return 'stringification'

    def __repr__(self):
        return 'representation'


mark = Astronaut('Mark', 'Watney')

print(str(mark))
# stringification

print(repr(mark))
# representation

5.2.8. Assignments

5.2.8.1. OOP Attribute Access Modifiers

  • Assignment name: OOP Attribute Access Modifiers

  • Last update: 2020-10-01

  • Complexity level: easy

  • Lines of code to write: 11 lines

  • Estimated time of completion: 8 min

  • Solution: solution/oop_attribute_access_modifiers.py

English
  1. Use data from "Input" section (see below)

  2. Define result: list[dict]

  3. Define class Iris with attributes

  4. Protected attributes: sepal_length, sepal_width, petal_length, petal_width

  5. Public attribute: species

  6. Iterate over DATA and add all public attributes to result

  7. Compare result with "Output" section (see below)

Polish
  1. Użyj danych z sekcji "Input" (patrz poniżej)

  2. Zdefiniuj result: list[dict]

  3. Define klasę Iris

  4. Chronione atrybuty: sepal_length, sepal_width, petal_length, petal_width

  5. Publiczne atrybuty: species

  6. Iteruj po DATA i dodaj wszystkie publiczne atrybuty do result

  7. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
DATA = [
    Iris(5.8, 2.7, 5.1, 1.9, 'virginica'),
    Iris(5.1, 3.5, 1.4, 0.2, 'setosa'),
    Iris(5.7, 2.8, 4.1, 1.3, 'versicolor'),
]
Output
>>> result  # doctest: +NORMALIZE_WHITESPACE
[{'species': 'virginica'},
 {'species': 'setosa'},
 {'species': 'versicolor'}]

5.2.8.2. OOP Attribute Access Dict

  • Assignment name: OOP Attribute Access Dict

  • Last update: 2020-10-01

  • Complexity level: medium

  • Lines of code to write: 35 lines

  • Estimated time of completion: 21 min

  • Solution: solution/oop_attribute_access_dict.py

English
  1. Use data from "Input" section (see below)

  2. Create result: list[Iris]

  3. Iterate over DATA skipping header

  4. Separate features from species in each row

  5. Append to result:

    • if species is "setosa" append instance of a class Setosa

    • if species is "versicolor" append instance of a class Versicolor

    • if species is "virginica" append instance of a class Virginica

  6. Initialize instances with features using *args notation

  7. Print instance class name and then both sum and mean

  8. Compare result with "Output" section (see below)

Polish
  1. Użyj danych z sekcji "Input" (patrz poniżej)

  2. Stwórz result: list[Iris]

  3. Iterując po DATA pomijając header

  4. Odseparuj features od species w każdym wierszu

  5. Dodaj do result:

    • jeżeli species jest "setosa" to dodaj instancję klasy Setosa

    • jeżeli species jest "versicolor" to dodaj instancję klasy Versicolor

    • jeżeli species jest "virginica" to dodaj instancję klasy Virginica

  6. Instancje inicjalizuj danymi z features używając notacji *args

  7. Wypisz nazwę stworzonej klasy oraz średnią z pomiarów

  8. Porównaj wyniki z sekcją "Output" (patrz poniżej)

Input
DATA = [
    ('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
]

class Iris:
    def __init__(self, sepal_length, sepal_width, petal_length, petal_width):
        self._sepal_length = sepal_length
        self._sepal_width = sepal_width
        self._petal_length = petal_length
        self._petal_width = petal_width

    def __repr__(self):
        raise NotImplementedError

    def values(self):
        raise NotImplementedError

    def mean(self):
        return sum(self.values()) / len(self.values())


class Setosa(Iris):
    pass

class Versicolor(Iris):
    pass

class Virginica(Iris):
    pass
Output
>>> result  # doctest: +NORMALIZE_WHITESPACE
[{'name': 'Virginica',  'mean': 3.88},
 {'name': 'Setosa',     'mean': 2.55},
 {'name': 'Versicolor', 'mean': 3.48},
 {'name': 'Virginica',  'mean': 4.15},
 {'name': 'Versicolor', 'mean': 3.9},
 {'name': 'Setosa',     'mean': 2.35}]
Hints
  • self.__class__.__name__

  • self.__dict__.values()

  • globals()[classname]