137 lines
3.9 KiB
Markdown
137 lines
3.9 KiB
Markdown
\[ [Index](index.md) | [Exercise 3.3](ex3_3.md) | [Exercise 3.5](ex3_5.md) \]
|
|
|
|
# Exercise 3.4
|
|
|
|
*Objectives:*
|
|
|
|
- Learn how to encapsulate object internals using private
|
|
attributes, properties, and slots
|
|
|
|
*Files Modified:* `stock.py`
|
|
|
|
## (a) Private attributes
|
|
|
|
As a general rule, attributes that are internal to a class should have a leading underscore.
|
|
In the previous exercise, the `Stock` class had a `types` class variable that was
|
|
used for converting rows of data. Change the code so that this variable has a leading
|
|
underscore on it.
|
|
|
|
## (b) Properties for computed attributes
|
|
|
|
Earlier, you defined a class `Stock`. For example:
|
|
|
|
```python
|
|
>>> s = Stock('GOOG',100,490.10)
|
|
>>> s.name
|
|
'GOOG'
|
|
>>> s.shares
|
|
100
|
|
>>> s.price
|
|
490.1
|
|
>>> s.cost()
|
|
49010.0
|
|
>>>
|
|
```
|
|
|
|
Using a property, turn `cost()` into an attribute that no longer requires the parentheses. For example:
|
|
|
|
```python
|
|
>>> s = Stock('GOOG', 100, 490.1)
|
|
>>> s.cost # Property. Computes the cost
|
|
49010.0
|
|
>>>
|
|
```
|
|
|
|
## (c) Enforcing Validation Rules
|
|
|
|
Using properties and private attributes, modify the `shares` attribute
|
|
of the `Stock` class so that it can only be assigned a non-negative
|
|
integer value. In addition, modify the `price` attribute so that it
|
|
can only be assigned a non-negative floating point value.
|
|
|
|
The new object should work almost exactly the same as
|
|
the old one except for extra type and value checking.
|
|
|
|
```python
|
|
>>> s = Stock('GOOG', 100, 490.10)
|
|
>>> s.shares = 50 # OK
|
|
>>> s.shares = '50'
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: Expected integer
|
|
>>> s.shares = -10
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: shares must be >= 0
|
|
|
|
>>> s.price = 123.45 # OK
|
|
>>> s.price = '123.45'
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: Expected float
|
|
>>> s.price = -10.0
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: price must be >= 0
|
|
>>>
|
|
```
|
|
|
|
## (d) Adding `__slots__`
|
|
|
|
Modify your new `Stock` class to use `__slots__`. You will find that
|
|
you have to use a different set of attribute names than
|
|
before--specifically, you will have to list the private attribute
|
|
names (e.g., if a property is storing a value in an attribute
|
|
`_shares`, that is the name you list in `__slots__`). Verify that the
|
|
class still works and that you can no longer add new attributes.
|
|
|
|
```python
|
|
>>> s = Stock('GOOG', 100, 490.10)
|
|
>>> s.spam = 42
|
|
Traceback (most recent call last):
|
|
File "<stdin>", line 1, in <module>
|
|
AttributeError: 'Stock' object has no attribute 'spam'
|
|
>>>
|
|
```
|
|
|
|
## (e) Reconciling Types
|
|
|
|
In the current `Stock` class, there is a `_types` class variable
|
|
that gives conversions when reading from a file, but there are also
|
|
properties that are enforcing types. Who is in charge of this show?
|
|
Fix the property definitions so that they use the types specified
|
|
in the `_types` class variable. Make sure the properties work
|
|
when types are changed via subclassing. For example:
|
|
|
|
```python
|
|
>>> from decimal import Decimal
|
|
>>> class DStock(Stock):
|
|
_types = (str, int, Decimal)
|
|
|
|
>>> s = DStock('AA', 50, Decimal('91.1'))
|
|
>>> s.price = 92.3
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: Expected a Decimal
|
|
>>>
|
|
```
|
|
|
|
**Discussion**
|
|
|
|
The resulting `Stock` class at the end of this exercise is a muddled
|
|
mess of properties, type checking, constructors, and other details.
|
|
Imagine how unpleasant it would be to maintain code that featured
|
|
dozens or hundreds of such class definitions.
|
|
|
|
We're going to figure out how to simplify things considerably, but it's
|
|
going to take some time and some more advanced techniques. Stay tuned.
|
|
|
|
\[ [Solution](soln3_4.md) | [Index](index.md) | [Exercise 3.3](ex3_3.md) | [Exercise 3.5](ex3_5.md) \]
|
|
|
|
----
|
|
`>>>` Advanced Python Mastery
|
|
`...` A course by [dabeaz](https://www.dabeaz.com)
|
|
`...` Copyright 2007-2023
|
|
|
|
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|