212 lines
4.2 KiB
Markdown
212 lines
4.2 KiB
Markdown
|
\[ [Index](index.md) | [Exercise 8.6](ex8_6.md) | [Exercise 9.2](ex9_2.md) \]
|
||
|
|
||
|
# Exercise 9.1
|
||
|
|
||
|
*Objectives:*
|
||
|
|
||
|
- A review of module basics
|
||
|
|
||
|
This exercise is about some of the more tricky details of library modules.
|
||
|
Start this exercise by creating a very simple library module:
|
||
|
|
||
|
```python
|
||
|
# simplemod.py
|
||
|
|
||
|
x = 42 # A global variable
|
||
|
|
||
|
# A simple function
|
||
|
def foo():
|
||
|
print('x is', x)
|
||
|
|
||
|
# A simple class
|
||
|
class Spam:
|
||
|
def yow(self):
|
||
|
print('Yow!')
|
||
|
|
||
|
# A scripting statement
|
||
|
print('Loaded simplemod')
|
||
|
```
|
||
|
|
||
|
## (a) Module Loading and System Path
|
||
|
|
||
|
Try importing the module you just created:
|
||
|
|
||
|
```python
|
||
|
>>> import simplemod
|
||
|
Loaded simplemod
|
||
|
>>> simplemod.foo()
|
||
|
x is 42
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
If this failed with an `ImportError`, your path setting is
|
||
|
flaky. Look at the value of `sys.path` and fix it.
|
||
|
|
||
|
```python
|
||
|
>>> import sys
|
||
|
>>> sys.path
|
||
|
... look at the result ...
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
## (b) Repeated Module Loading
|
||
|
|
||
|
Make sure you understand that modules are only loaded
|
||
|
once. Try a repeated import and notice how you do not see
|
||
|
the output from the `print` function:
|
||
|
|
||
|
```python
|
||
|
>>> import simplemod
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Try changing the value of `x` and see that a repeated import
|
||
|
has no effect.
|
||
|
|
||
|
```python
|
||
|
>>> simplemod.x
|
||
|
42
|
||
|
>>> simplemod.x = 13
|
||
|
>>> simplemod.x
|
||
|
13
|
||
|
>>> import simplemod
|
||
|
>>> simplemod.x
|
||
|
13
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Use `importlib.reload()` if you want to force a module to reload.
|
||
|
|
||
|
```python
|
||
|
>>> import importlib
|
||
|
>>> importlib.reload(simplemod)
|
||
|
Loaded simplemod
|
||
|
<module 'simplemod' from 'simplemod.py'>
|
||
|
>>> simplemod.x
|
||
|
42
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
`sys.modules` is a dictionary of all loaded modules. Take
|
||
|
a look at it, delete your module, and try a repeated import.
|
||
|
|
||
|
```python
|
||
|
>>> sys.modules
|
||
|
... look at output ...
|
||
|
>>> sys.modules['simplemod']
|
||
|
<module 'simplemod' from 'simplemod.py'>
|
||
|
>>> del sys.modules['simplemod']
|
||
|
>>> import simplemod
|
||
|
Loaded simplemod
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
## (c) from module import
|
||
|
|
||
|
Restart Python and import a selected symbol from a module.
|
||
|
|
||
|
```python
|
||
|
>>> ############### [ RESTART ] ###############
|
||
|
>>> from simplemod import foo
|
||
|
Loaded simplemod
|
||
|
>>> foo()
|
||
|
x is 42
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Notice how this loaded the entire module (observe the output of
|
||
|
the print function and how the `x` variable is used).
|
||
|
|
||
|
When you use `from`, the module object itself is not
|
||
|
visible. For example:
|
||
|
|
||
|
```python
|
||
|
>>> simplemod.foo()
|
||
|
Traceback (most recent call last):
|
||
|
File "<stdin>", line 1, in <module>
|
||
|
NameError: name 'simplemod' is not defined
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Make sure you understand that when you export things from a module,
|
||
|
they are simply name references. For example, try this and explain:
|
||
|
|
||
|
```python
|
||
|
>>> from simplemod import x,foo
|
||
|
>>> x
|
||
|
42
|
||
|
>>> foo()
|
||
|
x is 42
|
||
|
>>> x = 13
|
||
|
>>> foo()
|
||
|
x is 42 # !! Please explain
|
||
|
>>> x
|
||
|
13
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
## (d) Broken reload()
|
||
|
|
||
|
Create an instance:
|
||
|
|
||
|
```python
|
||
|
>>> import simplemod
|
||
|
>>> s = simplemod.Spam()
|
||
|
>>> s.yow()
|
||
|
Yow!
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Now, go to the `simplemod.py` file and change the implementation of `Spam.yow()` to the
|
||
|
following:
|
||
|
|
||
|
```python
|
||
|
# simplemod.py
|
||
|
...
|
||
|
|
||
|
class Spam:
|
||
|
def yow(self):
|
||
|
print('More Yow!')
|
||
|
```
|
||
|
|
||
|
Now, watch what happens on a reload. Do not restart Python for this part.
|
||
|
|
||
|
```python
|
||
|
>>> importlib.reload(simplemod)
|
||
|
Loaded simplemod
|
||
|
<module 'simplemod' from 'simplemod.py'>
|
||
|
>>> s.yow()
|
||
|
'Yow!'
|
||
|
>>> t = simplemod.Spam()
|
||
|
>>> t.yow()
|
||
|
'More Yow!'
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Notice how you have two instances of `Spam`, but they're using different implementations
|
||
|
of the `yow()` method. Yes, actually both versions of code are loaded at the same time.
|
||
|
You'll find other oddities as well. For example:
|
||
|
|
||
|
```python
|
||
|
>>> s
|
||
|
<simplemod.Spam object at 0x1006940b8>
|
||
|
>>> isinstance(s, simplemod.Spam)
|
||
|
False
|
||
|
>>> isinstance(t, simplemod.Spam)
|
||
|
True
|
||
|
>>>
|
||
|
```
|
||
|
|
||
|
Bottom line: It's probably best not to rely on reloading for anything important.
|
||
|
It might be fine if you're just trying to debug some things (as long as you're
|
||
|
aware of its limitations and dangers).
|
||
|
|
||
|
\[ [Solution](soln9_1.md) | [Index](index.md) | [Exercise 8.6](ex8_6.md) | [Exercise 9.2](ex9_2.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/)
|