Initial commit
This commit is contained in:
211
Exercises/ex9_1.md
Normal file
211
Exercises/ex9_1.md
Normal file
@@ -0,0 +1,211 @@
|
||||
\[ [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/)
|
||||
Reference in New Issue
Block a user