Initial commit
This commit is contained in:
153
Exercises/ex5_4.md
Normal file
153
Exercises/ex5_4.md
Normal file
@@ -0,0 +1,153 @@
|
||||
\[ [Index](index.md) | [Exercise 5.3](ex5_3.md) | [Exercise 5.5](ex5_5.md) \]
|
||||
|
||||
# Exercise 5.4
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Learn more about closures
|
||||
|
||||
In this section, we look briefly at a few of the more unusual aspects of
|
||||
closures.
|
||||
|
||||
## (a) Closures as a data structure
|
||||
|
||||
One potential use of closures is as a tool for data encapsulation. Try this
|
||||
example:
|
||||
|
||||
```python
|
||||
def counter(value):
|
||||
def incr():
|
||||
nonlocal value
|
||||
value += 1
|
||||
return value
|
||||
|
||||
def decr():
|
||||
nonlocal value
|
||||
value -= 1
|
||||
return value
|
||||
|
||||
return incr, decr
|
||||
```
|
||||
|
||||
This code defines two inner functions that manipulate a value. Try it out:
|
||||
|
||||
```python
|
||||
>>> up, down = counter(0)
|
||||
>>> up()
|
||||
1
|
||||
>>> up()
|
||||
2
|
||||
>>> up()
|
||||
3
|
||||
>>> down()
|
||||
2
|
||||
>>> down()
|
||||
1
|
||||
>>>
|
||||
```
|
||||
|
||||
Notice how there is no class definition involved here. Moreover,
|
||||
there is no global variable either. Yet, the `up()` and `down()`
|
||||
functions are manipulating some "behind the scenes" value. It's
|
||||
fairly magical.
|
||||
|
||||
## (b) Closures as a code generator
|
||||
|
||||
In [Exercise 4.3](ex4_3.md), you developed a collection of
|
||||
descriptor classes that allowed type-checking of object attributes.
|
||||
For example:
|
||||
|
||||
```python
|
||||
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = Integer()
|
||||
price = Float()
|
||||
```
|
||||
|
||||
This kind of thing can also be implemented using closures. Define a file
|
||||
``typedproperty.py`` and put the following code in it:
|
||||
|
||||
```python
|
||||
# typedproperty.py
|
||||
|
||||
def typedproperty(name, expected_type):
|
||||
private_name = '_' + name
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return getattr(self, private_name)
|
||||
|
||||
@value.setter
|
||||
def value(self, val):
|
||||
if not isinstance(val, expected_type):
|
||||
raise TypeError(f'Expected {expected_type}')
|
||||
setattr(self, private_name, val)
|
||||
|
||||
return value
|
||||
```
|
||||
|
||||
This look pretty wild, but the function is effectively making code. You'd use it in
|
||||
a class definition like this:
|
||||
|
||||
```python
|
||||
from typedproperty import typedproperty
|
||||
|
||||
class Stock:
|
||||
name = typedproperty('name', str)
|
||||
shares = typedproperty('shares', int)
|
||||
price = typedproperty('price', float)
|
||||
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Verify that this class performs type-checking in the same way as the
|
||||
descriptor code.
|
||||
|
||||
Add function `String()`, `Integer()`, and `Float()` to the `typedproperty.py` file
|
||||
so that you can write the following code:
|
||||
|
||||
```python
|
||||
from typedproperty import String, Integer, Float
|
||||
|
||||
class Stock:
|
||||
name = String('name')
|
||||
shares = Integer('shares')
|
||||
price = Float('price')
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
## (c) Challenge: Eliminating names
|
||||
|
||||
Modify the `typedproperty.py` code so that attribute names are no-longer required:
|
||||
|
||||
```python
|
||||
from typedproperty import String, Integer, Float
|
||||
|
||||
class Stock:
|
||||
name = String()
|
||||
shares = Integer()
|
||||
price = Float()
|
||||
def __init__(self, name, shares, price):
|
||||
self.name = name
|
||||
self.shares = shares
|
||||
self.price = price
|
||||
```
|
||||
|
||||
Hint: To do this, recall the `__set_name__()` method of descriptor objects that
|
||||
gets called when descriptors are placed in a class definition.
|
||||
|
||||
\[ [Solution](soln5_4.md) | [Index](index.md) | [Exercise 5.3](ex5_3.md) | [Exercise 5.5](ex5_5.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/)
|
||||
Reference in New Issue
Block a user