Modules: Basic Concept

Java: - A module is typically a single .java file containing one or more classes. - Classes are organized within packages.

TrackJava to Python Journey
Current SectionPython Basics
Progress12 of 19

1. Modules: Basic Concept

Java:

  • A module is typically a single .java file containing one or more classes.
  • Classes are organized within packages.
// File: Animal.java
public class Animal {
    public void eat() {
        System.out.println("Eating");
    }
}

Python:

  • A module is a single .py file containing functions, classes, or variables.
  • Modules are imported using the import statement.
# File: animal.py
class Animal:
    def eat(self):
        print("Eating")

def make_sound():
    print("Sound")

2. Importing Modules

Java:

// Import specific class
import com.company.animals.Animal;

// Import all classes from package
import com.company.animals.*;

// Using the class
Animal dog = new Animal();
dog.eat();

Python:

# Import entire module
import animal

# Import specific class
from animal import Animal

# Import specific function
from animal import make_sound

# Import with alias
import animal as anim
from animal import Animal as AnimalClass

# Import all (not recommended)
from animal import *

# Using
a = Animal()
a.eat()

make_sound()

Key Differences:

  • Java uses package hierarchy with dots (com.company.animals).
  • Python uses module names directly or file paths.

3. Packages: Directory Structure

Java Package Structure

Directory Layout:

src/
├── com/
│   └── company/
│       ├── animals/
│       │   ├── Animal.java
│       │   └── Dog.java
│       └── utils/
│           └── Logger.java

Animal.java:

package com.company.animals;

public class Animal {
    public void eat() {
        System.out.println("Eating");
    }
}

Dog.java:

package com.company.animals;

public class Dog extends Animal {
    public void bark() {
        System.out.println("Bark");
    }
}

Main.java:

import com.company.animals.Animal;
import com.company.animals.Dog;

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
        dog.bark();
    }
}

Python Package Structure

Directory Layout:

my_project/
├── company/
│   ├── __init__.py
│   ├── animals/
│   │   ├── __init__.py
│   │   ├── animal.py
│   │   └── dog.py
│   └── utils/
│       ├── __init__.py
│       └── logger.py
├── main.py
└── requirements.txt

company/init.py:

# Makes directory a package (can be empty)

company/animals/init.py:

# Makes directory a package
from .animal import Animal
from .dog import Dog

company/animals/animal.py:

class Animal:
    def eat(self):
        print("Eating")

company/animals/dog.py:

from .animal import Animal

class Dog(Animal):
    def bark(self):
        print("Bark")

company/utils/logger.py:

def log_message(msg):
    print(f"[LOG] {msg}")

main.py:

from company.animals import Dog, Animal
from company.utils.logger import log_message

if __name__ == "__main__":
    dog = Dog()
    dog.eat()
    dog.bark()
    log_message("Dog created successfully")

Key Differences:

  • Java uses / for package hierarchy, Python uses / for directories.
  • Python requires __init__.py files to mark directories as packages (Python 3.3+ supports namespace packages without it).
  • Java's package naming uses reverse domain convention (com.company).
  • Python uses relative (from .module import) and absolute (from company.animals import) imports.

4. Module/Package Naming Conventions

Java:

package com.company.projectname.modulename;  // All lowercase, dot-separated

Python:

# Module: lowercase_with_underscores.py
# Package: lowercase_with_underscores/
# Class: PascalCase
# Function: lowercase_with_underscores
# Constant: UPPERCASE_WITH_UNDERSCORES

5. Relative vs Absolute Imports

Python Only:

Absolute Import:

# In company/animals/dog.py
from company.animals.animal import Animal  # Full path

Relative Import:

# In company/animals/dog.py
from .animal import Animal  # Import from same package
from ..utils.logger import log_message  # Import from parent package

Java uses absolute imports only:

import com.company.animals.Animal;  // Always absolute

6. init.py and Package Initialization

Python:

# company/__init__.py
VERSION = "1.0.0"

def init_company():
    print("Initializing company")

# Executed when package is imported
print("Loading company package")

Usage:

import company
print(company.VERSION)  # "1.0.0"
company.init_company()

Java: No direct equivalent; static initializers in classes serve similar purposes.

public class CompanyConstants {
    public static final String VERSION = "1.0.0";
    
    static {  // Static initializer block
        System.out.println("Loading company constants");
    }
}

7. Distributing Code: requirements.txt vs Maven/Gradle

Python: requirements.txt

requirements.txt:

numpy==1.24.0
pandas>=1.5.0,<2.0.0
requests

Installation:

pip install -r requirements.txt

Java: Maven

pom.xml:

<project>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>
</project>

8. Complete Application Structure Example

Python Application

Directory Structure:

ecommerce_app/
├── ecommerce/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── product.py
│   │   └── order.py
│   ├── services/
│   │   ├── __init__.py
│   │   └── order_service.py
│   ├── utils/
│   │   ├── __init__.py
│   │   └── validators.py
│   └── config.py
├── main.py
├── requirements.txt
└── README.md

ecommerce/models/product.py:

class Product:
    def __init__(self, id, name, price):
        self.id = id
        self.name = name
        self.price = price
    
    def __str__(self):
        return f"{self.name}: ${self.price}"

