It Started Simple… Then It Didn’t

We learned that functions help us reuse code and keep our programs organized. We also learned that classes allow us to group related functionality together. However, as a program grows, it becomes impractical to keep all of these functions and classes in a single file.

To keep our code manageable, we can organize related functionality into separate modules. A module allows us to place functions and classes in their own file while still being able to use them in other parts of our program.

This approach keeps the main program focused on the high-level logic, while supporting functionality lives in its own module. It also promotes code reuse, since we can share these modules without copying code between files.

In this article, we will learn how to import functions and classes from modules so we can reuse them in our programs.


What Is a Module?

In Python, a module is simply a file that contains Python code. This code can include functions, classes, and variables that we want to reuse. The filename (without the .py extension) becomes the module name.

Instead of placing everything in a single file, we can organize related functionality into separate modules. This makes our code easier to read and maintain.

For example, we can create a file called friends.py:

def welcome_user(username):
    """Display a simple welcome."""
    print(f"Welcome {username.title()}! We're glad you're here.")
    
def introduce_friend(name, favorite_fruit):
    """Introduce a friend and their favorite fruit."""
    print(f"This is {name}. Their favorite fruit is {favorite_fruit}.")

Here, friends.py is the module, and it contains two reusable functions.


Why Modules Are Useful

Modules help us in a few simple but important ways:

1. Keep code organized
We can split code into smaller files. Each file focuses on one thing, which makes the program easier to read.

2. Reuse code easily
We can write a function once and use it in many places, without copying the code again.

3. Focus on the main task
Our main program stays clean and easy to follow, while details are kept in separate modules.

4. Avoid name conflicts
Different modules can have functions with the same name. Python keeps them separate using the module name.

In the next section, we’ll learn how to import these functions and use them.


Importing Modules

Let’s create another file called main.py and use the friends.py module:

import friends

friends.welcome_user("Alan") # Welcome Alan! We're glad you're here.
friends.introduce_friend("Bob", "Apple") # This is Bob. Their favorite fruit is Apple.

When Python sees import friends, it loads the code from friends.py so we can use it here.

To use the functions, we write:

module_name.function_name()

In this case:

  • friends.welcome_user()
  • friends.introduce_friend()

Using an Alias

Sometimes, different modules may have functions or variables with the same name. This can cause confusion or unexpected behavior. To avoid this, we can give a module a different name using the as keyword:

import friends as fr

fr.welcome_user("Alan") # Welcome Alan! We're glad you're here.
fr.introduce_friend("Bob", "Apple") # This is Bob. Their favorite fruit is Apple.

By using an alias like fr, we make it clear which module we are using. It also helps prevent name conflicts when working with multiple modules.

As a bonus, aliases can also make the code shorter and easier to type when the module name is long.


Importing Specific Functions

Instead of importing everything, we can import only what we need:

from friends import welcome_user

welcome_user("Alan") # Welcome Alan! We're glad you're here.
introduce_friend("Bob", "Apple")  # NameError: name 'introduce_friend' is not defined

When we import a specific function like this, we can call it directly by its name, without using the module name.

That’s why we can write:

welcome_user("Alan")

instead of:

friends.welcome_user("Alan")

However, only welcome_user is imported here. Since introduce_friend was not imported, calling it will result in an error.


Importing Multiple Functions

If we need more than one function, we can import them together in a single line.

from friends import welcome_user, introduce_friend

welcome_user("Alan") # Welcome Alan! We're glad you're here.
introduce_friend("Bob", "Apple") # This is Bob. Their favorite fruit is Apple.

This keeps the code clean while still allowing us to call each function directly without the module name.


Using Aliases for Functions

Just like modules, individual functions can also have aliases. This is useful if the function name is long or if we want a clearer name in our code.

from friends import welcome_user as greet

greet("Alan") # Welcome Alan! We're glad you're here.

Here, we renamed welcome_user to greet. This can make the code feel more natural to read.


Importing Everything (Not Recommended)

Python also allows us to import everything from a module at once.

from friends import *

welcome_user("Alan") # Welcome Alan! We're glad you're here.
introduce_friend("Bob", "Apple") # This is Bob. Their favorite fruit is Apple.

This lets us use all functions directly without specifying the module name.

However, this approach is not recommended. If multiple modules have functions with the same name, it can cause conflicts and make the code harder to understand.

It’s usually better to import only what you need, or use the module name to keep things clear.


Importing Classes

Just like functions, classes can also be stored in modules and reused in other files. This helps us keep our code organized, especially when classes start getting larger.

Let’s say we saved our HotelRoom class (which we made here) in a file called hotel.py.

class HotelRoom:
    """
    Represents a hotel room with basic details and availability status.
    """    
    hotel_name = "Hotel California"    
    def __init__(self, room_no, capacity):
        self.room_no = room_no
        self.capacity = capacity
        self.availability = True    
    
    def describe_room(self):
        print(f"Room number {self.room_no} with capacity {self.capacity}")    
    
    def check_in(self):
        if self.availability:
            self.availability = False
            print("Check-in successful.")
        else:
            print("Room is already occupied.")

    def check_out(self):
        if not self.availability:
            self.availability = True
            print(f"Room {self.room_no} is now available.")
        else:
            print(f"Room {self.room_no} is already available.")

    def is_room_available(self):
        return self.availability

Import the Module

We can import the entire module and access the class using dot notation:

import hotel

room1 = hotel.HotelRoom("101", 1)
print(room1.room_no) # 101

Here, hotel is the module name, and we use hotel.HotelRoom to create an object. This makes it clear where the class comes from.


Use an Alias

If the module name is long or we want to avoid name conflicts, we can give it a shorter name:

import hotel as h

room1 = h.HotelRoom("101", 1)
print(room1.room_no) # 101

This works the same way, but with a shorter name.


Import the Class Directly

If we only need one class, we can import it directly:

from hotel import HotelRoom

room1 = HotelRoom("101", 1)
print(room1.room_no) # 101

In this case, we can use HotelRoom directly without writing the module name.


Alias the Class

We can also rename the class when importing it:

from hotel import HotelRoom as Room

room1 = Room("101", 1)
print(room1.room_no) # 101

This can make the code shorter or match the naming style we prefer.


Import Everything

We can import all classes from a module:

from hotel import *

room1 = HotelRoom("101", 1)
print(room1.room_no) # 101

If a class depends on another class from a different module, you will need to import both. We’ll explore this idea in a later article.


Leave a Reply

Your email address will not be published. Required fields are marked *