DEV Community

Tejas Shinkar
Tejas Shinkar

Posted on

Functions, Scope (Local/Global), Arguments, return, Lambda, filter()/map()/reduce(), Modules

šŸ“Œ Key Concepts Overview

Concept One-Line Definition
Function A reusable, named block of code that runs when called
Parameter vs Argument Parameter = name in definition. Argument = value passed at call time
Local variable Exists only inside the function it's defined in
Global variable Exists outside any function, accessible everywhere
global keyword Lets a function modify a global variable
return Sends a value back from the function and ends it
*args Collects extra positional arguments into a tuple
**kwargs Collects extra keyword arguments into a dict
lambda A small, unnamed (anonymous) one-line function
filter() Keeps only elements that satisfy a condition
map() Applies an operation to every element
reduce() Aggregates an iterable into a single value
Module A .py file containing reusable functions/classes

🧩 Part 1 — Why Functions?

# WITHOUT a function — code duplicated everywhere
a, b = 10, 20
print(a + b)
str1 = input('Enter a string: ')
if str1 == str1[::-1]:
    print('Palindrome')

# ... later in the program, same logic copy-pasted again ...
Enter fullscreen mode Exit fullscreen mode

Functions exist to:

  • Avoid repeating code (write once, call many times)
  • Reduce program size
  • Organize logic into reusable, testable units
def add(a, b):
    c = a + b
    print(c)

def is_palindrome():
    str1 = input('Enter a string: ')
    if str1 == str1[::-1]:
        print('Palindrome')
    else:
        print('Not a Palindrome')

# Now reuse anywhere:
add(327, 46)
is_palindrome()
Enter fullscreen mode Exit fullscreen mode

Two Types of Functions

Type Example
In-built (Python provides) print(), len(), type(), input()
User-defined (you write) def add(a, b): ...

The 2 Components of Every Function

  1. Function Definition — written using def, contains the logic. Does NOT run on its own.
  2. Function Call — function_name() — this is what actually executes the code.
def add():            # ← definition (just creates it, doesn't run it)
    print('Hello')

add()                  # ← call (THIS runs it)
Enter fullscreen mode Exit fullscreen mode

āš ļø Don't Shadow Built-in Functions (Recap + New Example)

# āŒ DANGEROUS — overwrites Python's built-in print/len
def print():
    print('Hello')      # this would now infinitely recurse / break

def len():
    print('Hello')

len('Python')            # āŒ TypeError — len() no longer works as expected,
                          # it now expects ZERO arguments because you redefined it
Enter fullscreen mode Exit fullscreen mode

Lesson: Never name your functions print, len, list, input, type, etc. — you permanently break the built-in within that scope.


šŸŽÆ Parameterised vs Non-Parameterised Functions

# Non-parameterised — takes no input, asks inside the function
def add():
    a = int(input('Enter a number: '))
    b = int(input('Enter the next number: '))
    c = a + b
    print(c)

# Parameterised — takes input directly via parameters
def add1(x, y):
    c = x + y
    print(c)

add1(18, 46)     # 18 and 46 are ARGUMENTS passed to PARAMETERS x and y
Enter fullscreen mode Exit fullscreen mode

Parameter vs Argument — exact definition:

Term Where it appears Example
Parameter In the function definition def add1(x, y): → x, y
Argument In the function call add1(18, 46) → 18, 46
def even_odd(x):
    if x % 2 == 0:
        print('Even')
    else:
        print('odd')

even_odd(37)        # 'odd'
Enter fullscreen mode Exit fullscreen mode

ā˜ļø DevOps-Flavoured Function Examples

def restart_service(service_name):
    print(f"Restarting {service_name} service......")
    print(f"{service_name} restarted successfully")

restart_service("nginx")
restart_service("Docker")

def check_disk_usage():
    print("Checking disk usage...")
    print("Disk usage is 75%")

# Real system command execution
import os
def check_disk():
    os.system("df -h")        # runs actual shell command
