python-mastery/Exercises/ex6_1.md
2023-07-17 17:45:29 +02:00

196 lines
4.6 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 [Exercise 5.4](ex5_4.md) 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
![](https://i.creativecommons.org/l/by-sa/4.0/88x31.png). This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)