What is a Programming Language?
A programming language is a formal system that allows humans to give instructions to computers. It acts as a bridge between human-readable commands and machine code. Python, which we’ll focus on in this article, is a popular high-level programming language known for its readable syntax and extensive library ecosystem.
What is a Module?
A module in Python is a file containing Python code that can be imported and used in other Python programs. It’s a fundamental way to organize and reuse code by grouping related functions, classes, and variables together.
Key Aspects of Modules:
Code Organization: Modules help break down large programs into manageable files. Each module typically focuses on a specific functionality.
Code Reusability: Once created, modules can be imported and used in multiple programs:
import math # importing the math module result = math.sqrt(16) # using a function from the moduleNamespace Management: Modules create their own namespace (a container that maps names to objects, ensuring that identifiers like function and variable names remain unique within that module’s scope), preventing naming conflicts:
import module1 import module2 module1.function_name() # using function from module1 module2.function_name() # using different function with same name from module2
Types of Modules:
Built-in Modules: Come with Python installation
math: Mathematical functionsrandom: Random number generationdatetime: Date and time handling
Third-party Modules: Created by others, installed via pip (pip is a package manager for Python - just like how you download apps from Google Play Store on your Android phone, pip allows you to download and install Python packages from the Python Package Index (PyPI). It handles downloading, installing and managing these packages automatically)
- Can be part of larger packages
User-defined Modules: Created by the programmer
# mymodule.py def greet(name): return f"Hello, {name}!" # main.py import mymodule print(mymodule.greet("Alice"))
Module Import Methods:
Import Entire Module:
import math math.sqrt(16)Import Specific Items:
from math import sqrt sqrt(16)Import with Alias:
import numpy as np np.array([1, 2, 3])
What is a package?
A package in Python is a way to organize related modules into a directory hierarchy. It’s essentially a directory that contains multiple Python modules and a special __init__.py file that marks it as a package. Think of it as a folder containing multiple Python files (modules) that work together.
Key Components:
Directory Structure: A package must have:
- A directory containing Python modules (
.pyfiles) - A special
__init__.pyfile (can be empty)
- A directory containing Python modules (
__init__.py: This file:- Marks the directory as a Python package
- Can initialize package-level variables
- Can import specific items to make them available when the package is imported
Example of a basic package structure:
Key Characteristics of Packages:
Organization: Packages help organize related code into manageable units. For example:
mypackage/ ├── __init__.py ├── module1.py ├── module2.py └── subpackage/ ├── __init__.py └── module3.pyNamespace Management: Packages create hierarchical namespaces that help avoid naming conflicts. For instance:
from mypackage.module1 import function1 from mypackage.subpackage.module3 import function2Distribution: Packages can be easily distributed and installed using package managers like pip. For example:
pip install numpy # numpy is a package
Types of Packages:
Built-in Packages: Come pre-installed with Python
os: Operating system interfacesys: System-specific parametersmath: Mathematical functions
Third-party Packages: Created by the Python community
numpy: Numerical computingpandas: Data analysisrequests: HTTP library
Local Packages: Created by developers for their specific projects
Package vs Module:
- A module is a single Python file containing code
- A package is a collection of modules
- Every package is a module, but not every module is a package
What is a library?
A library is a collection of pre-written code (modules and packages) that can be easily reused. Libraries provide ready-to-use implementations of various functionalities, saving developers from having to write common code from scratch.
Key Characteristics of Libraries:
Reusability: Libraries contain code that can be used across different projects
import numpy as np # Reusing numpy's array functionality array = np.array([1, 2, 3])Abstraction: Libraries hide complex implementations behind simple interfaces
import requests # Complex HTTP logic abstracted into simple methods response = requests.get('https://api.example.com/data')Maintenance: Libraries are typically well-maintained and optimized
import pandas as pd # Regularly updated with bug fixes and improvements df = pd.read_csv('data.csv')
Types of Libraries:
Standard Library: Comes with Python installation
datetime: Date and time handlingjson: JSON encoding/decodingrandom: Random number generation
Third-party Libraries: External libraries that extend Python’s capabilities
matplotlib: Data visualizationscipy: Scientific computingdjango: Web development
Domain-specific Libraries: Focused on particular areas
tensorflow: Machine learningbeautifulsoup4: Web scrapingpygame: Game development
Library vs Package:
- A library is a broader term that may contain multiple packages
- A package is a way of organizing modules, while a library provides functionality
- For example,
scipyis a library that contains multiple packages for scientific computing
Different Package Versions May Be Incompatible
There are several key reasons why different versions of packages may be incompatible with each other:
1. Breaking Changes in APIs
- API Changes: Newer versions of packages often introduce changes to their Application Programming Interface (API)
# Old version (1.0) model.train(data) # New version (2.0) - Breaking change model.fit(data) # Method name changed from train to fit
2. Dependency Chain Conflicts
- Nested Dependencies: Packages often depend on other packages, creating a chain of dependenciesHere, you cannot use PackageA v2.0 and PackageC v1.0 together due to conflicting PackageB requirements
PackageA (v2.0) → requires PackageB (v1.5+) PackageC (v1.0) → requires PackageB (v1.0-1.4)
3. Python Version Requirements
- Different package versions may require different Python versions
# Package requirements example tensorflow 1.x → Python 2.7 or 3.3-3.6 tensorflow 2.x → Python 3.7+
4. Operating System Dependencies
- System Libraries: Packages may rely on different versions of system-level libraries
# Example: Package requiring specific system libraries opencv-python 3.x → requires libGL.so.1 opencv-python 4.x → requires newer version of libGL.so.1
5. Internal Data Structures
- Data Format Changes: Different versions may use incompatible internal data structures
# Old version serialized data model_v1.save('model.pkl') # Uses format A # New version cannot read old format model_v2.load('model.pkl') # Expects format B
6. Feature Deprecation and Removal
- Removed Features: Newer versions may remove features that older dependent packages rely on
# Old version pandas.DataFrame.as_matrix() # Works # New version pandas.DataFrame.as_matrix() # Raises AttributeError - Method removed
7. Security Updates
- Security Patches: Newer versions may implement security fixes that change how features work
# Old version - vulnerable requests.get(url, verify=False) # Allows insecure connections # New version - stricter security requests.get(url) # Requires SSL verification by default
These are just some of the reasons why we cannot just use any package version for any project.
This is a bad solution
A common but problematic approach that many beginners take is installing all packages globally in their base Python environment. Here’s what this typically looks like:
The “Install Everything in Base” Approach
Initial Project Setup
# Project A needs tensorflow 1.15 pip install tensorflow==1.15 pip install numpy==1.16 pip install other-packages==old-versionsStarting a New Project
# Project B needs tensorflow 2.0 pip uninstall tensorflow # Remove old version pip install tensorflow==2.0 # Install new version pip uninstall numpy # Remove incompatible version pip install numpy==1.19 # Install compatible version
Problems with this Approach
Time-Consuming Package Management
- Need to constantly install and uninstall packages
- Have to remember exact versions needed for each project
- Must reinstall all dependencies when switching projects
Dependency Conflicts
# Project A's code breaks because tensorflow 2.0 is not backward compatible import tensorflow as tf tf.placeholder() # This function exists in 1.x but not in 2.xVersion Tracking Nightmare
- No clear record of which versions work for which project
- Easy to forget dependencies when sharing code
- Hard to reproduce environment on another machine
System Instability
- Frequent package changes can corrupt Python environment
- System-wide Python installation becomes unstable
- Risk of breaking system tools that depend on Python
Project Isolation Issues
# Project A and B running simultaneously import tensorflow # Which version will this use? # Both projects might try to use different versions # Leading to unpredictable behaviorDebugging Complexity
- Hard to determine if issues are from:
- Current package versions
- Leftover conflicts from previous installations
- Incomplete package removals
- System-level Python problems
- Hard to determine if issues are from:
This approach is like having one toolbox for all your different jobs - when you need a different set of tools, you have to take everything out and put new tools in. It’s inefficient, prone to errors, and makes it difficult to switch between projects quickly.
A better solution - Virtual Environment
How Virtual Environments Help with Segregation
Virtual environments solve our dependency problems by providing complete isolation between projects. Let’s see how:
Project-Specific Package Management
# Project A's virtual environment project_a_env/ python==3.7 tensorflow==1.15 numpy==1.16 # Project B's virtual environment project_b_env/ python==3.8 tensorflow==2.0 numpy==1.19Each project gets its own “container” with exactly the packages it needs.
Independent Python Versions
- Different projects can use different Python versions
- Example:
# Web project using Python 3.9 web_env/ python==3.9 django==4.2 # ML project using Python 3.7 ml_env/ python==3.7 tensorflow==1.15
Clean Dependency Management
# requirements.txt for Project A tensorflow==1.15 numpy==1.16 pandas==1.0.3 # requirements.txt for Project B tensorflow==2.0 numpy==1.19 pandas==1.3.0Each project maintains its own list of exact package versions.
Easy Environment Reproduction
# Create and activate new environment for Project A python -m venv project_a_env source project_a_env/bin/activate # On Unix/macOS # Install Project A's dependencies pip install -r requirements.txtAnyone can recreate the exact environment needed.
Safe Experimentation
# Create temporary environment for testing python -m venv test_env # Try new packages without affecting other projects pip install experimental_package # If things go wrong, just delete the environment deactivate rm -rf test_envYou can experiment without risking your stable environments.
Multiple Projects Running Simultaneously
# Terminal 1 (Project A) source project_a_env/bin/activate python script.py # Uses tensorflow 1.15 # Terminal 2 (Project B) source project_b_env/bin/activate python script.py # Uses tensorflow 2.0Run different projects with different dependencies at the same time.
This segregation provides several benefits:
- Stability: Each project has its own stable environment
- Reproducibility: Environments can be exactly recreated
- Flexibility: Easy to switch between projects
- Safety: Experiments won’t affect other projects
- Clarity: Clear documentation of dependencies
- Portability: Projects can be easily shared and deployed
Think of virtual environments like separate workshops - each project gets its own space with its own set of tools, and there’s no risk of tools from one project interfering with another.
How to create a virtual environment
Using Python’s built-in venv module
The built-in venv module provides a lightweight way to create isolated Python environments. Here’s how to use it:
Creating a Virtual Environment
# Basic syntax python -m venv environment_name # Example python -m venv myproject_envActivating the Environment
# On Windows myproject_env\Scripts\activate # On Unix/macOS source myproject_env/bin/activateAfter activation, your prompt will change to show the active environment.
Installing Packages
# Install packages using pip pip install package_name # Example pip install numpy pandas # Install from requirements file pip install -r requirements.txtDeactivating the Environment
deactivate
Limitations of venv
Think of venv like a basic toolbox - it works fine for simple projects but has some key drawbacks. It can’t automatically figure out when tools might conflict with each other, only works with Python tools (not other types of software), needs different commands on Windows vs Mac/Linux, and isn’t great for data science projects that need special high-performance tools. This is why many people use Conda instead, which is like a more advanced toolbox that can handle all these complex situations better - it manages tool conflicts automatically, works the same way on all computers, can handle non-Python tools, and comes with optimized tools for data science work.
Using Conda
Conda is a powerful package and environment management system that’s particularly popular in data science. Here’s a comprehensive guide to using Conda effectively:
Basic Environment Management
- Creating Environments
# Create with specific Python version (recommended) conda create --name myenv python=3.10 # Activate environment (works on all platforms) conda activate myenv # List environments conda env list # Remove environment conda remove --name myenv --all
Package Management
- Installing and Managing Packages
# Install packages conda install numpy pandas scikit-learn # View installed packages conda list # Search for package conda list | grep numpy # Check dependencies conda depends package_name conda depends --reverse-depends package_name # Test installation compatibility conda install --dry-run new_package
Environment Documentation
- Export/Import
# Export environment conda env export > environment.yml # Export core packages only conda env export --from-history > environment.yml # Create from file conda env create -f environment.yml
Key Reminder: Always document configurations, test thoroughly, and maintain version control for reproducibility.
Some tips when learning from online resources
I love learning from YouTube tutorials, but since Python packages evolve so rapidly, I often find myself watching videos that are a few years old. This can lead to version mismatches between what’s shown in the tutorial and what’s currently available. Here’s how I handle these situations:
Case 1: If following a tutorial series/playlist/course
When working through a structured tutorial series:
Initial Setup: Environment Matching Tutorial Versions
To ensure a smooth learning experience when following tutorials, especially older ones, it is crucial to replicate the environment used in the tutorial as closely as possible. This primarily involves using the same versions of Python and the relevant packages.
# Step 1: Create a dedicated conda environment with the Python version from the tutorial conda create --name tutorial_env python=3.x # Replace 3.x with the Python version mentioned in the tutorial (e.g., 3.6, 3.7) # Step 2: Activate the newly created environment conda activate tutorial_env # Step 3: Install packages with the EXACT versions specified in the tutorial conda install package1==version1 package2==version2 package3==version3 # ... and so onFor example, if a tutorial uses TensorFlow 1.14 and Keras 2.2.4, the installation command would be:
conda install tensorflow==1.14 keras==2.2.4Rationale:
- Ensures Code Compatibility: By using the same package versions, you minimize the risk of encountering errors or unexpected behavior due to version mismatches. The code in the tutorial is designed to work with specific versions, and replicating this environment ensures that the code runs as intended.
- Focus on Core Concepts: Learning becomes more efficient when you are not constantly debugging version-related issues. Matching the tutorial environment allows you to concentrate on understanding the fundamental concepts and algorithms being taught, rather than spending time troubleshooting compatibility problems.
- Directly Follow Tutorial Instructions: You can follow the tutorial step-by-step without deviations caused by different software versions. This direct alignment simplifies the learning process, especially for beginners.
Case 2: Integrating tutorial concepts into existing projects
When you’re learning specific concepts from tutorials for your current project, you’re in a situation where you already have a defined project environment. In this case, directly using the tutorial’s packages might not be feasible or desirable. Here’s how to effectively learn from tutorials and apply the knowledge to your existing project:
Focus on Conceptual Understanding
- Extract Core Principles: Focus on understanding the fundamental concepts, algorithms, and design patterns being taught, independent of specific implementations
- Identify Version-Independent Logic: Distinguish between core logic that remains constant across versions versus syntax/API details that may change
- Document Version Context: Make note of which features are version-specific or deprecated to build awareness of library evolution and best practices
- Build Mental Models: Create clear mental models of how different components interact, making it easier to adapt concepts across versions
Strategic Code Adaptation
- Pattern-Based Translation: Rather than direct code copying, identify the underlying patterns and implement them using your environment’s idioms
- Maintain Environment Integrity: Carefully evaluate additions to prevent dependency conflicts and maintain project stability
- Progressive Enhancement: Add functionality incrementally while testing for compatibility at each step
- Example of Modern Adaptation:
# Tutorial (older version): # from tensorflow.keras.layers import Dense # Modern adaptation options: from keras.layers import Dense # Standalone Keras # or from tensorflow.keras.layers import Dense # TF 2.x # or import torch.nn as nn # PyTorch equivalent dense_layer = nn.Linear(in_features, out_features)
Systematic Package Integration
In case a new package is required, then we cannot blindly install it as mentioned in the tutorial as it might not be compatible with the current environment. Here is how we can do it:
Environment Analysis:
# Review current state conda list # Full package inventory conda list | grep package_name # Search specific packageCompatibility Verification:
# Pre-installation compatibility check conda install --dry-run new_package # Version-specific check conda install --dry-run "new_package>=2.0,<3.0"Version Management:
# List all available versions conda search new_package # Analyze package dependencies conda depends new_package conda depends --reverse-depends new_package # What depends on it?Smart Installation Process:
- Begin with latest stable version compatible with your environment
- If conflicts arise, systematically test earlier versions
- Leverage additional channels when needed:
# Try conda-forge for additional versions conda install -c conda-forge new_package # Install specific version with constraints conda install "new_package=2.1.*" --channel conda-forge
Conclusion
In today’s development landscape, we constantly switch between multiple projects and learning resources, each with unique package requirements and dependencies.
Without proper environment management, this juggling act can quickly become overwhelming, leading to conflicts and compatibility issues. This is where Conda proves invaluable - while it requires an initial time investment to learn, its robust environment management capabilities make it an essential tool for modern development.
The ability to create isolated, reproducible environments for each project or tutorial ensures smooth transitions between different tasks and eliminates dependency headaches. The time spent mastering Conda is minimal compared to the hours saved in debugging environment issues and managing project dependencies.
Also note that i have not made this post as a how to guide on how to use or install Conda, as that keeps on changing as new versions are released hence refer to the Conda documentation for the latest information. This post is just to motivate you to use a virtual environment for your projects.