The “def” keyword does two things: It creates a function object, and then assigns a variable (our function name) to that function object. So when I say:
def foo(): return "I'm foo!"
Python creates a new function object. Inside of that object, we can see the bytecodes, the arity (i.e., number of parameters), and a bunch of other stuff having to do with our function.
Most of those things are located inside of the function object’s __code__ attribute. Indeed, looking through __code__ is a great way to learn how Python functions work. The arity of our function “foo”, for example, is available via foo.__code__.co_argcount. And the byte codes are in foo.__code__.co_code.
The individual attributes of the __code__ object are read-only. But the __code__ attribute itself isn’t! We can thus be a bit mischievous, and perform a brain transplant on our function:
def foo(): return "I'm foo!" def bar(): return "I'm bar!" foo.__code__ = bar.__code__
Now, when we run foo(), we’re actually running the code that was defined for “bar”, and we get:
"I'm in bar!"
This is not likely to be something you want to put in your actual programs, but it does demonstrate some of the power of the __code__ object, and how much it can tell us about our functions. Indeed, I’ve found over the last few years that exploring the __code__ object can be quite interesting!
Nearly every day, I teach a course in Python. And nearly every day, I thus use the Jupyter notebook: I do my live-coding demos in it, answer students’ questions using it, and also send it to my students at the end of the day, so that they can review my code without having to type furiously or take pictures of my screen.
I also encourage my students to install the notebook — not just so that they can open the file I send them at the end of each day, but also so that they can learn to experiment, iterate, and play with their code using a modern interactive environment.
My two-day class in regular expressions uses Python, but as a means to an end: I teach the barest essentials of Python in an hour or so, just so that we can read from files and search inside of them using regular expressions. For this class, it’s overkill to ask the participants to install Python, especially if they plan to use other languages in their work.
When I teach this class, then, I set up a server that is only used for my course. Students can all log into the Jupyter notebook, create and use their own notebook files, and avoid installing anything new on their own computers.
Moreover, this whole setup now takes me just a few minutes. And since the class lasts only two days, I’m paying a few dollars for the use of a server that doesn’t contain any valuable data on it. So if someone happens to break into the server or ruin it, or if one of my students happens to mess around with things, nothing from my own day-to-day work will be ruined or at risk.
How do I do this? It’s pretty simple, actually:
apt-get install unzip emacs wget curl python3-pip
pip3 install -U jupyter
jupyter notebook --generate-config
c.NotebookApp.open_browser = False # no browser needed on a server
c.NotebookApp.ip = '0.0.0.0' # listen on the network
NotebookApp.password = '' # don't require a password
c.NotebookApp.token = '' # don't require a security token
nohup jupyter notebook
Once this is done, your server should be ready! If you’re at the IP address 192.168.1.1, then you should at this point be able to point your browser to http://192.168.1.1:8888, and you’ll see the Jupyter notebook. You can, of course, change the port that’s being used; I’ve found that when working from a cafe, non-standard ports can sometimes be blocked. Do remember that low-numbered ports, such as 80, can only be used by root, so you’ll need something higher, such as 8000 or 8080.
Also note that working this way means that all users have identical permissions. This means that in theory, one student can delete, change, or otherwise cause problems in another student’s notebook. In practice, I’ve never found this to be a problem.
A bigger problem is that the notebook’s back end can sometimes fail. In such cases, you’ll need to ssh back into the server and restart the Jupyter notebook process. It’s frustrating, but relatively rare. By using “nohup”, I’ve been able to avoid the server going down whenever my ssh connection died and/or I logged out.
I’ve definitely found that using the Jupyter notebook has improved my teaching. Having my students use it in my courses has also improved their learning! And as you can see, setting it up is a quick operation, requiring no more than a few minutes just before class starts. Just don’t forget to shut down the server when you’re done, or you’ll end up paying for a server you don’t need.
Comprehensive documentation for setting up a Jupyter server, including security considerations, are at the (excellent) Jupyter documentation site.