Class 11Computer Science · Beyond the syllabusFull chapter

Functions

The whole chapter in one place — read it, then test yourself. Clear notes, a reference sheet, a practice quiz, and worked NCERT exercise solutions.

Not in the exam syllabus. Moved to Class 12 — the CBSE 2025-26 Class 11 syllabus examines only built-in methods and modules. It is here because it is in your textbook — read it for interest, but do not spend revision time on it.

Why Functions Exist, and How to Write One

Quick answer User-defined functions are not in the CBSE Class 11 exam syllabus for 2025-26 — they are a Class 12 topic — but the def statement, the function body and the difference between defining and calling are worth learning early because they remove copy-paste from your programs.

Read this before you spend time here. Chapter 7 of your NCERT Class 11 book teaches user-defined functions. User-defined functions are not part of the CBSE Class 11 Computer Science (083) exam syllabus for 2025-26. They moved to Class 12, where Unit 1 opens with "Revision of Python topics covered in Class XI" and then teaches Functions. So nothing on this page can be asked in your Class 11 annual examination or school unit tests. (Class 11 has no board paper at all — the board exam comes at the end of Class 12.)

Two honest conclusions follow. First, if Unit 2 (Computational Thinking and Programming-1) is not yet solid — lists, tuples, dictionaries, strings, loops, the built-in methods — go and revise that first, because that is where your marks are. Second, once Unit 2 is solid, this chapter is a genuine head start: every single thing on this page is examinable next year, and students who arrive in Class 12 already comfortable with def find the whole year easier.

You have been using functions since day one. Every time you wrote len(marks) or max(marks) you called a function. Someone wrote that code once, and you reuse it by name.

marks = [78, 91, 65, 88]
print(len(marks))
print(max(marks))
print(sum(marks))
print(round(sum(marks) / len(marks), 2))

Output:

4
91
322
80.5

A user-defined function is the same idea, except you write the reusable block yourself.

The problem functions solve. Suppose a shop bills three orders, each with 18% GST:

amount = 250 * 3
print("Payable: Rs", round(amount + amount * 0.18, 2))

amount = 1499 * 2
print("Payable: Rs", round(amount + amount * 0.18, 2))

amount = 80 * 12
print("Payable: Rs", round(amount + amount * 0.18, 2))

Output:

Payable: Rs 885.0
Payable: Rs 3537.64
Payable: Rs 1132.8

The logic is written three times. If GST changes to 12%, you must edit three places and you will miss one. That is the real cost of copy-paste: not typing effort, but the bugs that appear when one copy is updated and the others are not.

Writing your first function. The header is def name(parameters): — the keyword def, a name that follows the same rules as a variable name, brackets, and a compulsory colon. Everything indented under the header is the function body.

def greet():
    print("Namaste, welcome to Class 11 CS")

greet()
greet()

Output:

Namaste, welcome to Class 11 CS
Namaste, welcome to Class 11 CS

Notice that greet without brackets would not print anything — the brackets are what actually run the function. greet is the function; greet() is the act of running it.

WORKED EXAMPLE — the GST bill as a function. The first line inside the body is a docstring: a string in triple quotes that says what the function does. Python stores it, and you can read it back later.

def bill_total(price, qty):
    """Print the amount payable including 18% GST."""
    amount = price * qty
    gst = amount * 0.18
    print("Item amount : Rs", amount)
    print("GST @18%    : Rs", round(gst, 2))
    print("Payable     : Rs", round(amount + gst, 2))

print("--- Order 1 ---")
bill_total(250, 3)
print("--- Order 2 ---")
bill_total(1499, 2)

Real output:

--- Order 1 ---
Item amount : Rs 750
GST @18%    : Rs 135.0
Payable     : Rs 885.0
--- Order 2 ---
Item amount : Rs 2998
GST @18%    : Rs 539.64
Payable     : Rs 3537.64

Now the GST rate lives in exactly one line. Change 0.18 once and every bill in the program is correct.

Flow of execution. This trips up almost everyone. The def block does not run when Python reads it — it only creates the function and moves on. The body runs only when you call it.

print("Line 1: before the def")

def show():
    print("Line 3: inside the function")

print("Line 2: after the def, before the call")
show()
print("Line 4: after the call")

Output:

Line 1: before the def
Line 2: after the def, before the call
Line 3: inside the function
Line 4: after the call

Because the function is created only when the def line executes, calling it earlier fails. This program:

show()

def show():
    print("Hello")

ends with (last line of the traceback):

NameError: name 'show' is not defined

A function body can never be empty. If you want to write the header now and the logic later, put pass in the body:

def not_written_yet():
    pass

print(not_written_yet())
print(type(not_written_yet))

Output:

None

