3.7 KiB
[ Index | Exercise 6.3 | Exercise 6.5 ]
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:
>>> 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.
>>> class Stock:
= ('name', 'shares', 'price')
_fields
>>> argstr = ','.join(Stock._fields)
>>> code = f'def __init__(self, {argstr}):\n'
>>> for name in Stock._fields:
+= f' self.{name} = {name}\n'
code >>> 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 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:
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:
class Stock(Structure):
= ('name', 'shares', 'price')
_fields
Stock.create_init()
The resulting class should work exactly the name way as before:
>>> s = Stock(name='GOOG', shares=100, price=490.1)
>>> s
'GOOG',100,490.1)
Stock(>>> s.shares = 50
>>> s.share = 50
Traceback (most recent call last):"<stdin>", line 1, in <module>
File "structure.py", line 12, in __setattr__
File 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 Exercise 2.1, you experimented with
namedtuple
objects in the collections
module.
Just to refresh your memory, here is how they worked:
>>> 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:
>>> import inspect
>>> print(inspect.getsource(namedtuple))
... look at the output ...>>>
[ Solution | Index | Exercise 6.3 | Exercise 6.5 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
.
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License