3.7 KiB
[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:
>>> 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:
_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 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):
_fields = ('name', 'shares', 'price')
Stock.create_init()
The resulting class should work exactly the name way as before:
>>> 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 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](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
... Copyright 2007-2023
. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License