Two useful facts fall out of that output. A function that does not send anything back gives you None, and a function is itself a kind of object with a type, just like an int or a list.

def statement def function_name(parameters): Header line. Colon compulsory, body indented (4 spaces is the convention).
Function call function_name(arguments) The brackets are what run it. The bare name refers to the function object, it does not execute it.
Docstring """One line saying what the function does.""" First statement inside the body. Read it back with function_name.__doc__
Empty body placeholder pass A function body must contain at least one statement; pass is the do-nothing statement.
Definition order rule the def must execute before the call executes Calling above the def raises NameError: name 'f' is not defined.
Function is an object type(f) is class 'function' f is the function, f() is the result of running it. Confusing the two is the most common beginner bug.
Remember
  • User-defined functions are NOT in the CBSE Class 11 2025-26 exam syllabus — they are Class 12 Unit 1. Revise Unit 2 first; treat this page as a head start, not exam prep.
  • The syntax is def name(parameters): followed by an indented body. The colon is compulsory and the body can never be empty — use pass as a placeholder.
  • Defining a function does not run it. Python creates the function when the def line executes; the body runs only when you write name().
  • A function must be defined before the line that calls it runs, otherwise you get NameError: name '...' is not defined.
  • A docstring — a triple-quoted string as the first statement in the body — documents the function and is stored in name.__doc__.

Parameters, Arguments and Default Values

Quick answer Parameters are the names in the def header, arguments are the values supplied at the call; Python matches them by position or by keyword, and a parameter given a default value becomes optional — but every parameter with a default must come after every parameter without one.

A function that always prints the same thing is not very useful. Parameters let you hand data in.

Get the vocabulary right, because CBSE asks for the difference directly — in the Class 12 paper, not in Class 11, where none of this is examined. In def fare(km, rate): the names km and rate are parameters — they appear in the definition. In the call fare(400, 1.25) the values 400 and 1.25 are arguments — they appear at the call. Parameters are empty boxes; arguments are what you put in them.

Positional arguments. By default Python matches arguments to parameters left to right, by position. Nothing checks that they make sense:

def student_row(name, marks):
    print(name, "scored", marks)

student_row("Ananya", 92)
student_row(92, "Ananya")

Output:

Ananya scored 92
92 scored Ananya

The second call is nonsense but Python runs it happily. This is why order matters and why a wrong-order call is such a nasty bug — there is no error message, just a wrong answer.

Keyword arguments. You can name the parameter at the call. Then order stops mattering:

def student_row(name, marks):
    print(name, "scored", marks)

student_row(marks=92, name="Ananya")
student_row("Rahul", marks=77)

Output:

Ananya scored 92
Rahul scored 77

You may mix the two, but all positional arguments must come before all keyword arguments. Writing func(a=3, 7) stops the program before it starts:

SyntaxError: positional argument follows keyword argument

WORKED EXAMPLE — default parameters, IRCTC style. Give a parameter a value in the header and it becomes optional. Here the standard sleeper rate is Rs 1.25 per km and AC costs 50% more:

def fare(km, rate=1.25, ac=False):
    cost = km * rate
    if ac:
        cost = cost * 1.5
    print("Distance", km, "km -> Rs", round(cost, 2))

fare(400)
fare(400, 2.0)
fare(400, ac=True)
fare(400, 2.0, True)

Real output:

Distance 400 km -> Rs 500.0
Distance 400 km -> Rs 800.0
Distance 400 km -> Rs 750.0
Distance 400 km -> Rs 1200.0

Read the third call carefully. To change ac while leaving rate alone, you must name it — fare(400, True) would have set rate = True, which is 1, and quietly given a wrong fare. Defaults plus keyword arguments together are what make a function comfortable to call.

The ordering rule for defaults. Every parameter with a default must come after every parameter without one. Otherwise Python cannot decide what a lone positional argument means. This definition never even runs:

def fare(rate=1.25, km):
    print(km * rate)
SyntaxError: parameter without a default follows parameter with a default

Note that this is a SyntaxError, not a runtime error — the whole file is rejected, even the parts that are fine.

The classic NCERT trace. Three parameters, two with defaults, three different calling styles:

def func(a, b=5, c=10):
    print('a is', a, 'and b is', b, 'and c is', c)

func(3, 7)
func(25, c=24)
func(c=50, a=100)

Real output:

a is 3 and b is 7 and c is 10
a is 25 and b is 5 and c is 24
a is 100 and b is 5 and c is 50

Trace it: in call 1, 3 and 7 fill a and b by position, c keeps its default. In call 2, 25 fills a, c is named, b keeps its default. In call 3, both are named, order is irrelevant, and b again keeps its default.

Wrong number of arguments. Python counts. Too few:

