\[ [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 "", line 1, in 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/)