Added solution code
This commit is contained in:
@@ -363,16 +363,22 @@ Frameworks / libraries sometimes use it for advanced features involving composit
|
||||
## Exercises
|
||||
|
||||
In Section 4, you defined a class `Stock` that represented a holding of stock.
|
||||
In this exercise, we will use that class.
|
||||
In this exercise, we will use that class. Restart the interpreter and make a
|
||||
few instances:
|
||||
|
||||
```python
|
||||
>>> ================================ RESTART ================================
|
||||
>>> from stock import Stock
|
||||
>>> goog = Stock('GOOG',100,490.10)
|
||||
>>> ibm = Stock('IBM',50, 91.23)
|
||||
>>>
|
||||
```
|
||||
|
||||
### Exercise 5.1: Representation of Instances
|
||||
|
||||
At the interactive shell, inspect the underlying dictionaries of the two instances you created:
|
||||
|
||||
```python
|
||||
>>> from stock import Stock
|
||||
>>> goog = Stock('GOOG',100,490.10)
|
||||
>>> ibm = Stock('IBM',50, 91.23)
|
||||
>>> goog.__dict__
|
||||
... look at the output ...
|
||||
>>> ibm.__dict__
|
||||
@@ -393,12 +399,14 @@ Try setting a new attribute on one of the above instances:
|
||||
>>>
|
||||
```
|
||||
|
||||
In the above output, you’ll notice that the `goog` instance has a attribute `date` whereas the `ibm` instance does not.
|
||||
In the above output, you'll notice that the `goog` instance has a
|
||||
attribute `date` whereas the `ibm` instance does not. It is important
|
||||
to note that Python really doesn't place any restrictions on
|
||||
attributes. For example, the attributes of an instance are not
|
||||
limited to those set up in the `__init__()` method.
|
||||
|
||||
It is important to note that Python really doesn’t place any restrictions on attributes.
|
||||
The attributes of an instance are not limited to those set up in the `__init__()` method.
|
||||
|
||||
Instead of setting an attribute, try placing a new value directly into the `__dict__` object:
|
||||
Instead of setting an attribute, try placing a new value directly into
|
||||
the `__dict__` object:
|
||||
|
||||
```python
|
||||
>>> goog.__dict__['time'] = '9:45am'
|
||||
@@ -408,21 +416,22 @@ Instead of setting an attribute, try placing a new value directly into the `__di
|
||||
```
|
||||
|
||||
Here, you really notice the fact that an instance is just a layer on
|
||||
top of a dictionary. *Note: it should be emphasized that direct
|
||||
manipulation of the dictionary is uncommon—you should always write
|
||||
your code to use the (.) syntax.*
|
||||
top of a dictionary. Note: it should be emphasized that direct
|
||||
manipulation of the dictionary is uncommon--you should always write
|
||||
your code to use the (.) syntax.
|
||||
|
||||
### Exercise 5.3: The role of classes
|
||||
|
||||
The definitions that make up a class definition are shared by all instances of that class.
|
||||
Notice, that all instances have a link back to their associated class:
|
||||
The definitions that make up a class definition are shared by all
|
||||
instances of that class. Notice, that all instances have a link back
|
||||
to their associated class:
|
||||
|
||||
```python
|
||||
>>> goog.__class__
|
||||
... look at output ...
|
||||
>>> ibm.__class__
|
||||
... look at output ...
|
||||
>>>
|
||||
>>>
|
||||
```
|
||||
|
||||
Try calling a method on the instances:
|
||||
@@ -435,7 +444,7 @@ Try calling a method on the instances:
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice that the name *cost* is not defined in either `goog.__dict__`
|
||||
Notice that the name 'cost' is not defined in either `goog.__dict__`
|
||||
or `ibm.__dict__`. Instead, it is being supplied by the class
|
||||
dictionary. Try this:
|
||||
|
||||
@@ -455,7 +464,9 @@ Try calling the `cost()` method directly through the dictionary:
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how you are calling the function defined in the class definition and how the `self` argument gets the instance.
|
||||
Notice how you are calling the function defined in the class
|
||||
definition and how the `self` argument gets the instance.
|
||||
|
||||
Try adding a new attribute to the `Stock` class:
|
||||
|
||||
```python
|
||||
@@ -482,21 +493,22 @@ However, notice that it is not part of the instance dictionary:
|
||||
```
|
||||
|
||||
The reason you can access the `foo` attribute on instances is that
|
||||
Python always checks the class dictionary if it can’t find something
|
||||
on the instance itself.
|
||||
Python always checks the class dictionary if it can't find something
|
||||
on the instance itself.
|
||||
|
||||
This part of the exercise illustrates something known as a class
|
||||
Note: This part of the exercise illustrates something known as a class
|
||||
variable. Suppose, for instance, you have a class like this:
|
||||
|
||||
```python
|
||||
class Foo(object):
|
||||
a = 13 # Class variable
|
||||
def __init__(self,b):
|
||||
self.b = b # Instance variable
|
||||
a = 13 # Class variable
|
||||
def __init__(self,b):
|
||||
self.b = b # Instance variable
|
||||
```
|
||||
|
||||
In this class, the variable `a`, assigned in the body of the class itself, is a *class variable*.
|
||||
It is shared by all of the instances that get created.
|
||||
In this class, the variable `a`, assigned in the body of the
|
||||
class itself, is a "class variable." It is shared by all of the
|
||||
instances that get created. For example:
|
||||
|
||||
```python
|
||||
>>> f = Foo(10)
|
||||
@@ -517,10 +529,10 @@ It is shared by all of the instances that get created.
|
||||
>>>
|
||||
```
|
||||
|
||||
### Exercise 5.4: Bound Methods
|
||||
### Exercise 5.4: Bound methods
|
||||
|
||||
A subtle feature of Python is that invoking a method actually involves
|
||||
two steps and something known as a bound method.
|
||||
two steps and something known as a bound method. For example:
|
||||
|
||||
```python
|
||||
>>> s = goog.sell
|
||||
@@ -529,11 +541,12 @@ two steps and something known as a bound method.
|
||||
>>> s(25)
|
||||
>>> goog.shares
|
||||
75
|
||||
>>>
|
||||
>>>
|
||||
```
|
||||
|
||||
Bound methods actually contain all of the pieces needed to call a method.
|
||||
For instance, they keep a record of the function implementing the method:
|
||||
Bound methods actually contain all of the pieces needed to call a
|
||||
method. For instance, they keep a record of the function implementing
|
||||
the method:
|
||||
|
||||
```python
|
||||
>>> s.__func__
|
||||
@@ -549,8 +562,8 @@ This is the same value as found in the `Stock` dictionary.
|
||||
>>>
|
||||
```
|
||||
|
||||
Take a close look at both references do `0x10049af50`. They are both the same in `s` and `Stock.__dict__['sell']`.
|
||||
Bound methods also record the instance, which is the `self` argument.
|
||||
Bound methods also record the instance, which is the `self`
|
||||
argument.
|
||||
|
||||
```python
|
||||
>>> s.__self__
|
||||
@@ -558,8 +571,8 @@ Stock('GOOG',75,490.1)
|
||||
>>>
|
||||
```
|
||||
|
||||
When you invoke the function using `()` all of the pieces come together.
|
||||
For example, calling `s(25)` actually does this:
|
||||
When you invoke the function using `()` all of the pieces come
|
||||
together. For example, calling `s(25)` actually does this:
|
||||
|
||||
```python
|
||||
>>> s.__func__(s.__self__, 25) # Same as s(25)
|
||||
@@ -572,7 +585,7 @@ For example, calling `s(25)` actually does this:
|
||||
|
||||
Make a new class that inherits from `Stock`.
|
||||
|
||||
```python
|
||||
```
|
||||
>>> class NewStock(Stock):
|
||||
def yow(self):
|
||||
print('Yow!')
|
||||
@@ -603,7 +616,7 @@ they will be searched for attributes.
|
||||
>>>
|
||||
```
|
||||
|
||||
Here’s how the `cost()` method of instance `n` above would be found:
|
||||
Here's how the `cost()` method of instance `n` above would be found:
|
||||
|
||||
```python
|
||||
>>> for cls in n.__class__.__mro__:
|
||||
|
||||
@@ -252,10 +252,10 @@ day-to-day coding.
|
||||
|
||||
## Exercises
|
||||
|
||||
### Exercise 5.6: Simple properties
|
||||
### Exercise 5.6: Simple Properties
|
||||
|
||||
Properties are a useful way to add "computed attributes" to an object.
|
||||
In Exercise 4.1, you created an object `Stock`. Notice that on your
|
||||
In `stock.py`, you created an object `Stock`. Notice that on your
|
||||
object there is a slight inconsistency in how different kinds of data
|
||||
are extracted:
|
||||
|
||||
@@ -271,17 +271,22 @@ are extracted:
|
||||
>>>
|
||||
```
|
||||
|
||||
Specifically, notice how you have to add the extra `()` to `cost` because it is a method.
|
||||
You can get rid of the extra `()` on `cost()` if you turn it into a property.
|
||||
Specifically, notice how you have to add the extra () to `cost` because it is a method.
|
||||
|
||||
You can get rid of the extra () on `cost()` if you turn it into a property.
|
||||
Take your `Stock` class and modify it so that the cost calculation works like this:
|
||||
|
||||
```python
|
||||
>>> ================================ RESTART ================================
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG', 100, 490.1)
|
||||
>>> s.cost
|
||||
49010.0
|
||||
>>>
|
||||
```
|
||||
|
||||
Try calling `s.cost()` as a function and observe that it doesn’t work now that `cost` has been defined as a property.
|
||||
Try calling `s.cost()` as a function and observe that it
|
||||
doesn't work now that `cost` has been defined as a property.
|
||||
|
||||
```python
|
||||
>>> s.cost()
|
||||
@@ -289,14 +294,19 @@ Try calling `s.cost()` as a function and observe that it doesn’t work now that
|
||||
>>>
|
||||
```
|
||||
|
||||
Making this change will likely break your earlier `pcost.py` program.
|
||||
You might need to go back and get rid of the `()` on the `cost()` method.
|
||||
|
||||
### Exercise 5.7: Properties and Setters
|
||||
|
||||
Modify the `shares` attribute so that the value is stored in a private
|
||||
attribute and that a pair of property functions are used to ensure
|
||||
that it is always set to an integer value.
|
||||
Here is an example of the expected behavior:
|
||||
Modify the `shares` attribute so that the value is stored in a
|
||||
private attribute and that a pair of property functions are used to ensure
|
||||
that it is always set to an integer value. Here is an example of the expected
|
||||
behavior:
|
||||
|
||||
```python
|
||||
>>> ================================ RESTART ================================
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG',100,490.10)
|
||||
>>> s.shares = 50
|
||||
>>> s.shares = 'a lot'
|
||||
@@ -306,12 +316,13 @@ TypeError: expected an integer
|
||||
>>>
|
||||
```
|
||||
|
||||
### Exercise 5.8: Adding slots
|
||||
### Exercise 5.8: Adding slots
|
||||
|
||||
Modify the `Stock` class so that it has a `__slots__` attribute.
|
||||
Then, verify that new attributes can’t be added:
|
||||
Modify the `Stock` class so that it has a `__slots__` attribute. Then,
|
||||
verify that new attributes can't be added:
|
||||
|
||||
```python
|
||||
>>> ================================ RESTART ================================
|
||||
>>> from stock import Stock
|
||||
>>> s = Stock('GOOG', 100, 490.10)
|
||||
>>> s.name
|
||||
@@ -321,8 +332,9 @@ Then, verify that new attributes can’t be added:
|
||||
>>>
|
||||
```
|
||||
|
||||
When you use `__slots__`, Python actually uses a more efficient internal representation of objects.
|
||||
What happens if you try to inspect the underlying dictionary of `s` above?
|
||||
When you use `__slots__`, Python actually uses a more efficient
|
||||
internal representation of objects. What happens if you try to
|
||||
inspect the underlying dictionary of `s` above?
|
||||
|
||||
```python
|
||||
>>> s.__dict__
|
||||
|
||||
Reference in New Issue
Block a user