Gastón Ramos
Personal blog
← Back to the English articles
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:
shopis the package.customers.pyis a module.products.pyis another module.main.pyis 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:
- Find the
shoppackage. - Find the
customersmodule inside it. - Import the
Customerclass.
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__.pyis 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:
- Module: an importable Python file.
- Package: a structure that groups modules.
import: imports a module and lets us access its members.from ... import: binds specific names in the current namespace.__init__.py: lets us initialize a package and provide a convenient import interface.__name__ == "__main__": lets us run code only when a file is used as the main program.venv: isolates each project’s dependencies.pip: installs Python packages.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 toconfig/routes.rb.makemigrationsandmigrate.- 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 :::