def fare(km, rate):
    print(km * rate)

fare(400)
TypeError: fare() missing 1 required positional argument: 'rate'

Too many:

fare(400, 1.25, True)
TypeError: fare() takes 2 positional arguments but 3 were given

Both are TypeError and both happen at the call, not at the definition — so a badly-called function can sit undetected in your code until that branch actually runs.

Parameter vs argument def f(km, rate): ... f(400, 1.25) km and rate are parameters (definition). 400 and 1.25 are arguments (call).
Positional argument f(400, 1.25) Matched by position, left to right. Wrong order runs silently and gives a wrong result.
Keyword argument f(rate=1.25, km=400) Matched by name, so order is free. Use it when a call has several similar-looking values.
Default parameter def f(km, rate=1.25): rate becomes optional; the default is used only when no argument is supplied for it.
Default ordering rule non-default parameters first, defaulted ones last def f(rate=1.25, km) gives SyntaxError: parameter without a default follows parameter with a default.
Mixing at the call positional arguments first, then keyword arguments f(a=3, 7) gives SyntaxError: positional argument follows keyword argument.
Remember
  • Parameters are the names in the def header; arguments are the values passed at the call. CBSE asks this distinction as a straight one-mark question in Class 12 — it is not examinable in Class 11.
  • Positional arguments are matched left to right — a wrong order usually gives no error, just a wrong answer.
  • Keyword arguments (name=value) are matched by name, so order does not matter; positional arguments must all come before keyword ones.
  • A parameter with a default value is optional. All defaulted parameters must be written after all non-defaulted ones, or you get SyntaxError: parameter without a default follows parameter with a default.
  • Passing too few or too many arguments raises TypeError at the moment of the call, never at the definition.

return: Sending a Value Back

Quick answer return hands a value back to the caller and ends the function immediately; a function with no return gives None, and returning several values packs them into a tuple that you can unpack in one line.

So far the functions have printed. Printing puts characters on the screen and then the value is gone — you cannot add it to something, store it in a list, or compare it. return sends the value back to the line that called the function, where you can do anything you like with it.

WORKED EXAMPLE — print versus return. This one example fixes the single most common confusion in the whole chapter:

def area_print(side):
    print(side * side)

def area_return(side):
    return side * side

area_print(5)
a = area_return(5)
print(a + 100)

b = area_print(5)
print(b)

Real output:

25
125
25
None

Line by line: area_print(5) prints 25 and gives nothing back. area_return(5) prints nothing but hands 25 back, so a + 100 is 125. The last two lines are the trap — b = area_print(5) prints 25 as a side effect, but the value stored in b is None, because a function with no return returns None. If you had written b * 2 you would have got a TypeError.

Rule of thumb: a function that computes something should return; a function whose entire job is to display something may print. Do not mix the two jobs in one function.

return ends the function at once. Nothing after it in that path ever runs:

def test(n):
    print("start")
    if n > 0:
        return "positive"
        print("this line never runs")
    print("end")
    return "not positive"

print(test(5))
print(test(-5))

Output:

start
positive
start
end
not positive

The string this line never runs is absent from the output, which proves the point.

Early return makes grading code clean. Because return exits immediately, you do not need elif chains:

def grade(marks):
    if marks >= 90:
        return "A1"
    if marks >= 80:
        return "A2"
    if marks >= 70:
        return "B1"
    if marks >= 33:
        return "Pass"
    return "Needs Improvement"

for m in [95, 82, 71, 40, 12]:
    print(m, "->", grade(m))

Output:

95 -> A1
82 -> A2
71 -> B1
40 -> Pass
12 -> Needs Improvement

Once marks >= 90 is true, the function is over — so the later tests can be written as if the earlier ones already failed.

Returning more than one value. Python lets you list several values after return. It packs them into a tuple:

def divide(a, b):
    q = a // b
    r = a % b
    return q, r

result = divide(47, 5)
print(result)
print(type(result))

quotient, remainder = divide(47, 5)
print("Quotient:", quotient, "Remainder:", remainder)

Output:

(9, 2)

Quotient: 9 Remainder: 2

Nothing new is happening — this is exactly the tuple packing and unpacking you already learnt in Unit 2. return q, r builds the tuple (9, 2), and quotient, remainder = ... unpacks it.

A bare return — the word alone, no value — ends the function and gives back None. It is used to bail out of a bad case.

Returned values compose. Because a call is an expression, you can use it anywhere a value fits — inside arithmetic, inside another call, inside a condition:

import math

def hypotenuse(a, b):
    return math.sqrt(a * a + b * b)

def perimeter(a, b):
    return a + b + hypotenuse(a, b)

print(round(hypotenuse(3, 4), 2))
print(round(perimeter(3, 4), 2))
print(round(hypotenuse(5, 12) + hypotenuse(8, 15), 2))