Enter fullscreen mode Exit fullscreen mode

lists have .append() but strings do NOT — str1.append('xyz') → AttributeError. Strings are immutable, so there's no in-place method to add to them; use concatenation or += instead.


šŸ”’ Part 2 — Local vs Global Variables

Local Variables

def add(a, b):           # a, b, c are LOCAL — only exist inside this function
    c = a + b
    print(c)

print(a)        # āŒ NameError: name 'a' is not defined — 'a' doesn't exist outside the function
print(c)        # āŒ NameError — same reason
Enter fullscreen mode Exit fullscreen mode

Global Variables

x = 47          # global variable
y = 48          # global variable

def add1(x, y):       # these x, y are LOCAL parameters — shadow the global ones
    c = x + y
    print(c)

add1(x, y)             # add1(47, 48) → prints 95
print(x)               # 47 — global x is untouched, local x inside function was separate
Enter fullscreen mode Exit fullscreen mode

Key rule: A local variable and a global variable can share the same name — they are still two completely different variables. The function's local one "shadows" the global one only inside the function body.

Reading a Global Variable Inside a Function — Works Fine

count = 10
def update1():
    global count
    print("Inside function:", count)   # can READ global without 'global' keyword too

update1()         # Inside function: 10
Enter fullscreen mode Exit fullscreen mode

Modifying a Global Variable Without global — Creates a NEW Local Variable

count = 10
def update():
    count = 20                          # this creates a NEW local 'count'
    print("Inside function:", count)    # Inside function: 20

update()
print(count)        # 10 — the GLOBAL count is untouched!
Enter fullscreen mode Exit fullscreen mode

The global Keyword — Actually Modify the Global Variable

count = 10
def update2():
    global count          # tells Python: use the OUTER 'count', don't create a local one
    count = 20
    print("Inside function:", count)

update2()
print(count)        # 20 — global count WAS modified this time
Enter fullscreen mode Exit fullscreen mode

The #1 confusion point in this topic:

Code Effect on global variable
count = 20 (no global) inside function Creates a separate LOCAL variable — global untouched
global count then count = 20 Modifies the actual GLOBAL variable

ā†©ļø Part 3 — return Statement

Print vs Return — The Critical Difference

def add1(x, y):
    c = x + y
    return c              # sends value BACK to the caller

result1 = add1(10, 20)    # result1 now HOLDS the value 30
print(result1)             # 30
print(result1 ** 2)        # 900 āœ… — can use the result further

def add2(x, y):
    c = x + y
    print(c)               # just prints — doesn't send value back

result2 = add2(10, 20)     # prints 30, but returns nothing
print(result2)              # None  āš ļø — function with no return gives None
print(result2 ** 2)         # āŒ TypeError: unsupported operand for None
Enter fullscreen mode Exit fullscreen mode

Golden Rule: Use print() inside a function only for display. Use return when you need to use the result later in your program (further calculations, conditions, storing in variables/lists).

return Terminates the Function Immediately

def add1(x, y):
    c = x + y
    return c
    print('Hello')        # āš ļø NEVER runs — return already exited the function

def add2(x, y):
    c = x + y
    print('Hello')        # runs FIRST — this line is before return
    return c

add2(10, 20)               # prints 'Hello', then returns c (silently, unless captured)
Enter fullscreen mode Exit fullscreen mode

Returning Multiple Values (as a Tuple)

def calc(x, y):
    c = x + y
    d = x - y
    return c, d            # returns BOTH — Python packs them into a tuple

t = calc(20, 10)
print(t, type(t))          # (30, 10) <class 'tuple'>
Enter fullscreen mode Exit fullscreen mode

Practical Functions — Factorial & Prime Check

# Factorial
def factorial(n):
    f = 1
    for i in range(1, n + 1):
        f *= i
    return f

x = factorial(5)
print(x)            # 120

