3.2. OOP Access Modifiers¶
Attributes and methods are always public
No protected and private keywords
Protecting is only by convention 1
name
- public attribute_name
- protected attribute (non-public by convention)__name
- private attribute (name mangling)__name__
- system attributename_
- avoid name collisionname(self)
- public method_name(self)
- protected method (non-public by convention)__name(self)
- private method (name mangling)__name__(self)
- system methodname_(self)
- avoid name collision
3.2.1. Example¶
>>> class Public:
... firstname: str
... lastname: str
>>>
>>> class Protected:
... _firstname: str
... _lastname: str
>>>
>>> class Private:
... __firstname: str
... __lastname: str
3.2.2. DataClasses¶
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Public:
... firstname: str
... lastname: str
>>>
>>>
>>> @dataclass
... class Protected:
... _firstname: str
... _lastname: str
>>>
>>>
>>> @dataclass
... class Private:
... __firstname: str
... __lastname: str
3.2.3. Public Attribute¶
name
- public attribute
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... firstname: str
... lastname: str
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> vars(astro)
{'firstname': 'Mark', 'lastname': 'Watney'}
>>>
>>> print(astro.firstname)
Mark
>>>
>>> print(astro.lastname)
Watney
3.2.4. Protected Attribute¶
_name
- protected attribute (non-public by convention)IDE should warn: "Access to a protected member _firstname of a class"
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... _firstname: str
... _lastname: str
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> vars(astro)
{'_firstname': 'Mark', '_lastname': 'Watney'}
>>>
>>> print(astro._firstname) # IDE should warn: "Access to a protected member _firstname of a class"
Mark
>>>
>>> print(astro._lastname) # IDE should warn: "Access to a protected member _lastname of a class"
Watney
3.2.5. Private Attribute¶
__name
- private attribute (name mangling)
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... __firstname: str
... __lastname: str
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> vars(astro)
{'_Astronaut__firstname': 'Mark', '_Astronaut__lastname': 'Watney'}
>>>
>>> print(astro._Astronaut__firstname)
Mark
>>>
>>> print(astro._Astronaut__lastname)
Watney
>>>
>>> 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 '__lastname'
3.2.6. Show Attributes¶
vars()
displayobj.__dict__
>>> class Astronaut:
... def __init__(self, firstname, lastname):
... self._firstname = firstname
... self._lastname = lastname
... self.publicname = f'{firstname} {lastname[0]}.'
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> vars(astro)
{'_firstname': 'Mark', '_lastname': 'Watney', 'publicname': 'Mark W.'}
>>>
>>> public_attributes = {attribute: value
... for attribute, value in vars(astro).items()
... if not attribute.startswith('_')}
>>>
>>> protected_attributes = {attribute: value
... for attribute, value in vars(astro).items()
... if attribute.startswith('_')}
>>>
>>>
>>> print(public_attributes)
{'publicname': 'Mark W.'}
>>>
>>> print(protected_attributes)
{'_firstname': 'Mark', '_lastname': 'Watney'}
3.2.7. System Attributes¶
__name__
- Current moduleobj.__class__
obj.__dict__
- Getting dynamic fields and valuesobj.__doc__
- Docstringobj.__annotations__
- Type annotations of an objectobj.__module__
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... firstname: str
... lastname: str
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> vars(astro)
{'firstname': 'Mark', 'lastname': 'Watney'}
>>>
>>> print(astro.__dict__)
{'firstname': 'Mark', 'lastname': 'Watney'}
3.2.8. Protected Method¶
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... _firstname: str
... _lastname: str
...
... 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))
['__annotations__', '__class__', '__dataclass_fields__',
'__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__match_args__',
'__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']
3.2.9. 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]}.'
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> astro.__get_fullname()
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute '__get_fullname'
3.2.10. 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'
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> print(str(astro))
stringification
>>>
>>> print(repr(astro))
representation
3.2.11. Show Methods¶
dir()
>>> 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))
['_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']
3.2.12. References¶
3.2.13. Assignments¶
"""
* Assignment: OOP AccessModifiers Protected
* Complexity: easy
* Lines of code: 7 lines
* Time: 8 min
English:
1. Modify class `Iris` to add attributes:
a. Protected attributes: sepal_length, sepal_width, petal_length, petal_width
b. Public attribute: species
2. Run doctests - all must succeed
Polish:
1. Zmodyfikuj klasę `Iris` by dodać atrybuty:
a. Chronione atrybuty: sepal_length, sepal_width, petal_length, petal_width
b. Publiczne atrybuty: species`
2. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> isclass(Iris)
True
>>> iris = Iris(5.8, 2.7, 5.1, 1.9, 'virginica')
>>> assert hasattr(iris, '_sepal_length'), \
'Iris class instance should have protected _sepal_length attribute'
>>> assert hasattr(iris, '_sepal_width'), \
'Iris class instance should have protected _sepal_width attribute'
>>> assert hasattr(iris, '_petal_length'), \
'Iris class instance should have protected _petal_length attribute'
>>> assert hasattr(iris, '_petal_width'), \
'Iris class instance should have protected _petal_width attribute'
>>> assert hasattr(iris, 'species'), \
'Iris class instance should have public species attribute'
>>> assert hasattr(Iris, '__annotations__'), \
'Iris class should have type annotations'
>>> Iris.__annotations__ # doctest: +NORMALIZE_WHITESPACE
{'_sepal_length': <class 'float'>,
'_sepal_width': <class 'float'>,
'_petal_length': <class 'float'>,
'_petal_width': <class 'float'>,
'species': <class 'str'>}
>>> 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'}]
"""
class Iris:
pass
"""
* Assignment: OOP AccessModifiers Dict
* Complexity: medium
* Lines of code: 8 lines
* Time: 8 min
English:
1. Create `result: list[Iris]`
2. Iterate over `DATA` skipping header
3. Separate `features` from `species` in each row
4. 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`
5. Initialize instances with `features` using `*features` notation
6. Run doctests - all must succeed
Polish:
1. Stwórz `result: list[Iris]`
2. Iterując po `DATA` pomiń nagłówek
3. Odseparuj `features` od `species` w każdym wierszu
4. Dodaj do `result`:
a. jeżeli `species` to "setosa" to dodaj instancję klasy `Setosa`
b. jeżeli `species` to "versicolor" to dodaj instancję klasy `Versicolor`
c. jeżeli `species` to "virginica" to dodaj instancję klasy `Virginica`
5. Instancje inicjalizuj danymi z `features` używając notacji `*features`
6. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `globals()[classname]`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> 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)]
"""
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