Output:

5.0
12.0
30.0

Check the last line by hand: 5-12-13 and 8-15-17 are Pythagorean triples, so 13 + 17 = 30. That is what composition buys you — perimeter did not have to know any geometry, it just used hypotenuse.

return statement return expression Sends the value to the caller AND terminates the function on the spot.
Missing return function with no return gives None print(f()) shows None. Printing inside a function is not the same as returning.
Bare return return Ends the function and returns None. Used to exit early from an invalid case.
Multiple return values return a, b then x, y = f() Python packs a, b into a tuple; the assignment unpacks it. type() of the result is tuple.
Using a returned value total = f(x) + 10 Only works if f returns. If f merely prints, total becomes None and the addition raises TypeError.
Early exit if condition: return value Any return that executes stops the function; the remaining body is skipped entirely.
Remember
  • return sends a value back to the caller; print only displays it. A value you print cannot be reused, a value you return can.
  • A function with no return statement returns None — so b = print_only_function() stores None, and using b in arithmetic raises TypeError.
  • return ends the function immediately; statements after it on that path never execute. This makes early-exit grading and validation code clean.
  • return a, b packs the values into a tuple, which you unpack with x, y = f(). It is ordinary tuple packing, not a special feature.
  • A function call is an expression, so returned values can be used directly inside arithmetic, conditions or other calls.

Scope of Variables: Local and Global

Quick answer A name assigned inside a function is local and disappears when the function ends; a global can be read inside a function without ceremony, but assigning to it creates a separate local unless you declare global first.

Scope means: from which parts of the program is this name visible? Python has two you need here — local (inside one function call) and global (at the top level of the file).

A variable assigned inside a function is local. It is created when the function starts and destroyed when it ends:

def compute():
    total = 100
    print("Inside :", total)

compute()
print("Outside:", total)

Output:

Inside : 100

then the program stops with (last line of the traceback):

NameError: name 'total' is not defined

This is a feature, not a nuisance. It means you can use i, total or temp inside a function without worrying whether some other part of a 500-line program already uses those names.

A global variable can be read inside a function with no special keyword:

gst_rate = 0.18

def show_rate():
    print("Rate used inside function:", gst_rate)

show_rate()
print("Rate outside:", gst_rate)

Output:

Rate used inside function: 0.18
Rate outside: 0.18

Python looks for gst_rate locally first, does not find it, then looks globally and finds it.

WORKED EXAMPLE — the shadowing surprise. Now watch what happens when you assign to a name that also exists globally:

count = 10

def change():
    count = 99
    print("Inside :", count)

change()
print("Outside:", count)

Real output:

Inside : 99
Outside: 10

The global count is untouched. The assignment did not modify the global — it created a brand-new local variable that happens to have the same name. The local hides, or shadows, the global for the duration of the call. This is deliberate: a function should not be able to wreck the rest of your program just because it reused a common name.

The global keyword is how you say "I really do mean the outer one":

count = 10

def change():
    global count
    count = 99
    print("Inside :", count)

print("Before :", count)
change()
print("After  :", count)

Output:

Before : 10
Inside : 99
After  : 99

Use it sparingly. A function that quietly rewrites globals is hard to test and hard to debug, because reading its call site tells you nothing about what it changed. Returning a value is almost always the better design.

The half-and-half error. This one confuses everybody the first time. Reading a global is fine, and assigning creates a local — so what happens if you do both?

count = 10

def bump():
    print(count)
    count = count + 1

bump()

Output (last line of the traceback):

UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Python scans the whole function body before running it. It sees count = ... somewhere in the body, and decides then and there that count is a local name for this entire function. So the earlier print(count) is asking for a local that has not been given a value yet. The fix is one line: add global count at the top of the function, or better, pass count in and return the new value.

Parameters are local too. A parameter is just a local variable that starts out holding the argument:

def double(n):
    n = n * 2
    print("Inside :", n)

n = 5
double(n)
print("Outside:", n)

Output:

Inside : 10
Outside: 5

The two ns are different variables that share a name. Reassigning the parameter never touches the caller's variable.

Summary table.

