Contents
Attention Students: this file is strictly supplemental. You are in no way expected to read any of it, and none of the following information is necessary for you to complete the assignment! This document exists to explain how this assignment works, and why it is structured the way it is.
Some of the things we have done in this assignment are non-standard and — if you continue reading — you will come to learn that this is in part due to the fact that Python has room for interpretation in terms of what is “right”.
Throughout the course you have learned about various aspects of the Python packaging
system, in particular how to import
functions and classes from other files. In the
world of python, we refer to the file as a “module”. In this course, we have restricted
our use of modules to be in the same directory as one another. For example, consider
the directory structure, where all methods in second.py
return a str
:
directory/
first.py
- def print_message()
second.py
- def hello()
- def there()
- def beautiful()
- def world()
What we have done so far in the course, would be to in first.py
, do something like
# Option 1: import everything
from second import *
def print_message():
""" Prints a welcome message. """
message = hello() + there() + beautiful() + world()
print(message)
as we know, we can also import specific functions. In the following example, we will
not import the function beautiful()
, and instead implement our own.
# Option 2: import specific functions
from second import hello, there, world
def beautiful():
""" Returns the string ``"BEAUTIFUL"``. """
return "BEAUTIFUL"
def print_message():
""" Prints a welcome message. """
message = hello() + there() + beautiful() + world()
print(message)
The natural question being “what if I want to be able to use both beautiful()
functions in my code?” We have a few different options, the one you are most familiar
with is to instead import just the module:
# Option 3.1
import second
def beautiful():
""" Returns the string ``"BEAUTIFUL"``. """
return "BEAUTIFUL"
def print_message(use_first):
"""
Prints a welcome message.
:Parameters:
``use_first`` (bool)
If ``True``, use the ``beautiful()`` defined in this module. Otherwise,
use the ``beautiful()`` function defined in the ``second`` module.
"""
if use_first:
message = second.hello() + second.there() + beautiful() + second.world()
else:
message = second.hello() + second.there() + second.beautiful() + second.world()
print(message)
Using the as
keyword, though, we can also import the second.beautiful
function
as a different name so that we can use either method without issue:
# Option 3.2
from second import hello, there, world
from second import beautiful as other_beautiful
def beautiful():
""" Returns the string ``"BEAUTIFUL"``. """
return "BEAUTIFUL"
def print_message(use_first):
"""
Prints a welcome message.
:Parameters:
``use_first`` (bool)
If ``True``, use the ``beautiful()`` defined in this module. Otherwise,
use the ``beautiful()`` function defined in the ``second`` module.
"""
if use_first:
message = hello() + there() + beautiful() + world()
else:
message = hello() + there() + other_beautiful() + world()
print(message)
Wow! This is one of the most useful features of Python: it’s import system is exceptionally flexible! However, you may notice that the following will cause a “problem”:
# Which beautiful will be used?
from second import *
# At this point we have a function ``beautiful()`` in the global namespace, which is
# defined in the ``second`` module.
def beautiful():
""" Returns the string ``"BEAUTIFUL"``. """
return "BEAUTIFUL"
# Uh-Oh! We now have redefined the ``beautiful()`` function _for this file_. So
# when we call ``beautiful()`` below, it will use the *latest* definition.
def print_message():
""" Prints a welcome message. """
message = hello() + there() + beautiful() + world()
print(message)
If that wasn’t enough, if you moved the import
statement to below the definition
of the beautiful()
function in this file, we will now use the second.beautiful()
function since it was brought in last.
Tip
The first takeaway from all of this: Python is flexible and allows you to do many
different styles of imports for a reason. Make sure you understand what your
import
statements are doing, and what side-effects they can have.
Note
In the wild, many people will use from <some module> import *
to gain access to
things they need. As we can see from above, this is not wrong. But it can lead
to hard-to-find bugs when two modules define the same function. We advise exercising
extreme caution when using import *
, or better — avoid using it altogether!
Everything we have discussed up until this point, though, has been when the two files are next to each other in the same directory. So now let us consider a slightly different directory structure:
directory/
dirA/
first.py
- def print_message()
dirB/
second.py
- def hello()
- def there()
- def beautiful()
- def world()
third.py
- def what_a_wonderful_world()
The first.py
and second.py
files are no longer next to each other, so currently
we cannot import any functions between them. The Python packaging system, in all
honesty, is rather complex. This is in part due to the flexibility of import
statements we discussed above, but there is also another important factor: which
directory are you running the file from?
Recalling that ..
means “one directory up”, you may have heard that Python 2 lets
you do “Relative Imports”. So you’re first thought may be to try and use something
like from ..dirB import second
. This will work if your current directory
is dirA
. But here is the distinction: when you run a file, it inherits the current
working directory of the shell that runs it.
# Go to the dirA directory
$ cd directory/dirA
# Run our file
$ python first.py
In this scenario, the working directory when we ran first.py
was dirA
, so the
relative import would have worked. However, if instead we were one directory above:
# Go to the directory
$ cd directory
# Run our file
$ python dirA/first.py
We now have a problem: the current working directory was directory
, and first.py
tried to import ../dirB
. But there is no ../dirB
, because we were already in
the parent directory!
Warning
Relative imports are an interesting trick, but will likely only cause you grief and peril. There are a lot of reasons to use them, but they are well beyond the scope of this course. As a rule of thumb, avoid them altogether.
__main__.
py
¶The __main__
module for MacPan
This is the module with the script code to start up the App. Make sure that this module is in a folder with the following files:
File | Purpose |
---|---|
controller.py |
The primary controller class. |
model.py |
The model classes. |
view |
Directory with view classes. |
Moving any of these folders or files will prevent the game from working properly. You are free to add new files into these folders as you wish.
This module provides one method (notify_course_of_action()
), which serves to
give a slightly more helpful error message when a student tries to complete this
assignment with an incompatible version of Python installed.
Often times in the wild you will encounter long and complicated __main__.py
(as
well as more often, long and complicated __init__.py
files). We were concerned
about students being able to run the assignment, but did not want to detract from what
__main__.py
was actually doing. That is, the core mechanics of __main__.py
are relatively straightforward:
However, it seemed likely that some students may not have had the right version of
Anaconda Python. Depending on when you installed it, there were certain crucial updates
related to the core library we used for displaying the assignment. We elected to
separate this out into a different file so that when you read __main__.py
you would
be able to see more clearly what it is doing.
In short, this is a long way of saying the use of error_out.py
is non-standard, and
only exists due to the circumstances surrounding this particular assignment’s code-base.
error_out.
notify_course_of_action
()[source]¶This method checks to see what version of python is installed, and informs the user where to go to get the correct version of python for this semester.
If this code-base is used in the future, the default library will be PyQt5
, so
PyQt4
from __main__.py
and PyQt5
from below.semester
variable, and install_base
if the website layout
has changed to a new setup.anaconda_needs
variable for the correct version.Refer to the PyQt documentation for more information on switching to PyQt5
.
Return: |
|
---|