Copy-pasting code is a primary source of bugs in software engineering. Functions allow you to encapsulate a piece of logic, give it a name, and reuse it infinitely. They are the fundamental building blocks of scalable applications.
1Defining and Calling
A function is a named block of code that only executes when explicitly commanded to do so. In Python, we define functions using the def keyword, followed by the function name (usually snake_case), parentheses, and a colon. All the logic belonging to that function must be indented underneath it.
Defining the function does absolutely nothing on its own; it just stores the logic in memory. To actually execute the code, you must 'call' the function by typing its name followed by parentheses elsewhere in your script.
# 1. Definition (Stores logic in memory)
def initialize_system():
print("Booting up AI Core...")
print("Loading weights... Done.")
# 2. The Call (Executes the logic)
initialize_system()Loading weights... Done.
2Parameters and Return Values
Functions are significantly more powerful when they can accept inputs and yield outputs. 'Parameters' act as placeholders inside the function definition. When you call the function, you pass in real data known as 'Arguments' to fill those placeholders.
Once the function calculates a result, it needs to give that data back to the main program. We use the return keyword for this. The moment Python hits a return statement, it immediately exits the function and sends the specified data back to the caller. Any code written beneath a return statement inside the function will never execute.
# 'price' and 'tax_rate' are parameters
def calculate_total(price, tax_rate):
total = price + (price * tax_rate)
return total
print("This line is dead code and never runs!")
# 100 and 0.05 are arguments
final_cost = calculate_total(100, 0.05)
print(f"Total Cost: ${final_cost}")3Variable Scope (Local vs Global)
One of the most critical concepts in software engineering is 'Scope'. If you create a variable *inside* a function, it is 'Local' to that function. It exists only temporarily while the function is running, and is immediately destroyed from memory the second the function finishes.
This is actually a feature, not a bug. It prevents variables in one function from accidentally overwriting variables in a completely different part of your application. If you need data to survive outside the function, you must explicitly return it. Attempting to print or access a local variable from the global scope will result in a fatal NameError.
def secure_process():
# This variable is trapped inside the function
secret_key = "ABC-123"
print("Processing with key...")
secure_process()
# This crashes the program. 'secret_key' no longer exists.
print(secret_key)Traceback (most recent call last):
File "main.py", line 9, in <module>
print(secret_key)
NameError: name 'secret_key' is not defined
