5.3. OOP Attribute 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 collision with built-ins
>>> class Public:
... firstname: str
... lastname: str
>>>
>>> class Protected:
... _firstname: str
... _lastname: str
>>>
>>> class Private:
... __firstname: str
... __lastname: str
5.3.1. 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
5.3.2. 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
5.3.3. Protected Attribute¶
_name
- protected attribute (non-public by convention)
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... _firstname: str
... _lastname: str
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
To list all the attributes once again we can use vars():
>>> vars(astro)
{'_firstname': 'Mark', '_lastname': 'Watney'}
Python will allow the following statement, however your IDE should warn you "Access to a protected member _firstname of a class":
>>> print(astro._firstname)
Mark
>>>
>>> print(astro._lastname)
Watney
5.3.4. 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'
5.3.5. Name Mangling¶
>>> class Person:
... def hello(self):
... return 'hello Person'
>>>
>>>
>>> class Astronaut(Person):
... def hello(self):
... return 'hello Astronaut'
>>>
>>>
>>> astro = Astronaut()
>>> astro.hello()
'hello Astronaut'
>>> class Person:
... def __hello(self):
... return 'hello Person'
>>>
>>>
>>> class Astronaut(Person):
... def __hello(self):
... return 'hello Astronaut'
>>>
>>>
>>> astro = Astronaut()
>>> astro._Astronaut__hello()
'hello Astronaut'
>>> astro._Person__hello()
'hello Person'
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Person:
... __firstname: str
... __lastname: str
>>>
>>> @dataclass
... class Astronaut(Person):
... __firstname: str
... __lastname: str
>>>
>>> astro = Astronaut('Mark', 'Watney')
Traceback (most recent call last):
TypeError: Astronaut.__init__() missing 2 required positional arguments: '_Astronaut__firstname' and '_Astronaut__lastname'
>>>
>>> astro = Astronaut('Mark', 'Watney', 'Melissa', 'Lewis')
>>>
>>> vars(astro)
{'_Person__firstname': 'Mark',
'_Person__lastname': 'Watney',
'_Astronaut__firstname': 'Melissa',
'_Astronaut__lastname': 'Lewis'}
5.3.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'}
5.3.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'}
5.3.8. References¶
5.3.9. Assignments¶
"""
* Assignment: OOP Access Dataclass
* Complexity: easy
* Lines of code: 5 lines
* Time: 3 min
English:
1. Modify dataclass `Iris` to add attributes:
a. Protected attributes: `sepal_length, sepal_width`
b. Private attributes: `petal_length, petal_width`
c. Public attribute: `species`
2. Run doctests - all must succeed
Polish:
1. Zmodyfikuj dataclass `Iris` aby dodać atrybuty:
a. Chronione atrybuty: `sepal_length, sepal_width`
b. Private attributes: `petal_length, petal_width`
c. Publiczne atrybuty: `species`
2. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> assert isclass(Iris)
>>> assert hasattr(Iris, '__annotations__')
>>> assert '_sepal_width' in Iris.__dataclass_fields__
>>> assert '_sepal_length' in Iris.__dataclass_fields__
>>> assert '_Iris__petal_width' in Iris.__dataclass_fields__
>>> assert '_Iris__petal_length' in Iris.__dataclass_fields__
>>> assert 'species' in Iris.__dataclass_fields__
"""
from dataclasses import dataclass
@dataclass
class Iris:
pass
"""
* Assignment: OOP Access Init
* Complexity: easy
* Lines of code: 6 lines
* Time: 5 min
English:
1. Modify class `Iris` to add attributes:
a. Protected attributes: `sepal_length, sepal_width`
b. Private attributes: `petal_length, petal_width`
c. Public attribute: `species`
2. Do not use `dataclass`
3. Run doctests - all must succeed
Polish:
1. Zmodyfikuj klasę `Iris` aby dodać atrybuty:
a. Chronione atrybuty: `sepal_length, sepal_width`
b. Private attributes: `petal_length, petal_width`
c. Publiczne atrybuty: `species`
2. Nie używaj `dataclass`
3. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> assert isclass(Iris)
>>> result = Iris(5.1, 3.5, 1.4, 0.2, 'setosa')
>>> assert hasattr(result, '_sepal_width')
>>> assert hasattr(result, '_sepal_length')
>>> assert hasattr(result, '_Iris__petal_width')
>>> assert hasattr(result, '_Iris__petal_length')
>>> assert hasattr(result, 'species')
"""
class Iris:
pass
"""
* Assignment: OOP Access Members
* Complexity: easy
* Lines of code: 3 lines
* Time: 8 min
English:
1. Define `result: list[dict]`
2. Define class `Iris` with attributes
3. Protected attributes: `sepal_length`, `sepal_width`, `petal_length`, `petal_width`
4. Public attribute: `species`
5. Iterate over `DATA` and add all public attributes to `result`
6. Run doctests - all must succeed
Polish:
1. Zdefiniuj `result: list[dict]`
2. klasę `Iris`
3. Chronione atrybuty: `sepal_length`, `sepal_width`, `petal_length`, `petal_width`
4. Publiczne atrybuty: `species`
5. Iteruj po `DATA` i dodaj wszystkie publiczne atrybuty do `result`
6. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> assert type(public) is dict
>>> assert all(type(k) is str for k,v in public.items())
>>> assert all(type(v) is str for k,v in public.items())
>>> assert type(protected) is dict
>>> assert all(type(k) is str for k,v in protected.items())
>>> assert all(type(v) is float for k,v in protected.items())
>>> assert type(private) is dict
>>> assert all(type(k) is str for k,v in private.items())
>>> assert all(type(v) is float for k,v in private.items())
>>> assert len(public) > 0, \
'public: list[dict] must not be empty'
>>> assert len(protected) > 0, \
'protected: list[dict] must not be empty'
>>> assert len(private) > 0, \
'private: list[dict] must not be empty'
>>> public
{'species': 'virginica'}
>>> protected
{'_sepal_width': 5.8, '_sepal_length': 2.7}
>>> private
{'_Iris__petal_width': 5.1, '_Iris__petal_length': 1.9}
"""
from dataclasses import dataclass
@dataclass
class Iris:
_sepal_width: float
_sepal_length: float
__petal_width: float
__petal_length: float
species: str
DATA = Iris(5.8, 2.7, 5.1, 1.9, 'virginica')
# All public attributes and their values
# type: dict[str,float|str]
public = ...
# All protected attributes and their values
# type: dict[str,float|str]
protected = ...
# All private attributes and their values
# type: dict[str,float|str]
private = ...