6.5. Access¶
6.5.1. Rationale¶
Attributes and methods are always public
No protected and private keywords
Protecting is only by convention [privatevar]
Attributes:
name
- public attribute
_name
- protected attribute (non-public by convention)
__name
- private attribute (name mangling)
__name__
- system attribute
name_
- avoid name collision
Methods:
name(self)
- public method
_name(self)
- protected method (non-public by convention)
__name(self)
- private method (name mangling)
__name__(self)
- system method
name_(self)
- avoid name collision
class Public:
firstname: str
lastname: str
def __init__(self):
self.firstname = 'Mark'
self.lastname = 'Watney'
class Protected:
_firstname: str
_lastname: str
def __init__(self):
self._firstname = 'Mark'
self._lastname = 'Watney'
class Private:
__firstname: str
__lastname: str
def __init__(self):
self.__firstname = 'Mark'
self.__lastname = 'Watney'
obj = Public()
print(obj.firstname)
# Mark
print(obj.lastname)
# Watney
print(obj.__dict__)
# {'firstname': 'Mark', 'lastname': 'Watney'}
obj = Protected()
print(obj._firstname) # IDE should warn: "Access to a protected member _firstname of a class"
# Mark
print(obj._lastname) # IDE should warn: "Access to a protected member _lastname of a class"
# Watney
print(obj.__dict__)
# {'_firstname': 'Mark', '_lastname': 'Watney'}
obj = Private()
print(obj.__firstname)
# Traceback (most recent call last):
# AttributeError: 'Private' object has no attribute '__firstname'
print(obj.__lastname)
# Traceback (most recent call last):
# AttributeError: 'Private' object has no attribute '__lastname'
print(obj.__dict__)
# {'_Private__firstname': 'Mark', '_Private__lastname': 'Watney'}
print(obj._Private__firstname)
# Mark
print(obj._Private__lastname)
# Watney
6.5.2. Protected Attribute¶
_name
- protected attribute (by convention)
Access modifiers:
class Temperature:
pass
temp = Temperature()
temp._value = 10
print(temp._value) # IDE should warn: "Access to a protected member _value of a class"
# 10
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'
6.5.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.'}
6.5.4. System Attributes¶
__name__
- system attribute
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'}
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.'}
6.5.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']
6.5.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'
6.5.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
6.5.8. Assignments¶
"""
* Assignment: OOP Access Protected
* Complexity: easy
* Lines of code: 7 lines
* Time: 8 min
English:
1. Use data from "Given" 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 "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Zdefiniuj `result: list[dict]`
3. Zdefiniuj 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ą "Tests" (patrz poniżej)
Tests:
>>> 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')]
>>> result = [{attribute: value}
... for row in DATA
... for attribute, value in row.__dict__.items()
... if not attribute.startswith('_')]
>>> result # doctest: +NORMALIZE_WHITESPACE
[{'species': 'virginica'},
{'species': 'setosa'},
{'species': 'versicolor'}]
"""
# Given
class Iris:
pass
"""
* Assignment: OOP Protected Dict
* Complexity: medium
* Lines of code: 8 lines
* Time: 8 min
English:
1. Use data from "Given" 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`:
a. if `species` is "setosa" append instance of a class `Setosa`
b. if `species` is "versicolor" append instance of a class `Versicolor`
c. if `species` is "virginica" append instance of a class `Virginica`
6. Initialize instances with `features` using `*args` notation
7. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (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`:
a. jeżeli `species` jest "setosa" to dodaj instancję klasy `Setosa`
b. jeżeli `species` jest "versicolor" to dodaj instancję klasy `Versicolor`
c. jeżeli `species` jest "virginica" to dodaj instancję klasy `Virginica`
6. Instancje inicjalizuj danymi z `features` używając notacji `*args`
7. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Hints:
* `globals()[classname]`
Tests:
>>> result # doctest: +NORMALIZE_WHITESPACE
[Virginica(5.8, 2.7, 5.1, 1.9),
Setosa(5.1, 3.5, 1.4, 0.2),
Versicolor(5.7, 2.8, 4.1, 1.3),
Virginica(6.3, 2.9, 5.6, 1.8),
Versicolor(6.4, 3.2, 4.5, 1.5),
Setosa(4.7, 3.2, 1.3, 0.2)]
"""
# Given
from dataclasses import dataclass
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')]
@dataclass(repr=False)
class Iris:
_sepal_length: float
_sepal_width: float
_petal_length: float
_petal_width: float
def __repr__(self):
name = self.__class__.__name__
args = tuple(self.__dict__.values())
return f'{name}{args}'
class Setosa(Iris):
pass
class Versicolor(Iris):
pass
class Virginica(Iris):
pass
result: list = []