This lesson doesn’t involve writing a ton of new code, but we learn how to just draft out code that we complete later. And then, how to organize it within a project so that all the code isn’t in a giant mess of a file.
The finished code for this exercise can be found at:
A more complicated version of this exercise can be found at:
This exercise assumes you've completed the previous exercise, in which you've created a basic woz.py
file in your projects/show-me-where
folder:
compciv-2016 └── projects └── show-me-where └── woz.py
You should still have a Terminal window open to your system shell, at your show-me-where
subdirectory, i.e., this should still work:
$ python woz.py
For this phase, we'll start out by creating a new file in show-me-where/
: geocoding.py
compciv-2016 └── projects └── show-me-where └── geocoding.py └── woz.py
In a later exercise, we'll actually make it do something with a real-world API. But for this exercise, we'll "mock" it out. No matter what the user inputs into the function, the geocode
command will always print out a pre-cannned response.
Nothing much. It's not really "fake" so much as a "placeholder". We know that our main program, woz.py
, will eventually call a geocode()
function that takes in an argument – a location
string from the user – and then return geodata from Mapzen, e.g. latitude
and longitude
.
But we'll do the actual geocoding in a later lesson. So we'll just write the code to respond with a "dummy string" for now.
"geocode"
option in woz.pyIn woz.py
, look for this code:
elif the_command == 'geocode':
print("TODO: geocode it")
We want this part of the program to accept user input, similar to "hello"
:
if the_command == "hello":
usertext = input("What is your name? ")
Except, we want to ask for the user's location. So refer to the "hello"
snippet above, and change it accordingly. Here's one way to do it:
elif the_command == 'geocode':
userlocation = input("What is your location? ")
print("OK...geocoding:", userlocation)
print("""
{
"latitude": 99,
"longitude": -42
}
""")
Now run woz.py
from the command line:
$ python woz.py
What do you want to do? geocode
What is your location? Somewhere
OK...geocoding: Somewhere
{
"latitude": 99,
"longitude": -42
}
That dictionary/JSON-formatted text is just dummy text, of course. But it works for now.
So this is a bit of software engineering and Python organization.
We don't want to do our geocoding inside of woz.py
– because things would get very cluttered. Instead, we want to "package" a geocode()
function inside of a different file.
If you haven't done it already, create a new file named geocoding.py
and, for now, put it in the current folder with woz.py
:
compciv-2016 └── projects └── show-me-where └── geocoding.py └── woz.py
Now, inside geocoding.py, create a function named geocode
. For now, it can just print the same dummy text as woz.py
did, in our previous example:
def geocode(location):
return ("""
{
"latitude": 99,
"longitude": -42
}
""")
(again, the above snippet should be in geocoding.py__)
import geocoding
OK, now switch back to woz.py, and now add an import statement at the top of woz.py:
import geocoding
Now, further down in the code, change this section:
elif the_command == 'geocode':
userlocation = input("What is your location? ")
print("OK...geocoding:", userlocation)
print("""
{
"latitude": 99,
"longitude": -42
}
""")
To:
elif the_command == 'geocode':
userlocation = input("What is your location? ")
print("OK...geocoding:", userlocation)
georesult = geocoding.geocode(userlocation)
print(georesult)
The result should be the same; all we've done is move the fake functionality into a separate file and function.
This is where things stand:
compciv-2016 └── projects └── show-me-where └── geocoding.py └── woz.py
utils
folderThings get a little messy when you throw a ton of files all in the same subfolder. Let's let woz.py
stand-alone for now. Which we can do by making a subfolder named utils
:
compciv-2016 └── projects └── show-me-where └── geocoding.py └── woz.py └── utils
Then, move geocoding.py
into utils
. In other words, it should no longer be at the same level as woz.py
. Move it into utils
:
compciv-2016 └── projects └── show-me-where └── woz.py └── utils └── geocoding.py
utils/__init__.py
This is going to seem like the most arbitrary thing. Basically, we want to make the utils
subfolder a package of files. And the way to do that is to create an empty file in utils
named __init__.py
: note the exact naming of the file, with double underscores before and after init
:
compciv-2016 └── projects └── show-me-where └── woz.py └── utils └── geocoding.py └── __init__.py
woz.py
One more maintenance thing.
In woz.py
, we had this import
statement:
import geocoding
But that won't work anymore because geocoding.py
is no longer in the same folder relative to woz.py
Now that geocoding.py
has been moved into a subfolder, utils
, we just have to change the import
target:
from utils import geocoding
i.e. "From the utils
folder, import the geocoding.py
file"
When you run woz.py
from the command line and say you want to "geocode"
, everything should work just as before.
And that's it. You've created a slightly more organized Python project by creating a package (which involves making that utils/__init__.py
file).
Remember all those times we've written this at the beginning of our scripts:
from os.path import join
Those were Python packages, too. Python packages let's us separate and organize code in different files, calling it as we need it in other files. Seems like a bit over-the-top right now, but that's only because we haven't written much code yet.