# Prime check
def is_prime():
    n = int(input('Enter a number: '))
    if n < 2:
        return False
    for i in range(2, n):
        if n % i == 0:
            return False
    else:
        return True

print(is_prime())
Enter fullscreen mode Exit fullscreen mode

šŸ“„ Part 4 — Types of Arguments

1. Positional argument
2. Keyword argument
3. Default argument
4. Variable-length argument
     a. *args    → variable positional args (tuple)
     b. **kwargs → variable keyword args (dict)
Enter fullscreen mode Exit fullscreen mode

1. Positional Arguments — Order Matters

def emp_details(name, age, salary):
    print('Name :- ', name)
    print('Age :- ', age)
    print('Salary :- ', salary)

emp_details('Sourav', 25, 75000)        # āœ… correct order
emp_details('Sourav', 75000, 25)        # āš ļø runs but WRONG — age=75000, salary=25 (logic bug!)
emp_details(25, 75000, 'Sourav')        # āš ļø runs but completely scrambled

emp_details('Sourav', 75000)            # āŒ TypeError: missing required argument 'salary'
Enter fullscreen mode Exit fullscreen mode

Rule: Number of arguments must exactly match number of parameters, and order determines which value goes to which parameter.

2. Keyword Arguments — Name the Parameter Explicitly

emp_details('Sourav', 25, salary=75000)               # āœ… mix positional + keyword
emp_details('Sourav', age=25, salary=75000)            # āœ… all named after first
emp_details(salary=75000, age=25, name='Sourav')        # āœ… order doesn't matter when all keyworded

emp_details(salary=75000, age=25, Name='Sourav')        # āŒ TypeError — 'Name' ≠ 'name' (case-sensitive!)
emp_details(salary=75000, age=25, 'Sourav')              # āŒ SyntaxError — positional AFTER keyword not allowed
Enter fullscreen mode Exit fullscreen mode

āš ļø Critical Rule: If mixing positional and keyword arguments, all positional arguments must come BEFORE any keyword arguments.

3. Default Arguments — Fallback Value if Not Provided

def emp_details(name, age, salary, loc='Bangalore'):    # loc has a default
    print('Name :- ', name)
    print('Age :- ', age)
    print('Salary :- ', salary)
    print('Location :- ', loc)

emp_details('Sourav', salary=75000, age=25)                          # loc defaults to 'Bangalore'
emp_details('Sourav', salary=75000, age=25, loc='Delhi')              # loc overridden to 'Delhi'
Enter fullscreen mode Exit fullscreen mode

DevOps relevance: Default arguments are everywhere — region='ap-south-1', timeout=30, retries=3 — sensible defaults that can be overridden when needed.

4. Variable-Length Arguments

def add(x, y):
    return x + y

add(10, 38)              # āœ… works
add(37, 25, 58)           # āŒ TypeError — too many arguments, only 2 parameters defined
Enter fullscreen mode Exit fullscreen mode

*args — Variable Positional Arguments → packed into a TUPLE

def xyz(*args):
    print(args, type(args))

xyz(10, 49, 28, 'Python')      # (10, 49, 28, 'Python') <class 'tuple'>

def add(*args):
    c = sum(args)
    return c

add(20, 49)                     # 69
add(18, 437, 28)                 # 483
add(37, 483, 746, 276, 373, 483, 27893)   # works with ANY number of arguments
Enter fullscreen mode Exit fullscreen mode

`kwargs` — Variable Keyword Arguments → packed into a DICT**

def abc(**kwargs):
    print(kwargs, type(kwargs))

abc(a=10, b=20, c=30, d=40)
# {'a': 10, 'b': 20, 'c': 30, 'd': 40} <class 'dict'>
Enter fullscreen mode Exit fullscreen mode

