Gastón Ramos

Personal blog

← Back to the English articles

Leer este artículo en español

October 21, 2026.

Python for Ruby developers: modules, imports, and packages

I’m still learning Python from a Ruby developer’s point of view. After covering the basic differences and classes, objects, and variables, I want to understand how to organize code with modules, imports, and packages. I’m also taking notes on virtual environments and dependencies before moving on to Django.

1. What is a module in Python?

In Python, a module is usually a .py file containing reusable code: functions, classes, constants, and so on.

For example, suppose we have a file called calculator.py:

# calculator.py

def add(a, b):
    return a + b

We can use that function from another file by importing the module.

Comparison with Ruby

In Ruby, we use require to load code from another file:

require_relative "calculator"

In Python, we use import:

import calculator

An important difference: in Python, importing a module also binds its name in the current namespace.


2. import versus from ... import

Suppose we have this file:

# customer.py

class Customer:
    def __init__(self, name):
        self.name = name

Option A: import the whole module

import customer

person = customer.Customer("Juan")

We write the module name (customer) first, followed by the class name (Customer).

Option B: import the class directly

from customer import Customer

person = Customer("Juan")

Now we can use Customer without the customer. prefix.

Summary

Import Usage
import customer customer.Customer("Juan")
from customer import Customer Customer("Juan")
from customer import Customer as Client Client("Juan")

Note: import customer loads the module; it does not literally copy all its code into the importing file.


3. What is a package?

A package organizes several modules in a directory.

For example:

commerce/
├── main.py
└── shop/
    ├── __init__.py
    ├── customers.py
    └── products.py

In this example:

  • shop is the package.
  • customers.py is a module.
  • products.py is another module.
  • main.py is the program that uses those modules.

Suppose customers.py contains:

class Customer:
    def __init__(self, name):
        self.name = name

From main.py, we can import the class:

from shop.customers import Customer

customer = Customer("Juan")

How do we read this statement?

from shop.customers import Customer

It means:

  1. Find the shop package.
  2. Find the customers module inside it.
  3. Import the Customer class.

4. What is __init__.py for?

It’s a special file that can live inside a package.

Historically, it indicated that a directory was a Python package. Modern Python also supports packages without __init__.py, called namespace packages.

It can be empty

shop/
├── __init__.py
├── customers.py
└── products.py

Even with an empty __init__.py, we can import:

from shop.customers import Customer

It can also make imports more convenient

Suppose we put this in shop/__init__.py:

from .customers import Customer

Now, from outside the package, we can write:

from shop import Customer

Instead of:

from shop.customers import Customer

The leading dot in .customers marks a relative import within the current package.

What else does __init__.py do?

Its code runs when Python initializes that package during an import.

That’s why it’s a good idea to avoid expensive work or unnecessary side effects in this file.


5. __name__ and __main__

Python has a special variable called __name__. Its value depends on how a module is used.

Case A: run a file directly

File calculator.py:

def add(a, b):
    return a + b

if __name__ == "__main__":
    print(add(2, 3))

If we run:

python calculator.py

The output is:

5

When a file runs as the main program, its __name__ is "__main__".

Case B: import the file

From another file:

import calculator

result = calculator.add(10, 20)
print(result)

The output is:

30

The print(add(2, 3)) in calculator.py does not run, because __name__ is now "calculator".

What’s the use of this?

It lets us separate code we want to run directly from code we want to make available for import.

An approximate Ruby equivalent

if __FILE__ == $PROGRAM_NAME
  puts add(2, 3)
end

In Python:

if __name__ == "__main__":
    print(add(2, 3))

6. Virtual environments: venv

In Python, it’s common to create a virtual environment for each project. A virtual environment lets us install dependencies without mixing them with those of other projects.

Comparison with Ruby

Ruby Python
RVM with gemsets venv to isolate dependencies
gem install pip install
Gemfile requirements.txt (roughly)
Bundler Dependency management tools such as pip and others
bundle install pip install -r requirements.txt

Important: venv does not manage Python versions the way RVM manages Ruby versions. Its main job is to isolate environments and packages.

Create a virtual environment

Inside the project directory:

python3 -m venv .venv

This creates a directory called .venv.

Activate it on Linux (Debian)

source .venv/bin/activate

With the environment active, the python and pip commands normally point to that environment.

Install Django

python -m pip install django

Django is installed in the active environment.

Leave the environment

deactivate

Keep the environment out of Git

Add this to .gitignore:

.venv/

7. pip and requirements.txt

pip installs Python packages. For example:

python -m pip install django

To list installed packages:

python -m pip list

To write installed dependencies and their versions to a file:

python -m pip freeze > requirements.txt

An example requirements.txt:

Django==5.2.7
requests==2.32.3

These versions are illustrative.

To install a project’s dependencies:

python -m pip install -r requirements.txt

This resembles Ruby’s bundle install workflow, although requirements.txt does not do exactly the same job as Gemfile plus Gemfile.lock.


8. A typical way to start a Python project

# Create a directory
mkdir my_project
cd my_project

# Create a virtual environment
python3 -m venv .venv

# Activate it
source .venv/bin/activate

# Install Django
python -m pip install django

# Save dependencies
python -m pip freeze > requirements.txt

Then we can create the Django project.


9. Common mistakes

Confusing the module with the class

If we write:

import customer

To create a Customer instance, we use:

person = customer.Customer("Juan")

Not:

person = Customer("Juan")

Unless we imported Customer directly.

Confusing __init__.py with __init__

These are two different things:

  • __init__.py is a special file related to packages.
  • __init__ is a special method used to initialize instances.

Thinking __main__ is a filename

"__main__" is the special value of __name__ when a module runs as the main program.

Sharing one virtual environment across projects

The usual practice is to use a separate .venv for each project, avoiding dependency conflicts.


10. Recap

The most important concepts are:

  1. Module: an importable Python file.
  2. Package: a structure that groups modules.
  3. import: imports a module and lets us access its members.
  4. from ... import: binds specific names in the current namespace.
  5. __init__.py: lets us initialize a package and provide a convenient import interface.
  6. __name__ == "__main__": lets us run code only when a file is used as the main program.
  7. venv: isolates each project’s dependencies.
  8. pip: installs Python packages.
  9. requirements.txt: records dependencies and versions so they can be installed in another environment.

11. Next topic: Django

With these concepts, an experienced Ruby on Rails developer has enough background to start learning Django.

The next concepts to explore are:

  • The difference between a project and an app.
  • models.py: models and fields.
  • views.py: request handling logic, comparable to Rails controllers.
  • urls.py: routes, comparable to config/routes.rb.
  • makemigrations and migrate.
  • The Django ORM.
  • Tests and debugging.

The aim is to learn Django through comparisons with Rails, focusing on reading existing code and fixing bugs.

Until next time,
Gastón Ramos

 ::: If you'd like to comment, email me: ramos.gaston AT gmail.com :::