ecommerce/models/order.py:

class Order:
    def __init__(self, order_id):
        self.order_id = order_id
        self.products = []
    
    def add_product(self, product):
        self.products.append(product)
    
    def total_price(self):
        return sum(p.price for p in self.products)

ecommerce/services/order_service.py:

from ..models.order import Order
from ..models.product import Product

class OrderService:
    def __init__(self):
        self.orders = []
    
    def create_order(self, order_id):
        order = Order(order_id)
        self.orders.append(order)
        return order
    
    def get_order_total(self, order_id):
        order = next((o for o in self.orders if o.order_id == order_id), None)
        return order.total_price() if order else 0

ecommerce/utils/validators.py:

def is_valid_email(email):
    return "@" in email

def is_valid_price(price):
    return price > 0

ecommerce/config.py:

# Configuration constants
DATABASE_URL = "sqlite:///ecommerce.db"
DEBUG = True
LOG_LEVEL = "INFO"

main.py:

from ecommerce.models.product import Product
from ecommerce.services.order_service import OrderService
from ecommerce.utils.validators import is_valid_price

if __name__ == "__main__":
    # Create service
    service = OrderService()
    
    # Create products
    p1 = Product(1, "Laptop", 999)
    p2 = Product(2, "Mouse", 25)
    
    # Validate prices
    if is_valid_price(p1.price) and is_valid_price(p2.price):
        # Create order
        order = service.create_order("ORD001")
        order.add_product(p1)
        order.add_product(p2)
        
        print(f"Order Total: ${order.total_price()}")
    
    from ecommerce import config
    print(f"Debug Mode: {config.DEBUG}")

Java Application

Directory Structure:

ecommerce-app/
├── src/main/java/com/company/ecommerce/
│   ├── models/
│   │   ├── Product.java
│   │   └── Order.java
│   ├── services/
│   │   └── OrderService.java
│   ├── utils/
│   │   └── Validators.java
│   ├── config/
│   │   └── Config.java
│   └── Main.java
├── pom.xml
└── README.md

com/company/ecommerce/models/Product.java:

package com.company.ecommerce.models;

public class Product {
    private int id;
    private String name;
    private double price;
    
    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    
    public double getPrice() {
        return price;
    }
    
    @Override
    public String toString() {
        return name + ": $" + price;
    }
}

com/company/ecommerce/models/Order.java:

package com.company.ecommerce.models;

import java.util.ArrayList;
import java.util.List;

public class Order {
    private String orderId;
    private List<Product> products;
    
    public Order(String orderId) {
        this.orderId = orderId;
        this.products = new ArrayList<>();
    }
    
    public void addProduct(Product product) {
        products.add(product);
    }
    
    public double getTotalPrice() {
        return products.stream().mapToDouble(Product::getPrice).sum();
    }
}

com/company/ecommerce/services/OrderService.java:

package com.company.ecommerce.services;

import java.util.ArrayList;
import java.util.List;
import com.company.ecommerce.models.Order;

public class OrderService {
    private List<Order> orders = new ArrayList<>();
    
    public Order createOrder(String orderId) {
        Order order = new Order(orderId);
        orders.add(order);
        return order;
    }
    
    public double getOrderTotal(String orderId) {
        return orders.stream()
            .filter(o -> o.getOrderId().equals(orderId))
            .map(Order::getTotalPrice)
            .findFirst()
            .orElse(0.0);
    }
}

com/company/ecommerce/Main.java:

package com.company.ecommerce;

import com.company.ecommerce.models.Product;
import com.company.ecommerce.services.OrderService;

public class Main {
    public static void main(String[] args) {
        OrderService service = new OrderService();
        
        Product p1 = new Product(1, "Laptop", 999);
        Product p2 = new Product(2, "Mouse", 25);
        
        Order order = service.createOrder("ORD001");
        order.addProduct(p1);
        order.addProduct(p2);
        
        System.out.println("Order Total: $" + order.getTotalPrice());
    }
}

9. Namespace/Package Comparison Table

  • Basic Unit

    • Java: Class in Package
    • Python: Function/Class in Module
  • Organization

    • Java: package com.company.module;
    • Python: company/module/ directory
  • File/Class

    • Java: One public class per file
    • Python: Multiple classes/functions per file
  • Import Style

    • Java: import package.Class;
    • Python: from module import Class
  • Relative Imports

    • Java: Not supported
    • Python: Supported with . and ..
  • Package Init

    • Java: Static initializer block
    • Python: __init__.py file
  • Visibility

    • Java: public, private, protected
    • Python: Convention-based (_, __)
  • Directory Structure

    • Java: Mirrors package hierarchy
    • Python: Same as file path structure

10. Key Differences Summary

  1. Namespace Convention: Java uses reverse domain naming (com.company); Python uses lowercase with underscores.
  2. Flexibility: Python allows multiple classes per file; Java typically one public class per file.
  3. Imports: Python supports relative imports; Java only absolute.
  4. Package Init: Python uses __init__.py files; Java uses static blocks or configuration classes.
  5. Directory Mapping: Both map directory structure to package hierarchy, but Python is more flexible.
  6. Type Safety: Java enforces package/module structure at compile time; Python is runtime-based.