DevOps relevance: *args/**kwargs are everywhere in real tools — e.g., Boto3 functions, Django views, decorators — to accept flexible config without hardcoding every possible parameter.


⚔ Part 5 — Lambda (Anonymous Functions)

# Syntax: lambda arguments: expression
# Called immediately: (lambda arguments: expression)(call_arguments)

# Add two numbers
(lambda x, y: x + y)(20, 48)        # 68

a = (lambda x, y: x * y)(20, 20)
print(a)                              # 400

# Conditional (ternary) inside lambda
(lambda a, b, c: a if a > b and a > c else b if b > a and b > c else c)(28, 37, 18)
# 37 — finds the maximum of 3 numbers

(lambda x: 'Even' if x % 2 == 0 else 'Odd')(23)
# 'Odd'
Enter fullscreen mode Exit fullscreen mode

When to use lambda: Quick, throwaway, single-expression functions — especially as arguments to filter(), map(), reduce(), or sorted(key=...). NOT meant for complex multi-line logic (use a regular def for that).


šŸ” Part 6 — filter(), map(), reduce()

filter() — Keep Elements That Match a Condition

# filter(lambda argument: condition, iterable)
# Returns a filter OBJECT — wrap with list() or tuple() to see values

# Manual for-loop equivalent (what filter replaces):
lst = [1,2,3,4,5,6,7,8,9,10]
lst1 = []
for i in lst:
    if i % 2 == 0:
        lst1.append(i)
print(lst1)                          # [2,4,6,8,10]

# Same thing with filter() — one line
print(list(filter(lambda x: x % 2 == 0, lst)))     # [2,4,6,8,10]

# DevOps example: flag high CPU usage servers
cpu_usage = [48, 27, 93, 14, 86]
high_cpu = list(filter(lambda x: x > 80, cpu_usage))
print(high_cpu)                       # [93, 86]

# Extract vowels from a string
str1 = 'ncihvewc cnoejife2oiyfecndlncjihuhew'
print(tuple(filter(lambda x: x.lower() in 'aeiou', str1)))
Enter fullscreen mode Exit fullscreen mode

map() — Apply an Operation to Every Element

# map(lambda argument: expression, iterable)
# Returns a map OBJECT — wrap with list() or tuple()

# Manual for-loop equivalent:
lst = [1,2,3,4,5,6,7,8,9,10]
lst1 = []
for i in lst:
    lst1.append(i**2)
print(lst1)                           # [1,4,9,16,25,36,49,64,81,100]

# Same with map() — one line
print(list(map(lambda x: x**2, lst)))   # [1,4,9,16,25,36,49,64,81,100]

# map() on heterogeneous list — operation applies to EVERY element
lst = [1, 2, 3, 'Python', 4, 5, 6, 7, 'DevOps', 8, 9, 10]
print(list(map(lambda x: x*2, lst)))
# [2,4,6,'PythonPython',8,10,12,14,'DevOpsDevOps',16,18,20]
# Note: x*2 means NUMERIC doubling for numbers, STRING repetition for strings

# map() on a string — applies to each character
str1 = "python"
print(list(map(lambda x: x*2, str1)))
# ['pp','yy','tt','hh','oo','nn']
Enter fullscreen mode Exit fullscreen mode

filter() vs map() — Core Difference:

Function Purpose Output size
filter() Keep only items matching a condition ≤ original (may shrink)
map() Transform every item = original (same size)

reduce() — Aggregate to a Single Value

from functools import reduce      # must import — not built-in like filter/map

# reduce(lambda x, y: expression, iterable)

lst = [1,2,3,4,5,6,7,8,9,10]
res = reduce(lambda x, y: x + y, lst)
print(res)                          # 55 — sum of all elements

# Find minimum
reduce(lambda x, y: x if x < y else y, lst)    # 1

# Find maximum
reduce(lambda x, y: x if x > y else y, lst)    # 10
Enter fullscreen mode Exit fullscreen mode

How reduce() works internally: It takes the first two elements, applies the lambda, takes that result + the next element, applies the lambda again — repeating until one value remains.


šŸ“¦ Part 7 — Modules

# A module = a .py file containing functions/classes
# Access with: module_name.function_name()

import Mod                  # imports Mod.py
Mod.greet()                  # calls greet() function inside Mod.py
Mod.adding(37, 27)
Mod.mul(37, 17)
Enter fullscreen mode Exit fullscreen mode

Best practice: Don't put "free executable code" (code that runs immediately on import) inside a module — only function/class definitions. This keeps the module reusable and side-effect-free when imported elsewhere.

DevOps relevance: This is exactly how you'd structure a utils.py or aws_helpers.py module with reusable functions like get_ec2_status(), restart_service(), parse_log() — imported across multiple automation scripts.


ā˜ļø DevOps / Cloud Use Cases

# 1. Reusable service restart function with default region
def restart_service(service_name, region='ap-south-1'):
    print(f"Restarting {service_name} in {region}...")

restart_service('nginx')
restart_service('nginx', region='us-east-1')

# 2. *args to accept any number of servers to health-check
def health_check(*servers):
    for s in servers:
        print(f'Checking {s}...')

health_check('web-01', 'web-02', 'db-01')

# 3. **kwargs to accept flexible AWS-style config
def launch_instance(**config):
    print(config)

launch_instance(instance_type='t2.micro', region='ap-south-1', count=2)

# 4. filter() to find unhealthy servers
server_status = {'web-01': 200, 'web-02': 500, 'db-01': 200, 'cache-01': 503}
unhealthy = list(filter(lambda s: server_status[s] != 200, server_status))
print(unhealthy)                      # ['web-02', 'cache-01']

# 5. map() to convert a list of instance IDs into ARNs
instance_ids = ['i-001', 'i-002', 'i-003']
arns = list(map(lambda i: f'arn:aws:ec2:ap-south-1:123456789:instance/{i}', instance_ids))
print(arns)

# 6. reduce() to sum total monthly AWS costs across services
from functools import reduce
service_costs = [120.50, 45.25, 300.10, 15.00]
total = reduce(lambda x, y: x + y, service_costs)
print(f'Total monthly cost: ${total}')

# 7. global + counter pattern for tracking retries across calls
retry_count = 0
def attempt_connection():
    global retry_count
    retry_count += 1
    print(f'Attempt #{retry_count}')

attempt_connection()
attempt_connection()
print(f'Total retries: {retry_count}')
Enter fullscreen mode Exit fullscreen mode

āŒ Common Mistakes

Mistake Code Fix
Confusing print() and return Function uses print(), then tries result = func() and uses result Use return when you need the value later
Forgetting global keyword count = 20 inside function — global stays unchanged Use global count before modifying
Code after return Lines after return never execute Put cleanup/logging BEFORE return
Positional after keyword f(a=1, 2) All positional args must come first
Case-sensitive keyword names f(Name='x') when param is name Match exact parameter name and case
Shadowing built-ins with function names def len(): ... Never name functions after built-ins
Forgetting list()/tuple() around filter/map print(filter(...)) → shows object, not values Wrap with list() or tuple()
Forgetting to import functools for reduce reduce(...) → NameError from functools import reduce
String .append() str1.append('x') Strings are immutable — use += or concatenation

šŸŽÆ Interview Points

  1. "Difference between parameter and argument?"
    → Parameter is the variable name in the function definition. Argument is the actual value passed when calling the function.

  2. "Difference between local and global variables?"
    → Local variables exist only inside the function where they're defined. Global variables are defined outside any function and accessible everywhere — but a function can't modify a global variable without the global keyword.

  3. "What does the global keyword do?"
    → It tells Python that an assignment inside a function should modify the OUTER (global) variable instead of creating a new local one.

  4. "Difference between print() and return inside a function?"
    → print() just displays output to console — the function still returns None. return sends the value back to the caller so it can be stored or reused.

  5. "What happens to code written after a return statement?"
    → It never executes — return immediately exits the function.

  6. "Difference between *args and `kwargs?"**
    →
    args collects extra positional arguments into a tuple. *kwargs` collects extra keyword arguments into a dictionary.

  7. "Difference between filter() and map()?"
    → filter() returns only the elements that satisfy a condition (output may be smaller). map() applies a transformation to every element (output is always the same size).

  8. "Why do you need to import reduce but not filter/map?"
    → filter() and map() are built-in functions. reduce() lives in the functools module and must be explicitly imported.

  9. "What is a lambda function and when would you use it?"
    → An anonymous, single-expression function — used for short, throwaway logic, typically as an argument to filter(), map(), reduce(), or sorted().

  10. "Can a function return multiple values?"
    → Yes — Python packs them into a tuple automatically: return c, d → caller receives (c, d).


šŸ“š Knowledge Base — Quick Revision

# ── FUNCTION BASICS ─────────────────────────────────────────
def func_name(param1, param2):     # definition
    ...
    return value                    # optional

func_name(arg1, arg2)               # call — this is what executes it

# ── SCOPE ───────────────────────────────────────────────────
x = 10                  # global
def f():
    x = 20               # LOCAL — does NOT affect global x
def g():
    global x
    x = 20               # modifies the actual global x

# ── ARGUMENT TYPES ──────────────────────────────────────────
def f(a, b):              pass    # positional
f(a=1, b=2)                       # keyword
def f(a, b=10):            pass   # default (b optional)
def f(*args):               pass  # variable positional → tuple
def f(**kwargs):             pass # variable keyword → dict

# Rule: positional args must come BEFORE keyword args in a call

# ── RETURN VS PRINT ─────────────────────────────────────────
def f():
    return 5      # value usable later: x = f()
def g():
    print(5)       # just displays; g() itself returns None

# ── LAMBDA ──────────────────────────────────────────────────
lambda x, y: x + y                              # basic
lambda x: 'Even' if x % 2 == 0 else 'Odd'        # ternary inside lambda

# ── FILTER / MAP / REDUCE ───────────────────────────────────
list(filter(lambda x: condition, iterable))      # keep matching items
list(map(lambda x: expression, iterable))        # transform every item
from functools import reduce
reduce(lambda x, y: expression, iterable)        # aggregate to ONE value

# ── MODULES ─────────────────────────────────────────────────
import module_name
module_name.function_name()
Enter fullscreen mode Exit fullscreen mode

šŸ‹ļø Practice Questions

Easy

  1. Write a function square(n) that takes a number and returns its square. Call it and print the result.
  2. Write a function greet(name, greeting='Hello') with a default argument. Call it once with just a name, and once overriding the greeting.
  3. Use lambda to write a one-line function that checks if a number is positive, negative, or zero.

Medium

  1. Write a function total(*args) that accepts any number of numbers and returns their sum using reduce().
  2. Demonstrate the difference between local and global scope: create a global variable counter = 0, write a function that tries to increment it without global, show it fails to update, then fix it using the global keyword.
  3. Given cpu_list = [45, 78, 92, 33, 88, 67], use filter() to get servers with CPU > 70, and map() to convert all values to strings with a % symbol (e.g., '45%').

DevOps-Focused

  1. Service Manager Function: Write a function manage_service(action, service_name, region='ap-south-1') that prints a message like "Performing 'restart' on nginx in ap-south-1". Call it 3 times: once with all positional args, once with keyword args in different order, and once overriding the region.
  2. Cost Calculator with reduce(): Given a list of dicts representing AWS services and their monthly costs:
   services = [
       {'name': 'EC2', 'cost': 120.50},
       {'name': 'S3', 'cost': 45.25},
       {'name': 'RDS', 'cost': 300.10},
   ]
Enter fullscreen mode Exit fullscreen mode

Use map() to extract just the costs into a list, then use reduce() (with functools) to calculate the total. Print: "Total AWS monthly cost: $465.85".


Top comments (0)