What you do inside the functionEffect on the global
Only read the nameReads the global value
Assign to the nameCreates a separate local; global unchanged
Read, then assign, no globalUnboundLocalError
Declare global x, then assignGlobal is changed
Local variable any name assigned inside a function body Created at call, destroyed at return. Using it outside raises NameError: name '...' is not defined.
Global variable any name assigned at the top level of the file Readable inside any function with no keyword at all.
Shadowing x = 5 inside a function that has a global x Makes a fresh local x. The global keeps its old value after the call ends.
global declaration global x Write it before any use of x in that function. Then assignment inside updates the global.
UnboundLocalError read x, then assign x, with no global x Python marks x local for the whole function, so the earlier read has nothing to read.
Parameters are local def double(n): n = n * 2 n is a fresh local holding a copy of the reference. Reassigning it cannot change the caller's variable.
Remember
  • A name first assigned inside a function is local: it lives only during that call, and using it outside raises NameError.
  • A global variable can be READ inside a function without any keyword, because Python searches local names first and then global ones.
  • Assigning to a name inside a function creates a NEW local even if a global of the same name exists — this is shadowing, and it protects the rest of your program.
  • Reading a global and then assigning to it in the same function, without declaring global, raises UnboundLocalError, because Python fixes the name as local before running the body.
  • The global keyword makes assignment affect the outer variable, but returning a value is almost always the cleaner design.

Functions in Practice: Lists, Layers and Common Mistakes

Quick answer Passing a list into a function is different from passing an integer — the function can change the list in place — and once you know that, several small functions can be layered into a complete marksheet program that is far easier to debug than one long block.

Scope explains what happens to names. This section explains what happens to objects, which is where the last real surprise lives.

WORKED EXAMPLE — lists change, integers do not. The same-looking code behaves differently for a list and an int:

def add_bonus(marks_list, bonus):
    marks_list.append(bonus)
    print("Inside :", marks_list)

def add_number(x, bonus):
    x = x + bonus
    print("Inside :", x)

scores = [78, 91, 65]
add_bonus(scores, 5)
print("Outside:", scores)

total = 234
add_number(total, 5)
print("Outside:", total)

Real output:

Inside : [78, 91, 65, 5]
Outside: [78, 91, 65, 5]
Inside : 239
Outside: 234

The list changed outside the function. The integer did not. The reason is the mutable / immutable distinction from Unit 2: the parameter always receives a reference to the same object the caller has. For a list you can modify that shared object with append, sort, remove or lst[0] = ..., and the caller sees it. An int, str, float or tuple cannot be modified at all, so x = x + bonus can only build a new object and point the local name at it.

Rebinding versus mutating. The distinction is not "list versus int", it is "changing the object versus changing the name":

def replace(lst):
    lst = [1, 2, 3]
    print("Inside :", lst)

def modify(lst):
    lst[0] = 999
    print("Inside :", lst)

a = [10, 20, 30]
replace(a)
print("Outside:", a)

b = [10, 20, 30]
modify(b)
print("Outside:", b)

Output:

Inside : [1, 2, 3]
Outside: [10, 20, 30]
Inside : [999, 20, 30]
Outside: [999, 20, 30]

Both parameters are lists. replace pointed its local name at a different list and the caller's list was untouched. modify changed the shared list itself and the caller saw it. Remember it as: assignment moves the label, methods change the object.

WORKED EXAMPLE — a complete marksheet program. Three small functions, each doing one job, plus the statistics module you already know:

import statistics

def percentage(marks):
    """Return the average of a list of marks, rounded to 2 places."""
    return round(sum(marks) / len(marks), 2)

def grade(pct):
    """Return the CBSE-style grade for a percentage."""
    if pct >= 90:
        return "A1"
    elif pct >= 80:
        return "A2"
    elif pct >= 70:
        return "B1"
    elif pct >= 33:
        return "Pass"
    else:
        return "ER"

def report(name, marks):
    pct = percentage(marks)
    print(name.ljust(8), str(pct).rjust(6), grade(pct))

names = ["Ananya", "Rahul", "Fatima", "Vikram"]
sheet = [[92, 88, 95, 90, 91],
         [67, 72, 58, 80, 74],
         [45, 38, 51, 40, 47],
         [30, 25, 41, 28, 33]]

print("Name        Pct  Grade")
all_pct = []
for i in range(len(names)):
    report(names[i], sheet[i])
    all_pct.append(percentage(sheet[i]))

print("Class mean   :", round(statistics.mean(all_pct), 2))
print("Class median :", statistics.median(all_pct))
print("Topper       :", names[all_pct.index(max(all_pct))], max(all_pct))

Real output:

Name        Pct  Grade
Ananya     91.2 A1
Rahul      70.2 B1
Fatima     44.2 Pass
Vikram     31.4 ER
Class mean   : 59.25
Class median : 57.2
Topper       : Ananya 91.2

Look at what the layering bought. report calls percentage and grade but contains no arithmetic and no grade boundaries of its own. If the school changes the A1 cut-off to 91, you edit one line in grade and nothing else. If the average is wrong, there is exactly one function to check. That is the actual reason professional code is written in functions — not to save typing, but to shrink the area you have to search when something breaks.

The docstring is retrievable, which is how help() works for built-in functions too:

