Prerequisites
- Basic understanding of programming concepts ๐
- Python installation (3.8+) ๐
- VS Code or preferred IDE ๐ป
What you'll learn
- Understand the concept fundamentals ๐ฏ
- Apply the concept in real projects ๐๏ธ
- Debug common issues ๐
- Write clean, Pythonic code โจ
๐ฏ Introduction
Welcome to this exciting tutorial on Pythonโs __name__ == "__main__"
pattern! ๐ In this guide, weโll demystify this magical line of code that youโve probably seen in Python scripts everywhere.
Have you ever wondered why Python scripts often end with this mysterious incantation? ๐ค Itโs like a secret handshake that tells Python whether your code is the star of the show or just a supporting actor! Whether youโre building command-line tools ๐ง, creating reusable libraries ๐, or writing test scripts ๐งช, understanding this pattern is essential for writing professional Python code.
By the end of this tutorial, youโll know exactly when and why to use this pattern, and youโll be writing Python modules like a pro! Letโs dive in! ๐โโ๏ธ
๐ Understanding name == โmainโ
๐ค What is name?
Think of __name__
as Pythonโs way of introducing your code to itself! ๐ญ Itโs like a name tag that changes depending on how your code is being used.
In Python terms, __name__
is a special variable that Python sets automatically:
- โจ When you run a file directly:
__name__
becomes"__main__"
- ๐ฆ When you import a file:
__name__
becomes the moduleโs name - ๐ฏ This helps Python know what role your code is playing!
๐ก Why Use This Pattern?
Hereโs why developers love this pattern:
- Dual Purpose ๐: Make files work as both scripts AND modules
- Clean Imports ๐ฅ: Prevent code from running when imported
- Better Testing ๐งช: Test modules without executing everything
- Professional Code ๐ผ: Industry standard practice
Real-world example: Imagine building a calculator ๐งฎ. With this pattern, you can import the math functions into other programs without the calculatorโs menu popping up every time!
๐ง Basic Syntax and Usage
๐ Simple Example
Letโs start with a friendly example:
# ๐ Hello, Python modules!
print(f"This file's __name__ is: {__name__}")
def greet(name):
"""๐ A friendly greeting function"""
return f"Hello, {name}! Welcome to Python modules! ๐"
def calculate_age(birth_year):
"""๐ Calculate age from birth year"""
from datetime import datetime
current_year = datetime.now().year
return current_year - birth_year
# ๐ฏ The magic line!
if __name__ == "__main__":
# This code ONLY runs when the file is executed directly
print("๐ Running as a script!")
print(greet("Python Learner"))
age = calculate_age(2000)
print(f"Someone born in 2000 is {age} years old! ๐")
๐ก Explanation: When you run this file directly, youโll see all the prints. But if you import it into another file, only the functions will be available - no unwanted output!
๐ฏ Common Patterns
Here are patterns youโll use daily:
# ๐๏ธ Pattern 1: Script with main function
def main():
"""๐ฎ Main game loop"""
print("๐ฎ Starting the game...")
# Game logic here
print("๐ Game over!")
if __name__ == "__main__":
main()
# ๐จ Pattern 2: Module with test code
def add_numbers(a, b):
"""โ Add two numbers"""
return a + b
def multiply_numbers(a, b):
"""โ๏ธ Multiply two numbers"""
return a * b
if __name__ == "__main__":
# ๐งช Test our functions
print(f"2 + 3 = {add_numbers(2, 3)}")
print(f"4 ร 5 = {multiply_numbers(4, 5)}")
# ๐ Pattern 3: Command-line tool
import sys
def process_file(filename):
"""๐ Process a file"""
print(f"Processing {filename}... ๐")
# File processing logic
return f"โ
Processed {filename} successfully!"
if __name__ == "__main__":
if len(sys.argv) > 1:
result = process_file(sys.argv[1])
print(result)
else:
print("โ ๏ธ Please provide a filename!")
๐ก Practical Examples
๐ Example 1: Shopping Cart Module
Letโs build something real:
# ๐๏ธ shopping_cart.py
class Product:
"""๐ฆ A product in our store"""
def __init__(self, name, price, emoji="๐ฆ"):
self.name = name
self.price = price
self.emoji = emoji
def __str__(self):
return f"{self.emoji} {self.name} - ${self.price:.2f}"
class ShoppingCart:
"""๐ Shopping cart for our store"""
def __init__(self):
self.items = []
self.discount = 0
def add_item(self, product, quantity=1):
"""โ Add item to cart"""
for _ in range(quantity):
self.items.append(product)
print(f"Added {quantity}x {product.emoji} {product.name} to cart! ๐")
def calculate_total(self):
"""๐ฐ Calculate total price"""
subtotal = sum(item.price for item in self.items)
discount_amount = subtotal * self.discount
total = subtotal - discount_amount
return total
def apply_discount(self, percentage):
"""๐ท๏ธ Apply discount"""
self.discount = percentage / 100
print(f"Applied {percentage}% discount! ๐")
def show_cart(self):
"""๐ Display cart contents"""
if not self.items:
print("๐ Your cart is empty!")
return
print("\n๐ Shopping Cart:")
print("-" * 40)
# Group items
item_counts = {}
for item in self.items:
key = f"{item.emoji} {item.name}"
item_counts[key] = item_counts.get(key, 0) + 1
for item_name, count in item_counts.items():
price = next(i.price for i in self.items if f"{i.emoji} {i.name}" == item_name)
print(f"{count}x {item_name} @ ${price:.2f} each")
print("-" * 40)
print(f"๐ฐ Total: ${self.calculate_total():.2f}")
if self.discount > 0:
print(f"๐ท๏ธ Discount: {self.discount * 100}% off!")
# ๐ฏ Code that runs ONLY when executed directly
if __name__ == "__main__":
print("๐ช Welcome to Python Shop! ๐")
# Create some products
laptop = Product("Gaming Laptop", 999.99, "๐ป")
coffee = Product("Premium Coffee", 12.99, "โ")
book = Product("Python Mastery", 39.99, "๐")
headphones = Product("Wireless Headphones", 149.99, "๐ง")
# Create cart and shop
cart = ShoppingCart()
cart.add_item(laptop)
cart.add_item(coffee, 3)
cart.add_item(book)
cart.add_item(headphones)
# Show cart before discount
cart.show_cart()
# Apply discount
print("\n๐ Special offer time!")
cart.apply_discount(15)
cart.show_cart()
๐ฏ Try it yourself: Import this module in another file and use the classes without the demo shopping session running!
๐ฎ Example 2: Game Score Tracker
Letโs make it fun:
# ๐ game_tracker.py
import json
import os
from datetime import datetime
class Player:
"""๐ฎ A game player"""
def __init__(self, name, emoji="๐ฎ"):
self.name = name
self.emoji = emoji
self.score = 0
self.level = 1
self.achievements = []
self.start_time = datetime.now()
def add_points(self, points):
"""๐ฏ Add points to player score"""
self.score += points
print(f"{self.emoji} {self.name} earned {points} points! โจ")
# Check for level up
if self.score >= self.level * 100:
self.level_up()
def level_up(self):
"""๐ Level up the player"""
self.level += 1
achievement = f"๐ Level {self.level} Master"
self.achievements.append(achievement)
print(f"๐ {self.name} reached Level {self.level}! ๐")
print(f"New achievement: {achievement}")
def get_stats(self):
"""๐ Get player statistics"""
play_time = (datetime.now() - self.start_time).seconds // 60
return {
"name": self.name,
"emoji": self.emoji,
"score": self.score,
"level": self.level,
"achievements": self.achievements,
"play_time_minutes": play_time
}
class GameSession:
"""๐ฏ A game session manager"""
def __init__(self, game_name="Python Quest"):
self.game_name = game_name
self.players = {}
self.high_scores = self.load_high_scores()
def add_player(self, name, emoji="๐ฎ"):
"""โ Add a new player"""
if name not in self.players:
self.players[name] = Player(name, emoji)
print(f"Welcome {emoji} {name} to {self.game_name}! ๐")
return self.players[name]
def load_high_scores(self):
"""๐ Load high scores from file"""
if os.path.exists("high_scores.json"):
with open("high_scores.json", "r") as f:
return json.load(f)
return []
def save_high_scores(self):
"""๐พ Save high scores to file"""
scores = []
for player in self.players.values():
scores.append({
"name": player.name,
"score": player.score,
"level": player.level
})
# Combine with existing scores
all_scores = scores + self.high_scores
# Sort by score and keep top 10
all_scores.sort(key=lambda x: x["score"], reverse=True)
self.high_scores = all_scores[:10]
with open("high_scores.json", "w") as f:
json.dump(self.high_scores, f, indent=2)
print("๐พ High scores saved!")
def show_leaderboard(self):
"""๐ Display the leaderboard"""
print("\n๐ LEADERBOARD ๐")
print("=" * 40)
for i, score in enumerate(self.high_scores[:5], 1):
medal = "๐ฅ" if i == 1 else "๐ฅ" if i == 2 else "๐ฅ" if i == 3 else "๐
"
print(f"{medal} {i}. {score['name']} - {score['score']} pts (Lvl {score['level']})")
print("=" * 40)
def demo_game():
"""๐ฎ Demo game session"""
print("๐ฏ Starting Demo Game Session! ๐ฏ")
# Create game session
game = GameSession("Python Adventure")
# Add players
alice = game.add_player("Alice", "๐ธ")
bob = game.add_player("Bob", "๐ค")
charlie = game.add_player("Charlie", "๐ฆธ")
# Simulate gameplay
print("\n๐ฎ Let the games begin! ๐ฎ")
# Alice's turn
alice.add_points(50)
alice.add_points(75)
# Bob's turn
bob.add_points(100)
bob.add_points(25)
# Charlie's turn
charlie.add_points(60)
charlie.add_points(80)
charlie.add_points(40)
# Show final stats
print("\n๐ GAME STATS ๐")
for player in game.players.values():
stats = player.get_stats()
print(f"\n{stats['emoji']} {stats['name']}:")
print(f" ๐ฏ Score: {stats['score']}")
print(f" ๐ Level: {stats['level']}")
print(f" ๐ Achievements: {len(stats['achievements'])}")
# Save and show leaderboard
game.save_high_scores()
game.show_leaderboard()
# ๐ฏ This only runs when executed directly!
if __name__ == "__main__":
demo_game()
๐ Advanced Concepts
๐งโโ๏ธ Advanced Topic 1: Module Testing Framework
When youโre ready to level up, try this advanced pattern:
# ๐งช test_framework.py
import sys
import traceback
from typing import Callable, Any
class TestRunner:
"""๐งช A simple test runner"""
def __init__(self):
self.tests = []
self.passed = 0
self.failed = 0
def test(self, func: Callable) -> Callable:
"""๐ฏ Decorator to mark test functions"""
self.tests.append(func)
return func
def run_tests(self):
"""๐ Run all registered tests"""
print("๐งช Running tests...\n")
for test_func in self.tests:
test_name = test_func.__name__
try:
test_func()
self.passed += 1
print(f"โ
{test_name}")
except AssertionError as e:
self.failed += 1
print(f"โ {test_name}: {str(e)}")
except Exception as e:
self.failed += 1
print(f"๐ฅ {test_name}: Unexpected error - {str(e)}")
# Summary
total = self.passed + self.failed
print(f"\n๐ Test Results: {self.passed}/{total} passed")
if self.failed == 0:
print("๐ All tests passed! You're awesome! ๐")
else:
print(f"โ ๏ธ {self.failed} tests failed. Keep debugging! ๐ช")
return self.failed == 0
# Create global test runner
runner = TestRunner()
# ๐จ Example usage
def assert_equal(actual: Any, expected: Any, message: str = ""):
"""โ
Assert two values are equal"""
if actual != expected:
error_msg = f"Expected {expected}, got {actual}"
if message:
error_msg = f"{message}: {error_msg}"
raise AssertionError(error_msg)
@runner.test
def test_addition():
"""โ Test addition"""
assert_equal(2 + 2, 4)
assert_equal(10 + 5, 15)
@runner.test
def test_string_operations():
"""๐ Test string operations"""
assert_equal("Hello " + "World", "Hello World")
assert_equal("Python" * 2, "PythonPython")
@runner.test
def test_list_operations():
"""๐ Test list operations"""
my_list = [1, 2, 3]
my_list.append(4)
assert_equal(len(my_list), 4)
assert_equal(my_list[-1], 4)
# ๐ฏ Run tests only when executed directly
if __name__ == "__main__":
success = runner.run_tests()
sys.exit(0 if success else 1)
๐๏ธ Advanced Topic 2: Plugin System
For the brave developers:
# ๐ plugin_system.py
import importlib
import os
from typing import Dict, List, Protocol
class Plugin(Protocol):
"""๐ Plugin interface"""
name: str
version: str
def initialize(self) -> None:
"""๐ Initialize the plugin"""
...
def execute(self, data: Dict) -> Dict:
"""โก Execute plugin logic"""
...
class PluginManager:
"""๐ฏ Manages plugins"""
def __init__(self, plugin_dir: str = "plugins"):
self.plugin_dir = plugin_dir
self.plugins: Dict[str, Plugin] = {}
def discover_plugins(self):
"""๐ Discover and load plugins"""
if not os.path.exists(self.plugin_dir):
print(f"โ ๏ธ Plugin directory '{self.plugin_dir}' not found!")
return
print(f"๐ Searching for plugins in {self.plugin_dir}/...")
for filename in os.listdir(self.plugin_dir):
if filename.endswith(".py") and not filename.startswith("_"):
module_name = filename[:-3]
self._load_plugin(module_name)
def _load_plugin(self, module_name: str):
"""๐ฆ Load a single plugin"""
try:
module = importlib.import_module(f"{self.plugin_dir}.{module_name}")
# Look for plugin class
for item_name in dir(module):
item = getattr(module, item_name)
if (isinstance(item, type) and
hasattr(item, 'name') and
hasattr(item, 'execute')):
plugin = item()
self.plugins[plugin.name] = plugin
print(f"โ
Loaded plugin: {plugin.name} v{plugin.version}")
plugin.initialize()
break
except Exception as e:
print(f"โ Failed to load {module_name}: {e}")
def execute_plugin(self, plugin_name: str, data: Dict) -> Dict:
"""โก Execute a specific plugin"""
if plugin_name in self.plugins:
return self.plugins[plugin_name].execute(data)
else:
print(f"โ ๏ธ Plugin '{plugin_name}' not found!")
return data
def list_plugins(self):
"""๐ List all loaded plugins"""
if not self.plugins:
print("๐ญ No plugins loaded!")
else:
print("\n๐ Available Plugins:")
for name, plugin in self.plugins.items():
print(f" โข {name} (v{plugin.version})")
# ๐ฏ Demo when run directly
if __name__ == "__main__":
print("๐ Plugin System Demo ๐\n")
# Create plugin manager
manager = PluginManager()
# Create demo plugin directory
os.makedirs("plugins", exist_ok=True)
# Create a sample plugin
sample_plugin = '''
class EmojiPlugin:
"""โจ Adds emojis to text"""
name = "emoji_enhancer"
version = "1.0"
def initialize(self):
print("โจ Emoji plugin ready!")
def execute(self, data):
text = data.get("text", "")
data["text"] = f"๐ {text} ๐"
return data
'''
with open("plugins/emoji_plugin.py", "w") as f:
f.write(sample_plugin)
# Load and use plugins
manager.discover_plugins()
manager.list_plugins()
# Test plugin
result = manager.execute_plugin("emoji_enhancer", {"text": "Hello Python"})
print(f"\n๐ค Result: {result.get('text')}")
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Forgetting the Underscore
# โ Wrong way - missing underscores!
if name == "main": # ๐ฅ This will never be True!
print("This won't run!")
# โ
Correct way - double underscores on both sides!
if __name__ == "__main__": # ๐ฏ Perfect!
print("This runs when executed directly! ๐")
๐คฏ Pitfall 2: Import Side Effects
# โ Dangerous - code runs on import!
print("๐ข Loading module...") # This ALWAYS runs!
database = connect_to_database() # ๐ฅ Connects every import!
send_email("Module loaded!") # ๐ฑ Sends email on import!
# โ
Safe - control when code runs!
def initialize():
"""๐ Initialize the module"""
print("๐ข Initializing...")
return connect_to_database()
if __name__ == "__main__":
# Only runs when executed directly
db = initialize()
print("โ
Module ready for testing!")
๐ฐ Pitfall 3: Testing Module Imports
# โ Can't test if module imports work
def calculate(x, y):
return x + y
print(calculate(2, 3)) # Always prints when imported!
# โ
Better - test code separate from module code
def calculate(x, y):
"""๐งฎ Calculate sum of two numbers"""
return x + y
if __name__ == "__main__":
# Test code here
print("๐งช Running tests...")
assert calculate(2, 3) == 5
assert calculate(-1, 1) == 0
print("โ
All tests passed!")
๐ ๏ธ Best Practices
- ๐ฏ Always Use It: Add to every Python file that can be run
- ๐ฆ Keep Imports Clean: Donโt execute code at module level
- ๐งช Test Friendly: Put test code under the if statement
- ๐๏ธ Use main() Function: Organize executable code in main()
- ๐ Document Module Usage: Show how to import AND run
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Multi-Purpose Calculator Module
Create a calculator that works both as a module and a standalone program:
๐ Requirements:
- โ Basic math operations (add, subtract, multiply, divide)
- ๐งฎ Scientific functions (power, square root, factorial)
- ๐ Statistics functions (mean, median, mode)
- ๐จ Command-line interface when run directly
- ๐ Clean import for use in other programs
- ๐ฏ Each function needs descriptive emoji!
๐ Bonus Points:
- Add memory feature (store/recall)
- Implement calculation history
- Create a simple GUI when run directly
๐ก Solution
๐ Click to see solution
# ๐งฎ calculator.py
import math
import statistics
from typing import List, Union
class Calculator:
"""๐งฎ A multi-purpose calculator"""
def __init__(self):
self.memory = 0
self.history = []
# Basic operations
def add(self, a: float, b: float) -> float:
"""โ Add two numbers"""
result = a + b
self._record(f"{a} + {b} = {result}")
return result
def subtract(self, a: float, b: float) -> float:
"""โ Subtract b from a"""
result = a - b
self._record(f"{a} - {b} = {result}")
return result
def multiply(self, a: float, b: float) -> float:
"""โ๏ธ Multiply two numbers"""
result = a * b
self._record(f"{a} ร {b} = {result}")
return result
def divide(self, a: float, b: float) -> float:
"""โ Divide a by b"""
if b == 0:
raise ValueError("Cannot divide by zero! ๐ซ")
result = a / b
self._record(f"{a} รท {b} = {result}")
return result
# Scientific operations
def power(self, base: float, exponent: float) -> float:
"""โก Calculate base to the power of exponent"""
result = math.pow(base, exponent)
self._record(f"{base}^{exponent} = {result}")
return result
def square_root(self, n: float) -> float:
"""โ Calculate square root"""
if n < 0:
raise ValueError("Cannot calculate square root of negative number! ๐ซ")
result = math.sqrt(n)
self._record(f"โ{n} = {result}")
return result
def factorial(self, n: int) -> int:
"""โ Calculate factorial"""
if n < 0:
raise ValueError("Factorial is not defined for negative numbers! ๐ซ")
result = math.factorial(n)
self._record(f"{n}! = {result}")
return result
# Statistics operations
def mean(self, numbers: List[float]) -> float:
"""๐ Calculate mean (average)"""
result = statistics.mean(numbers)
self._record(f"mean({numbers}) = {result}")
return result
def median(self, numbers: List[float]) -> float:
"""๐ Calculate median"""
result = statistics.median(numbers)
self._record(f"median({numbers}) = {result}")
return result
def mode(self, numbers: List[float]) -> float:
"""๐ Calculate mode (most common)"""
result = statistics.mode(numbers)
self._record(f"mode({numbers}) = {result}")
return result
# Memory operations
def memory_store(self, value: float):
"""๐พ Store value in memory"""
self.memory = value
print(f"๐พ Stored {value} in memory")
def memory_recall(self) -> float:
"""๐ Recall value from memory"""
print(f"๐ Recalled {self.memory} from memory")
return self.memory
def memory_clear(self):
"""๐๏ธ Clear memory"""
self.memory = 0
print("๐๏ธ Memory cleared")
# History
def _record(self, operation: str):
"""๐ Record operation in history"""
self.history.append(operation)
def show_history(self):
"""๐ Show calculation history"""
if not self.history:
print("๐ญ No calculations yet!")
else:
print("\n๐ Calculation History:")
for i, calc in enumerate(self.history[-10:], 1):
print(f" {i}. {calc}")
def interactive_mode():
"""๐ฎ Interactive calculator mode"""
calc = Calculator()
print("๐งฎ Python Calculator ๐งฎ")
print("=" * 40)
print("Commands: +, -, *, /, ^, sqrt, !, mean, median, mode")
print("Memory: ms (store), mr (recall), mc (clear)")
print("Other: history, help, quit")
print("=" * 40)
while True:
try:
command = input("\n๐งฎ Enter command: ").strip().lower()
if command == "quit":
print("๐ Thanks for calculating! Goodbye! ๐")
break
elif command == "help":
print("๐ Enter operations like: 2 + 3, 5 * 4, sqrt 16")
elif command == "history":
calc.show_history()
elif command == "mc":
calc.memory_clear()
elif command == "mr":
print(f"Result: {calc.memory_recall()}")
elif command.startswith("ms "):
value = float(command.split()[1])
calc.memory_store(value)
elif "+" in command:
parts = command.split("+")
result = calc.add(float(parts[0]), float(parts[1]))
print(f"Result: {result}")
elif "-" in command and not command.startswith("-"):
parts = command.split("-")
result = calc.subtract(float(parts[0]), float(parts[1]))
print(f"Result: {result}")
elif "*" in command:
parts = command.split("*")
result = calc.multiply(float(parts[0]), float(parts[1]))
print(f"Result: {result}")
elif "/" in command:
parts = command.split("/")
result = calc.divide(float(parts[0]), float(parts[1]))
print(f"Result: {result}")
elif "^" in command:
parts = command.split("^")
result = calc.power(float(parts[0]), float(parts[1]))
print(f"Result: {result}")
elif command.startswith("sqrt "):
n = float(command.split()[1])
result = calc.square_root(n)
print(f"Result: {result}")
elif command.endswith("!"):
n = int(command[:-1])
result = calc.factorial(n)
print(f"Result: {result}")
else:
print("โ Unknown command. Type 'help' for assistance.")
except ValueError as e:
print(f"โ Error: {e}")
except Exception as e:
print(f"๐ฅ Unexpected error: {e}")
# ๐ฏ Run interactive mode only when executed directly
if __name__ == "__main__":
interactive_mode()
๐ Key Takeaways
Youโve learned so much! Hereโs what you can now do:
- โ
Understand
__name__
and why it changes ๐ญ - โ Create dual-purpose files that work as scripts AND modules ๐
- โ Prevent import side effects in your code ๐ก๏ธ
- โ Write professional Python modules like a pro ๐ผ
- โ Debug module import issues with confidence ๐
Remember: The if __name__ == "__main__":
pattern is your friend! It helps you write flexible, reusable Python code. ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve mastered the script vs module pattern!
Hereโs what to do next:
- ๐ป Practice with the calculator exercise above
- ๐๏ธ Convert your existing scripts to use this pattern
- ๐ Move on to our next tutorial: Module Search Path and sys.path
- ๐ Share your dual-purpose modules with others!
Remember: Every Python expert uses this pattern daily. Now youโre one of them! Keep coding, keep learning, and most importantly, have fun! ๐
Happy coding! ๐๐โจ