146 lines
3.7 KiB
Markdown
146 lines
3.7 KiB
Markdown
|
\[ [Index](index.md) | [Exercise 6.3](ex6_3.md) | [Exercise 6.5](ex6_5.md) \]
|
||
|
|
||
|
# Exercise 6.4
|
||
|
|
||
|
*Objectives:*
|
||
|
|
||
|
- Learn to create code with `exec()`
|
||
|
|
||
|
## (a) Experiment with exec()
|
||
|
|
||
|
Define a fragment of Python code in a string and try running it:
|
||
|
|
||
|
```python
|
||
|
>>> code = '''
|
||
|
for i in range(n):
|
||
|
print(i, end=' ')
|
||
|
'''
|
||
|
>>> n = 10
|
||
|
>>> exec(code)
|
||
|
0 1 2 3 4 5 6 7 8 9
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
That's interesting, but executing random code fragments is not
|
||
|
especially useful. A more interesting use of `exec()` is in making
|
||
|
code such as functions, methods, or classes. Try this example in
|
||
|
which we make an `__init__()` function for a class.
|
||
|
|
||
|
```python
|
||
|
>>> class Stock:
|
||
|
_fields = ('name', 'shares', 'price')
|
||
|
|
||
|
>>> argstr = ','.join(Stock._fields)
|
||
|
>>> code = f'def __init__(self, {argstr}):\n'
|
||
|
>>> for name in Stock._fields:
|
||
|
code += f' self.{name} = {name}\n'
|
||
|
>>> print(code)
|
||
|
def __init__(self, name,shares,price):
|
||
|
self.name = name
|
||
|
self.shares = shares
|
||
|
self.price = price
|
||
|
|
||
|
>>> locs = { }
|
||
|
>>> exec(code, locs)
|
||
|
>>> Stock.__init__ = locs['__init__']
|
||
|
|
||
|
>>> # Now try the class
|
||
|
>>> s = Stock('GOOG', 100, 490.1)
|
||
|
>>> s.name
|
||
|
'GOOG'
|
||
|
>>> s.shares
|
||
|
100
|
||
|
>>> s.price
|
||
|
490.1
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
In this example, an `__init__()` function is made directly from the `_fields` variable.
|
||
|
There are no weird hacks involving a special `_init()` method or stack frames.
|
||
|
|
||
|
## (b) Creating an `__init__()` function
|
||
|
|
||
|
In link:ex6_3.txt[Exercise 6.3], you wrote code that inspected the
|
||
|
signature of the `__init__()` method to set the attribute names
|
||
|
in a `_fields` class variable. For example:
|
||
|
|
||
|
```python
|
||
|
class Stock(Structure):
|
||
|
def __init__(self, name, shares, price):
|
||
|
self._init()
|
||
|
|
||
|
Stock.set_fields()
|
||
|
```
|
||
|
|
||
|
Instead of inspecting the `__init__()` method, write a class method
|
||
|
`create_init(cls)` that creates an `__init__()` method from the value of
|
||
|
`_fields`. Use the `exec()` function to do this as shown above.
|
||
|
Here's how a user will use it:
|
||
|
|
||
|
```python
|
||
|
class Stock(Structure):
|
||
|
_fields = ('name', 'shares', 'price')
|
||
|
|
||
|
Stock.create_init()
|
||
|
```
|
||
|
|
||
|
The resulting class should work exactly the name way as before:
|
||
|
|
||
|
```python
|
||
|
>>> s = Stock(name='GOOG', shares=100, price=490.1)
|
||
|
>>> 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 12, in __setattr__
|
||
|
raise AttributeError('No attribute %s' % name)
|
||
|
AttributeError: No attribute share
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Modify the `Stock` class in progress to use the `create_init()` function as shown.
|
||
|
Verify with your unit tests as before.
|
||
|
|
||
|
While you're at it, get rid of the `_init()` and `set_fields()`
|
||
|
methods on the `Structure` class--that approach was kind of weird.
|
||
|
|
||
|
## (c) Named Tuples
|
||
|
|
||
|
In link:ex2_1.html[Exercise 2.1], you experimented with `namedtuple` objects
|
||
|
in the `collections` module. Just to refresh your memory, here is how
|
||
|
they worked:
|
||
|
|
||
|
```python
|
||
|
>>> from collections import namedtuple
|
||
|
>>> Stock = namedtuple('Stock', ['name', 'shares', 'price'])
|
||
|
>>> s = Stock('GOOG', 100, 490.1)
|
||
|
>>> s.name
|
||
|
'GOOG'
|
||
|
>>> s.shares
|
||
|
100
|
||
|
>>> s[1]
|
||
|
100
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Under the covers, the `namedtuple()` function is creating code as a string
|
||
|
and executing it using `exec()`. Look at the code and marvel:
|
||
|
|
||
|
```python
|
||
|
>>> import inspect
|
||
|
>>> print(inspect.getsource(namedtuple))
|
||
|
... look at the output ...
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
\[ [Solution](soln6_4.md) | [Index](index.md) | [Exercise 6.3](ex6_3.md) | [Exercise 6.5](ex6_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/)
|