def percentage(marks):
    """Return the average of a list of marks, rounded to 2 places."""
    return round(sum(marks) / len(marks), 2)

print(percentage.__doc__)
print(type(percentage))

Output:

Return the average of a list of marks, rounded to 2 places.

Mistakes worth memorising now.

MistakeWhat Python says
Calling before the def has runNameError: name 'f' is not defined
Forgetting the colon after the headerSyntaxError: expected ':'
Empty function bodyIndentationError — use pass
Default parameter written before a non-default oneSyntaxError: parameter without a default follows parameter with a default
Wrong number of argumentsTypeError: f() missing 1 required positional argument
Using the result of a print-only functionSilently None, then a TypeError later
Assigning to a global without declaring itA new local is created and the global is left unchanged — or UnboundLocalError if the function reads the name before assigning it
Naming your function sum, len, max or listNo error — it just hides the built-in for the rest of the program

What Class 12 adds on top of this. Everything above is the foundation. Next year you will also meet functions that call themselves (recursion), functions used with files and databases, and the __name__ == '__main__' idiom. None of that makes sense without def, arguments, return and scope — so if the five sections on this page are comfortable, you are genuinely ahead.

Immutable argument int, float, str, bool, tuple The function cannot change the caller's value. Reassigning inside has no effect outside.
Mutable argument list, dictionary, set Any in-place change made inside the function is seen by the caller: lst.append(x), lst.sort(), lst[i] = v, del lst[i], d[k] = v, s.add(x). Note that sort and indexing belong to lists, not to sets.
Rebinding vs mutating lst = [1,2,3] vs lst[0] = 1 The first renames the local only; the second edits the shared object.
Function calling function def report(...): pct = percentage(...) Perfectly normal. Both definitions must have executed before the outer call runs.
Docstring retrieval f.__doc__ Returns the triple-quoted first statement of the body, or None if there is no docstring.
Shadowing a built-in def sum(a, b): ... No error is raised, but the real sum() is now unreachable in that program. Avoid built-in names.
Remember
  • A parameter receives a reference to the caller's object, so mutating a list, dictionary or set inside a function changes it for the caller too.
  • Reassigning a parameter (lst = [1,2,3]) only moves the local name; mutating (lst[0] = 999, lst.append(x)) changes the shared object. Assignment moves the label, methods change the object.
  • Immutable arguments — int, float, str, tuple — can never be changed by the function, so the caller's variable is always safe.
  • Splitting a program into small single-job functions shrinks the area you must search when a result is wrong; a changed rule is edited in exactly one place.
  • Never name a function after a built-in (sum, len, max, list, str) — Python gives no warning and the built-in stops working for the rest of the program.

The formula sheet

Every formula in this chapter, in one place — screenshot it before your exam.

def function_name(parameters):
def statement
function_name(arguments)
Function call
"""One line saying what the function does."""
Docstring
pass
Empty body placeholder
the def must execute before the call executes
Definition order rule
type(f) is class 'function'
Function is an object
def f(km, rate): ... f(400, 1.25)
Parameter vs argument
f(400, 1.25)
Positional argument
f(rate=1.25, km=400)
Keyword argument
def f(km, rate=1.25):
Default parameter
non-default parameters first, defaulted ones last
Default ordering rule
positional arguments first, then keyword arguments
Mixing at the call
return expression
return statement
function with no return gives None
Missing return
return
Bare return
return a, b then x, y = f()
Multiple return values
total = f(x) + 10
Using a returned value
if condition: return value
Early exit
any name assigned inside a function body
Local variable
any name assigned at the top level of the file
Global variable
x = 5 inside a function that has a global x
Shadowing
global x
global declaration
read x, then assign x, with no global x
UnboundLocalError
def double(n): n = n * 2
Parameters are local
int, float, str, bool, tuple
Immutable argument
list, dictionary, set
Mutable argument
lst = [1,2,3] vs lst[0] = 1
Rebinding vs mutating
def report(...): pct = percentage(...)
Function calling function
f.__doc__
Docstring retrieval
def sum(a, b): ...
Shadowing a built-in

Test yourself

Tap an answer to check it instantly — you'll see why it's right, and what to revise if it isn't.

0 correct · 0/12 answered
Q1

What is the output of this program? def f(x, y=3): return x ** y print(f(2), f(2, 4), f(y=2, x=3))

Q2

What is the output of this program? def check(n): if n % 2 == 0: return "Even" print("Odd") print(check(4)) print(check(7))

Q3

What is the output of this program? def total(a, b=2, c=3): return a + b + c print(total(1), total(1, 2), total(c=1, a=2))

Q4

What is the output of this program? n = 3 def calc(): global n n = n * 2 return n print(calc(), n, calc(), n)

