Modules
Any Python program is a module and other programs can use the functions and variables in it (and classes too etc.), but for its contents to be reused easily there are some standard practices to follow. We'll work with our previous program containing the playing card functions and now named playing_cards.py
,
# playing_cards.py
SUITS = ('Clubs', 'Diamonds', 'Hearts', 'Spades')
FACE_VALUES = ('Ace', 'Two', 'Three', 'Four', 'Five', 'Six',
'Seven', 'Eight', 'Nine', 'Ten', 'Jack',
'Queen', 'King')
def suit(cardnum):
return SUITS[cardnum // 13]
def face_value(cardnum):
return FACE_VALUES[cardnum % 13]
card = 15
print("Card", card, "is the", face_value(card), "of", suit(card))
Now suppose that in our new Blackjack program we have a hand of card numbers and would like to display the cards in the hand. We write code like this,
# blackjack.py
...
print('You are holding,')
for card in hand:
print('The', label(card))
...
in hopes of generating output like this,
You are holding,
The Three of Diamonds
The Four of Spades
The Nine of Clubs
A first try
One program accesses another program's functions by importing the program, so to be able to use label
we need to import playing_cards.py
. To access the functions in playing_cards.py
we preface the function name with the module name separated by a period .
. We need to do this because modules are objects (like lists and strings) and the definitions inside them are their attributes. So label
's full name will be its module name, playing_cards
, followed by its function name, label
(just like we have done with math.sqrt
or random.randint
, think of it as a lastname.firstname system for now),
# blackjack.py
import playing_cards
hand = [15, 42, 8]
print('You are holding,')
for card in hand:
print('The', playing_cards.label(card))
Note that when importing a module we omit the .py
suffix, as we do when we refer to the module within the program, i.e. import playing_cards
instead of import playing_cards.py
and playing_cards.label
, instead of playing_cards.py.label
.
A problem
This almost works but the output isn't quite what we wanted,
>>>
Card 15 is the Three of Diamonds
Card 15 is the Three of Diamonds
You are holding,
The Three of Diamonds
The Four of Spades
The Nine of Clubs
>>>
We have the output we want, but above it we have extraneous output from playing_cards.py
. The reason we get the undesired output is that modules are run upon import, i.e. when you import a module the first thing Python does is to run it. It runs it becuse this interprets the code in the module making the definitions in the file available to the current program.
One fix to eliminate the undesired otuput would be to delete the print
statements from playing_cards.py
. But this is an ugly fix, not an elegant solution. Those print
statements in playing_cards.py
may be serving a purpose. In fact standard practice is to include code in all modules to test the functions in the module (these are called unit tests). The last thing we want to do is remove code that tests our module and verifies that it works!
A solution
Fortunately Python can tell if a program is running on its own or being
imported. When it is running on its own, it is assigned the
name __main__
. The double underscores are another Python standard
practice: they signal that this is an internal Python name. You should
never give a variable of your own a name beginning with double
underscores. When it is imported on the other hand it is assigned a name
based on its file name less the .py
suffix. So when we
execute playing_cards.py
on its own its name is __main__
, but when
we import it it's name is playing_cards
. Watch. If I add a statement
to display the module's __name__
attribute (look for it on line 16)
# playing_cards.pySUITS = ('Clubs', 'Diamonds', 'Hearts', 'Spades')
FACE_VALUES = ('Ace', 'Two', 'Three', 'Four', 'Five', 'Six',
'Seven', 'Eight', 'Nine', 'Ten', 'Jack',
'Queen', 'King')
def suit(cardnum):
return SUITS[cardnum // 13]
def face_value(cardnum):
return FACE_VALUES[cardnum % 13]
def label(cardnum):
return face_value(cardnum) + " of " + suit(cardnum)
print('My name is', __name__) # Line 16!
card = 15
print("Card", card, "is the", face_value(card), "of", suit(card))
print("Card", card, "is the", label(card))
when I run playing_cards.py
it displays,
>>>
My name is __main__
Card 15 is the Three of Diamonds
Card 15 is the Three of Diamonds
but when I run blackjack.py
look what it says its name is,
>>>
My name is playing_cards
Card 15 is the Three of Diamonds
Card 15 is the Three of Diamonds
You are holding,
The Three of Diamonds
The Four of Spades
The Nine of Clubs
I know this has been a long explanation, but it points us to a simple
solution. We will introduce an if
test into playing_cards.py
that will
see what its current name is. If it is __main__
it will run the tests
and otherwise it will not. This way the tests will not be run when the
file is imported, but are still available by running the module on its
own. Our modified playing_cards.py
will look like this,
# playing_cards.py
SUITS = ('Clubs', 'Diamonds', 'Hearts', 'Spades')
FACE_VALUES = ('Ace', 'Two', 'Three', 'Four', 'Five', 'Six',
'Seven', 'Eight', 'Nine', 'Ten', 'Jack',
'Queen', 'King')
def suit(cardnum):
return SUITS[cardnum // 13]
def face_value(cardnum):
return FACE_VALUES[cardnum % 13]
def label(cardnum):
return face_value(cardnum) + " of " + suit(cardnum)
if __name__ == '__main__':
card = 15
print("Card", card, "is the", face_value(card), "of", suit(card))
print("Card", card, "is the", label(card))