Defining and Calling Functions
Functions are reusable blocks of code that perform a specific task. They allow you to organize, reuse, and simplify your programs by breaking them into smaller pieces.
What is a Function?
- A function is a group of statements that run only when called.
- Functions help avoid repeating code and make programs easier to maintain.
- Python has many built-in functions (like
len(),print(),range()), and you can also define your own.
Defining a Function
You define a function in Python using the def keyword.
def greet():
print("Hello, welcome to Python!")
def→ keyword to define a function.greet→ function name.()→ parentheses (can hold parameters).:→ starts the function body.- Indentation → defines the function’s block.
Calling a Function
To use (execute) a function, call it by name followed by parentheses.
greet() # Output: Hello, welcome to Python!
A function can be called multiple times:
for i in range(3):
greet()
Functions with Parameters
Functions can accept inputs, called parameters (or arguments when passed).
def greet_user(name):
print("Hello,", name)
greet_user("Amr") # Output: Hello, Amr
greet_user("Sara") # Output: Hello, Sara
- Parameters make functions flexible and reusable.
Functions with Multiple Parameters
You can define multiple parameters separated by commas.
def add(x, y):
print("Sum is:", x + y)
add(3, 5) # Output: Sum is: 8
Return Values
Functions can return results using the return statement.
def square(n):
return n * n
result = square(4)
print(result) # Output: 16
- Without
return, the function returnsNoneby default.
Default Parameters
You can set default values for parameters.
def greet_user(name="Guest"):
print("Welcome,", name)
greet_user() # Output: Welcome, Guest
greet_user("Amr") # Output: Welcome, Amr
Keyword Arguments
You can call functions by explicitly naming parameters.
def introduce(name, age):
print(f"My name is {name} and I am {age} years old.")
introduce(age=25, name="Ali")
Function Arguments (Positional, Keyword, `*args`, `**kwargs`)
Functions in Python can accept inputs in different ways. Understanding how arguments are passed makes your functions flexible and reusable.
1. Positional Arguments
- The most common type of arguments.
- Values are passed in order to the function’s parameters.
def subtract(a, b):
print(a - b)
subtract(10, 3) # Output: 7
subtract(3, 10) # Output: -7
The order matters!
2. Keyword Arguments
- Instead of relying on position, you can pass arguments by name.
- Makes functions more readable and avoids mistakes with order.
def introduce(name, age):
print(f"My name is {name} and I am {age} years old.")
introduce(age=25, name="Amr")
# Output: My name is Amr and I am 25 years old.
3. Default Arguments
- Parameters can have default values.
- If no value is provided, the default is used.
def greet(name="Guest"):
print("Hello,", name)
greet() # Output: Hello, Guest
greet("Sara") # Output: Hello, Sara
4. Arbitrary Positional Arguments (`*args`)
- Use
*argswhen you don’t know how many positional arguments will be passed. argscollects all extra arguments into a tuple.
def total(*numbers):
print(numbers) # tuple of all arguments
return sum(numbers)
print(total(1, 2, 3)) # Output: 6
print(total(5, 10, 15, 20)) # Output: 50
5. Arbitrary Keyword Arguments (`**kwargs`)
- Use
kwargsto accept any number of keyword arguments**. kwargscollects them into a dictionary.
def profile(**info):
print(info)
profile(name="Amr", age=30, country="Netherlands")
# Output: {'name': 'Amr', 'age': 30, 'country': 'Netherlands'}
You can access values like a normal dictionary:
def profile(**info):
print("Name:", info.get("name"))
print("Age:", info.get("age"))
profile(name="Sara", age=25)
6. Mixing Different Argument Types
Python functions can combine all these types, but the order matters:
**Order of arguments in function definition**
positional → args → default → *kwargs
def demo(a, b, *args, x=10, **kwargs):
print("a:", a)
print("b:", b)
print("args:", args)
print("x:", x)
print("kwargs:", kwargs)
demo(1, 2, 3, 4, 5, x=99, name="Amr", age=30)
Output:
a: 1
b: 2
args: (3, 4, 5)
x: 99
kwargs: {'name': 'Amr', 'age': 30}
Return Values and Scope (Local vs Global Variables)
Functions often need to send back results using return. Along with this, Python has rules about where variables “live,” called scope. Understanding both is key to writing clean, bug-free programs.
1. Return Values
- A function can return data with the
returnkeyword. - Without
return, functions returnNoneby default.
def square(x):
return x * x
result = square(5)
print(result) # Output: 25
- Functions can return multiple values as a tuple:
def get_info():
return "Amr", 30, "Netherlands"
name, age, country = get_info()
print(name, age, country)
# Output: Amr 30 Netherlands
2. The Concept of Scope
Scope defines where a variable can be accessed in a program.
Python uses the LEGB rule:
- L (Local) → inside the current function.
- E (Enclosing) → in nested functions.
- G (Global) → variables defined at the top-level of a module.
- B (Built-in) → Python’s built-in names (like
len,print).
3. Local Variables
- Variables defined inside a function are local to that function.
- They exist only while the function runs.
def greet():
message = "Hello from inside!" # local variable
print(message)
greet()
# print(message) # Error: name 'message' is not defined
4. Global Variables
- Variables defined outside any function are global.
- They can be read inside functions, but not modified directly unless declared
global.
counter = 0 # global variable
def increase():
global counter
counter += 1
increase()
print(counter) # Output: 1
Overusing `global` makes code harder to maintain. Prefer returning values instead.
5. Enclosing (Nonlocal) Scope
- In nested functions, the inner function can access variables from the outer function, but cannot modify them directly.
- Use
nonlocalto modify them.
def outer():
x = "outer value"
def inner():
nonlocal x
x = "changed by inner"
inner()
print(x)
outer() # Output: changed by inner
6. Best Practices
- Keep variables local whenever possible.
- Use
returninstead ofglobalto pass results. - Avoid reusing variable names from outer scopes (reduces confusion).
Lambda Functions (Anonymous Functions)
Sometimes, you need a small, throwaway function for quick calculations. Instead of defining a full def function, Python lets you use lambda functions, also called anonymous functions.
1. What is a Lambda Function?
- A lambda function is a single-line function defined without a name.
- Syntax:
lambda arguments: expression
- Unlike
def, it doesn’t need a name and can only contain a single expression (no statements, loops, or assignments inside).
2. Basic Example
# Normal function
def square(x):
return x * x
# Lambda equivalent
square_lambda = lambda x: x * x
print(square_lambda(5)) # Output: 25
3. Multiple Parameters
Lambda functions can take multiple arguments.
add = lambda a, b: a + b
print(add(3, 7)) # Output: 10
compare = lambda x, y: x if x > y else y
print(compare(5, 8)) # Output: 8
4. Using Lambdas with Built-in Functions
Lambda functions are often used with higher-order functions like map, filter, and sorted.
- map() → applies a function to every element
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, nums))
print(squares) # [1, 4, 9, 16]
- filter() → keeps only elements that meet a condition
nums = [5, 10, 15, 20, 25]
evens = list(filter(lambda n: n % 2 == 0, nums))
print(evens) # [10, 20]
- sorted() → customize sorting
people = [("Amr", 30), ("Sara", 25), ("Ali", 35)]
sorted_people = sorted(people, key=lambda person: person[1])
print(sorted_people) # [('Sara', 25), ('Amr', 30), ('Ali', 35)]
5. Lambdas Inside Other Functions
Sometimes used as inline helpers.
def make_multiplier(n):
return lambda x: x * n
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # Output: 10
print(triple(5)) # Output: 15
6. Limitations of Lambdas
- Only one expression allowed (no multiple statements).
- Harder to debug than normal functions.
- Best used for short, simple logic, not complex operations.
Modules and the Import System
As your Python programs grow, it’s best to organize code into multiple files instead of keeping everything in one place. Python provides modules and the import system for this purpose.
1. What is a Module?
- A module is simply a Python file (
.py) that contains functions, classes, or variables. - Modules help you reuse code and keep projects structured.
Example: Create a file called mymath.py
# mymath.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
Now you can use this module in another Python file.
2. Importing a Module
Use the import keyword to bring in a module.
import mymath
print(mymath.add(2, 3)) # Output: 5
print(mymath.multiply(4, 5)) # Output: 20
3. Importing Specific Functions
You don’t have to import the entire module; you can import only what you need.
from mymath import add
print(add(10, 20)) # Output: 30
In this case, you can call `add()` directly without prefixing `mymath.`.
4. Using Aliases
You can give shorter names to modules or functions using as.
import mymath as mm
print(mm.add(5, 7)) # Output: 12
from mymath import multiply as mul
print(mul(3, 4)) # Output: 12
5. Standard Library Modules
Python comes with a large standard library.
Examples:
import math
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
import random
print(random.randint(1, 10)) # random number 1–10
6. Importing All ( Not Recommended)
You can import everything from a module, but this may cause name conflicts.
from math import *
print(sin(3.14)) # Works, but pollutes namespace
7. The Import Search Path
When you import a module, Python searches in:
- The current directory.
- The PYTHONPATH environment variable (if set).
- The standard library.
- Installed packages in site-packages.
import sys
print(sys.path) # shows the list of directories Python searches
8. Packages (Folders with `__init__.py`)
- A package is a collection of modules inside a folder.
- The folder must contain an
__init__.pyfile (even if empty).
Example project structure:
myproject/
mathutils/
__init__.py
add.py
multiply.py
You can import like this:
from mathutils import add
9. Reloading Modules
Sometimes, during development, you want to reload a module after editing it.
import importlib
import mymath
# reload after changes
importlib.reload(mymath)
Built-in vs. User-defined Modules
Python’s modular system allows you to organize and reuse code. Modules can be either built-in (provided by Python itself) or user-defined (created by you or others).
1. Built-in Modules
- These come with Python’s standard library.
- No need to install or create them—just import and use.
- They provide ready-to-use tools for math, random numbers, dates, file operations, networking, etc.
Examples:
import math
print(math.sqrt(25)) # 5.0
print(math.pi) # 3.14159...
import random
print(random.randint(1, 10)) # random number between 1 and 10
import datetime
print(datetime.datetime.now()) # current date and time
Popular built-in modules:
os→ interact with operating system (files, paths)sys→ system-specific parameters, Python pathmath→ mathematical functionsrandom→ random numbersdatetime→ dates and timesjson→ JSON encoding/decoding
2. User-defined Modules
- A user-defined module is any
.pyfile you create yourself. - You can put functions, classes, and variables inside and import them into other programs.
Example: create a file mymath.py
# mymath.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
Now use it in another file:
import mymath
print(mymath.add(2, 3)) # 5
print(mymath.multiply(4, 5)) # 20
3. Combining Both
You can freely use built-in modules alongside user-defined modules.
import math
import mymath
print(mymath.add(5, 10)) # user-defined
print(math.factorial(5)) # built-in
4. Why Use User-defined Modules?
- Reusability → write once, use many times.
- Organization → split large programs into smaller files.
- Collaboration → different team members can work on different modules.
5. Key Differences
| Feature | Built-in Modules | User-defined Modules |
|---|---|---|
| Source | Part of Python’s standard library | Written by the programmer |
| Availability | Always available | Must be created/imported manually |
| Examples | math, os, random, sys | mymath.py, utils.py, project modules |
| Purpose | Provide ready-made tools | Solve specific project needs |