Q5

What is the output of this program? data = [1, 2, 3] def push(lst): lst.append(4) lst = [9, 9] return lst print(push(data), data)

Q6

What is the output of this program? def swap(a, b): return b, a x, y = swap(10, 20) print(x, y, swap(1, 2))

Q7

What is the output of this program? def show(msg, times=2): for i in range(times): print(msg, end="") print() show("Hi") show("Ok", 3) show(times=1, msg="Go")

Q8

What happens when Python runs a file containing only this definition? def fare(rate=1.25, km): return km * rate

Q9

What is the result of running this program? def fare(km, rate): print(km * rate) fare(400)

Q10

What is the result of running this program? def compute(): total = 100 compute() print(total)

Q11

What is the result of running this program? count = 10 def bump(): print(count) count = count + 1 bump()

Q12

For the definition def fare(km, rate=1.25): and the call fare(400, 2.0), which statement is correct?

NCERT solutions & previous-year questions

Step-by-step model answers — tap a question to reveal the full solution.

NCERT questions 6

1 What is a function? Why are functions used in a program? Write the general syntax of a user-defined function in Python.Introduction to functions

What a function is. A function is a named block of statements that performs one well-defined task. You write it once and run it as many times as you like by calling its name. Python has three kinds: built-in functions such as len(), max(), sum() and round() that come ready-made; functions defined in modules, such as math.sqrt() or statistics.mean(), which become usable once you import the module; and user-defined functions that you write yourself with the def keyword.

Why we use them.

  1. No repetition. The logic is written in one place, so a rule change is edited once instead of in five copies.
  2. Fewer bugs. When output is wrong, only one small function has to be checked instead of a 200-line program.
  3. Readability. grade(pct) tells a reader what is happening; ten lines of if/elif do not.
  4. Reuse. A tested function can be used again in another part of the program, or in another program.

Syntax.

def function_name(parameter1, parameter2):
    """optional docstring describing the function"""
    statement(s)
    return value        # optional

The keyword is def; the name follows identifier rules; the brackets hold zero or more parameters; the colon is compulsory; the body is indented under the header. Calling it is function_name(argument1, argument2).

Worked demonstration:

def bill_total(price, qty):
    """Print the amount payable including 18% GST."""
    amount = price * qty
    gst = amount * 0.18
    print("Item amount : Rs", amount)
    print("GST @18%    : Rs", round(gst, 2))
    print("Payable     : Rs", round(amount + gst, 2))

bill_total(250, 3)

Output:

Item amount : Rs 750
GST @18%    : Rs 135.0
Payable     : Rs 885.0
2 Differentiate between the following, giving one suitable example of each: (a) a parameter and an argument, (b) a local variable and a global variable.Parameters, arguments and scope

(a) Parameter vs argument

ParameterArgument
A name written inside the brackets of the def header.A value written inside the brackets at the function call.
It is a variable — an empty box waiting to be filled.It is the actual data put into that box.
Exists only while the function runs.Exists in the calling code before the call is made.
May be given a default value.May be passed positionally or by keyword.
def interest(principal, rate, years):
    return principal * rate * years / 100

p = 50000
r = 7.5
t = 3
print("Simple interest = Rs", interest(p, r, t))
print("Amount          = Rs", p + interest(p, r, t))

Output:

Simple interest = Rs 11250.0
Amount          = Rs 61250.0

Here principal, rate and years are parameters; the values of p, r and t — 50000, 7.5 and 3 — are the arguments.

(b) Local vs global variable

Local variableGlobal variable
Assigned inside a function.Assigned at the top level of the program.
Visible only inside that function.Visible everywhere, including inside functions (for reading).
Created at the call, destroyed at the return.Lives as long as the program runs.
To change a global from inside, you must declare global first.Assigning to its name inside a function makes a new local instead.
school = "Priodemy Public School"

def enrol(name):
    roll = 21
    print(name, "joined", school, "with roll no", roll)

enrol("Meera")
print(school)
print(roll)

Output:

Meera joined Priodemy Public School with roll no 21
Priodemy Public School

and then the program stops with:

NameError: name 'roll' is not defined

school is global — readable inside enrol and outside it. roll is local — it ceases to exist the moment enrol finishes, so the last print fails.

3 What will be the output of the following program? def func(a, b=5, c=10): print('a is', a, 'and b is', b, 'and c is', c) func(3, 7) func(25, c=24) func(c=50, a=100)Default and keyword parameters

Output:

a is 3 and b is 7 and c is 10
a is 25 and b is 5 and c is 24
a is 100 and b is 5 and c is 50

