196 lines
4.7 KiB
Markdown
196 lines
4.7 KiB
Markdown
\[ [Index](index.md) | [Exercise 5.6](ex5_6.md) | [Exercise 6.2](ex6_2.md) \]
|
|
|
|
# Exercise 6.1
|
|
|
|
*Objectives:*
|
|
|
|
- Learn more about function argument passing conventions
|
|
|
|
*Files Created:* `structure.py`, `stock.py`
|
|
|
|
|
|
**IMPORTANT NOTE**
|
|
|
|
This exercise is going to start a long road of rewriting the `stock.py` file in a more
|
|
sane way. Before doing anything, copy your work in `stock.py` to a new file
|
|
`orig_stock.py`.
|
|
|
|
We're going to recreate the `Stock` class from scratch using some new techniques.
|
|
Make sure you have your unit tests from link:ex5_4.html[Exercise 5.4] handy. You'll want those.
|
|
|
|
If you define a function, you probably already know that it can be
|
|
called using a mix of positional or keyword arguments. For example:
|
|
|
|
```python
|
|
>>> def foo(x, y, z):
|
|
return x + y + z
|
|
|
|
>>> foo(1, 2, 3)
|
|
6
|
|
>>> foo(1, z=3, y=2)
|
|
6
|
|
>>>
|
|
```
|
|
|
|
You may also know that you can pass sequences and dictionaries as
|
|
function arguments using the * and ** syntax. For example:
|
|
|
|
```python
|
|
>>> args = (1, 2, 3)
|
|
>>> foo(*args)
|
|
6
|
|
>>> kwargs = {'y':2, 'z':3 }
|
|
>>> foo(1,**kwargs)
|
|
6
|
|
>>>
|
|
```
|
|
|
|
In addition to that, you can write functions that accept any number of
|
|
positional or keyword arguments using the * and ** syntax. For
|
|
example:
|
|
|
|
```python
|
|
>>> def foo(*args):
|
|
print(args)
|
|
|
|
>>> foo(1,2)
|
|
(1, 2)
|
|
>>> foo(1,2,3,4,5)
|
|
(1, 2, 3, 4, 5)
|
|
>>> foo()
|
|
()
|
|
>>>
|
|
>>> def bar(**kwargs):
|
|
print(kwargs)
|
|
|
|
>>> bar(x=1,y=2)
|
|
{'y': 2, 'x': 1}
|
|
>>> bar(x=1,y=2,z=3)
|
|
{'y': 2, 'x': 1, 'z': 3}
|
|
>>> bar()
|
|
{}
|
|
>>>
|
|
```
|
|
|
|
Variable argument functions are sometimes useful as a technique for
|
|
reducing or simplifying the amount of code you need to type. In this
|
|
exercise, we'll explore that idea for simple data structures.
|
|
|
|
## (a) Simplified Data Structures
|
|
|
|
In earlier exercises, you defined a class representing a stock like
|
|
this:
|
|
|
|
```python
|
|
class Stock:
|
|
def __init__(self,name,shares,price):
|
|
self.name = name
|
|
self.shares = shares
|
|
self.price = price
|
|
```
|
|
|
|
Focus on the `__init__()` method---isn't that a lot of
|
|
code to type each time you want to populate a structure? What if you
|
|
had to define dozens of such structures in your program?
|
|
|
|
In a file `structure.py`, define a base class
|
|
`Structure` that allows the user to define simple
|
|
data structures as follows:
|
|
|
|
```python
|
|
class Stock(Structure):
|
|
_fields = ('name','shares','price')
|
|
|
|
class Date(Structure):
|
|
_fields = ('year', 'month', 'day')
|
|
```
|
|
|
|
The `Structure` class should define an `__init__()`
|
|
method that takes any number of arguments and which looks for the
|
|
presence of a `_fields` class variable. Have the method
|
|
populate the instance from the attribute names in `_fields`
|
|
and values passed to `__init__()`.
|
|
|
|
Here is some sample code to test your implementation:
|
|
|
|
```python
|
|
>>> s = Stock('GOOG',100,490.1)
|
|
>>> s.name
|
|
'GOOG'
|
|
>>> s.shares
|
|
100
|
|
>>> s.price
|
|
490.1
|
|
>>> s = Stock('AA',50)
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: Expected 3 arguments
|
|
>>>
|
|
```
|
|
|
|
## (b) Making a Useful Representation
|
|
|
|
Modify the `Structure` class so that it produces a nice
|
|
representation when `repr()` is used. For example:
|
|
|
|
```python
|
|
>>> s = Stock('GOOG', 100, 490.1)
|
|
>>> s
|
|
Stock('GOOG',100,490.1)
|
|
>>>
|
|
```
|
|
|
|
## (c) Restricting Attribute Names
|
|
|
|
Give the `Structure` class a `__setattr__()` method that restricts
|
|
the allowed set of attributes to those listed in the `_fields` variable.
|
|
However, it should still allow any "private" attribute (e.g., name starting
|
|
with `_` to be set).
|
|
|
|
For example:
|
|
|
|
```python
|
|
>>> s = Stock('GOOG',100,490.1)
|
|
>>> s.shares = 50
|
|
>>> s.share = 50
|
|
Traceback (most recent call last):
|
|
File "<stdin>", line 1, in <module>
|
|
File "structure.py", line 13, in __setattr__
|
|
raise AttributeError('No attribute %s' % name)
|
|
AttributeError: No attribute share
|
|
>>> s._shares = 100 # Private attribute. OK
|
|
>>>
|
|
```
|
|
|
|
## (d) Starting Over
|
|
|
|
Create a new file `stock.py` (or delete all of your previous code). Start over by defining `Stock` as follows:
|
|
|
|
```python
|
|
# stock.py
|
|
|
|
from structure import Structure
|
|
|
|
class Stock(Structure):
|
|
_fields = ('name', 'shares', 'price')
|
|
|
|
@property
|
|
def cost(self):
|
|
return self.shares * self.price
|
|
|
|
def sell(self, nshares):
|
|
self.shares -= nshares
|
|
```
|
|
|
|
Once you've done this, run your `teststock.py` unit tests. You should get a lot of failures, but at least a
|
|
handful of the tests should pass.
|
|
|
|
\[ [Solution](soln6_1.md) | [Index](index.md) | [Exercise 5.6](ex5_6.md) | [Exercise 6.2](ex6_2.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/)
|