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

171 lines
4.5 KiB
Markdown

\[ [Index](index.md) | [Exercise 3.1](ex3_1.md) | [Exercise 3.3](ex3_3.md) \]
# Exercise 3.2
*Objectives:*
- Learn about attribute access
- Learn how use getattr(), setattr(), and related functions.
- Experiment with bound methods.
*Files Created:* `tableformat.py`
## (a) The Three Operations
The entire Python object system consists of just three core operations: getting, setting, and deleting
of attributes. Normally, these are accessed via the dot (.) like this:
```python
>>> s = Stock('GOOG', 100, 490.1)
>>> s.name # get
'GOOG'
>>> s.shares = 50 # set
>>> del s.shares # delete
>>>
```
The three operations are also available as functions. For example:
```python
>>> getattr(s, 'name') # Same as s.name
'GOOG'
>>> setattr(s, 'shares', 50) # Same as s.shares = 50
>>> delattr(s, 'shares') # Same as del s.shares
>>>
```
An additional function `hasattr()` can be used to probe an object for the existence of an attribute:
```python
>>> hasattr(s, 'name')
True
>>> hasattr(s, 'blah')
False
>>>
```
## (b) Using getattr()
The `getattr()` function is extremely useful for
writing code that processes objects in an extremely generic way. To
illustrate, consider this example which prints out a set of
user-defined attributes:
```python
>>> s= Stock('GOOG', 100, 490.1)
>>> fields = ['name','shares','price']
>>> for name in fields:
print(name, getattr(s, name))
name GOOG
shares 100
price 490.1
>>>
```
## (c) Table Output
In [Exercise 3.1](ex3_1.md), you wrote a function `print_portfolio()`
that made a nicely formatted table. That function was custom tailored
to a list of `Stock` objects. However, it can be completely generalized
to work with any list of objects using the technique in part (b).
Create a new module called `tableformat.py`. In that program,
write a function `print_table()` that takes a sequence (list) of objects,
a list of attribute names, and prints a nicely formatted table. For example:
```python
>>> import stock
>>> import tableformat
>>> portfolio = stock.read_portfolio('Data/portfolio.csv')
>>> tableformat.print_table(portfolio, ['name','shares','price'])
name shares price
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
>>> tableformat.print_table(portfolio,['shares','name'])
shares name
---------- ----------
100 AA
50 IBM
150 CAT
200 MSFT
95 GE
50 MSFT
100 IBM
>>>
```
For simplicity, just have the `print_table()` function print each field in
a 10-character wide column.
## (d) Bound Methods
It may be surprising, but method calls are layered onto the machinery used
for simple attributes. Essentially, a method is an attribute that
executes when you add the required parentheses () to call it like a function. For
example:
```python
>>> s = Stock('GOOG',100,490.10)
>>> s.cost # Looks up the method
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
>>> s.cost() # Looks up and calls the method
49010.0
>>> # Same operations using getattr()
>>> getattr(s, 'cost')
<bound method Stock.cost of <__main__.Stock object at 0x409530>>
>>> getattr(s, 'cost')()
49010.0
>>>
```
A bound method is attached to the object where it came from. If that
object is modified, the method will see the modifications. You can
view the original object by inspecting the `__self__` attribute
of the method.
```python
>>> c = s.cost
>>> c()
49010.0
>>> s.shares = 75
>>> c()
36757.5
>>> c.__self__
<__main__.Stock object at 0x409530>
>>> c.__func__
<function cost at 0x37cc30>
>>> c.__func__(c.__self__) # This is what happens behind the scenes of calling c()
36757.5
>>>
```
Try it with the `sell()` method just to make sure you
understand the mechanics:
```python
>>> f = s.sell
>>> f.__func__(f.__self__, 25) # Same as s.sell(25)
>>> s.shares
50
>>>
```
\[ [Solution](soln3_2.md) | [Index](index.md) | [Exercise 3.1](ex3_1.md) | [Exercise 3.3](ex3_3.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/)