Working, call by call.

  1. func(3, 7) — both arguments are positional. 3 goes to a, 7 goes to b. Nothing is supplied for c, so its default 10 is used. Result: a = 3, b = 7, c = 10.
  2. func(25, c=24) — 25 is positional and fills the first parameter, a. c is named explicitly as 24. b was never mentioned, so it keeps its default 5. Result: a = 25, b = 5, c = 24.
  3. func(c=50, a=100) — both are keyword arguments, so the order in which they are written is irrelevant; Python matches by name. b again keeps its default 5. Result: a = 100, b = 5, c = 50.

The rule to carry away: a default value is used only when no argument reaches that parameter, whether positionally or by keyword. Also note that a has no default, so every one of the three calls had to supply it — func() alone would raise TypeError: func() missing 1 required positional argument: 'a'.

4 Observe the following code fragments carefully and identify the error in each. Also give the corrected code. (i) def display() print("Hello") (ii) def area(radius=1, pi): return pi * radius * radius (iii) def sum(a, b): return a + b sum(10)Errors in function definition and call

(i) Missing colon after the function header. Every def header must end with a colon. Python 3.13 reports:

SyntaxError: expected ':'

Correction: def display():

(ii) A non-default parameter written after a default parameter. Because radius has a default and pi does not, Python could not decide what a single positional argument should fill. It reports:

SyntaxError: parameter without a default follows parameter with a default

Correction: put the non-default parameter first — def area(pi, radius=1):

(iii) Wrong number of arguments at the call. The function needs two arguments; only one is supplied. This is not a syntax error — the definition is perfectly legal — so the failure happens when the call runs:

TypeError: sum() missing 1 required positional argument: 'b'

Correction: call it as sum(10, 0), or give b a default with def sum(a, b=0):. Separately, sum is a bad name because it hides the built-in sum() for the rest of the program — rename it add.

All three corrected and run:

def add(a, b):
    return a + b

def area(pi, radius=1):
    return pi * radius * radius

def display():
    print("Hello")

print(add(10, 0))
print(area(3.14, 5))
display()

Output:

10
78.5
Hello
5 Write a program using a user-defined function to compute the sum of the series 1 + 1/2 + 1/3 + ... + 1/n, where n is passed as an argument.Writing functions with return

Approach. Keep a running total starting at 0, loop i from 1 to n, and add 1/i each time. Use return, not print, so the caller can round the answer, store it or add it to something else.

Two details matter. range(1, n + 1) is needed because range stops one short of its second argument. And 1 / i must use the true-division operator /, not // — with // every term after the first would become 0.

Program:

def series_sum(n):
    total = 0
    for i in range(1, n + 1):
        total = total + 1 / i
    return total

for n in [1, 5, 10]:
    print("n =", n, "-> sum =", round(series_sum(n), 4))

Output:

n = 1 -> sum = 1.0
n = 5 -> sum = 2.2833
n = 10 -> sum = 2.929

Check by hand for n = 5: 1 + 0.5 + 0.3333 + 0.25 + 0.2 = 2.2833. Matches.

Dry run for n = 3:

i1/itotal after adding
11.01.0
20.51.5
30.3333...1.8333...

The loop then ends and return total hands 1.8333... back.

6 Write a user-defined function myfact(n) that returns the factorial of n. Using it, write a second function to compute nCr = n! / (r! x (n-r)!), and test it.return values and reusing functions

Approach. Factorial is a running product, so start f at 1 (not 0 — anything multiplied by 0 stays 0) and multiply by every integer from 1 to n. For n = 0 the loop body never runs and f stays 1, which is correct since 0! = 1.

Then ncr simply calls myfact three times. This is the point of the question: a function that already works becomes a building block, and ncr contains no factorial logic of its own. Use // for the division because nCr is always a whole number and / would give a float like 10.0.

Program:

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

for n in [0, 1, 5, 10]:
    print("factorial(", n, ") =", myfact(n))

def ncr(n, r):
    return myfact(n) // (myfact(r) * myfact(n - r))

print("5C2 =", ncr(5, 2))
print("10C3 =", ncr(10, 3))

Output:

factorial( 0 ) = 1
factorial( 1 ) = 1
factorial( 5 ) = 120
factorial( 10 ) = 3628800
5C2 = 10
10C3 = 120

Verification by hand. 5C2 = 120 / (2 x 6) = 120 / 12 = 10. 10C3 = 3628800 / (6 x 5040) = 3628800 / 30240 = 120. Both match the program output.

Note on why return was essential. If myfact had printed instead of returning, ncr could not have used the value at all — it would have received None three times and crashed with a TypeError. A function that computes should return.

Part of Priodemy for School

Interactive CBSE lessons, Class 8–12 — free with every school on Priodemy EduSuite. Explore more chapters and labs on the Priodemy for School hub.

Ask AI