Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Writing FunctionsLesson 3.1
PSD for Architecture, Planning & Urban Design/Module 3 · Functions, Modules & Files

Lesson 3.1 · Functions, Modules & Files

Writing Functions

Give a useful piece of work a name, and you can reuse it anywhere without copying a single line

13 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

You have written the same window-to-floor ratio three times this project. A function lets you write it once, name it, and never type it again.

Up to now your scripts have run top to bottom - a single list of instructions. That works until you notice you are copying the same few lines around: the same area formula, the same unit conversion, the same little rule. Copy-paste feels quick, but every copy is a place a bug can hide and a change you will forget to make everywhere.

A function is the fix. It takes a useful piece of work, gives it a name, and lets you run it again with different inputs whenever you need. This lesson is the turning point where your code stops being one long script and starts becoming a small kit of named tools you build up over a career - starting with a real design calculation you will package today.

Function = named work + inputs + return. Write the rule once; call it forever.

What a function is, and the shape of one

A function is a named block of instructions you can run - call - as many times as you like. You have already used functions written by other people: print(), len(), round(). Now you write your own. The keyword def (short for define) starts one:

python
def greet_room(name):
    print(f"Designing the {name}")

greet_room("kitchen")
greet_room("bedroom")

Read the shape carefully, because every function follows it. The first line is the header: def, the name you choose, then parentheses holding the inputs. It ends in a colon, and everything indented below it is the body - the work. Defining a function does not run it; the body only executes when you call it by writing its name with parentheses, as the last two lines do.

Naming the work is the whole point. greet_room says what it does far better than three loose print lines would, and now the instruction lives in exactly one place. If you decide the message should change, you edit the body once and every call updates. That is the quiet superpower of functions: one name, one home, one place to change.

It helps to notice how a function changes the reading of a script, not just its writing. A well-named function turns a wall of detail into a summary you can scan: load_rooms(), then filter_large(), then write_schedule() reads almost like a sentence describing the plan, while the fiddly detail hides inside each function until you need it. This is the same instinct you use in a drawing set - a general arrangement plan gives the overview, and detail drawings hold the specifics. Functions let your code work at both altitudes at once: a clear high-level story on top, precise instructions underneath. Names are free; spend them generously and describe what a function does, not how - paint_litres, not calc1.

ANATOMY OF A FUNCTIONdefwindow_ratio(glass, floor):return glass / floordef keyword names itparameters (inputs)the body does the work and returns a valueratio = window_ratio(2.4, 18.0) # now ratio is 0.133
Zoom
The anatomy of a function: def names it, the parentheses hold its parameters (inputs), the indented body does the work and returns a value, and a separate call runs it with real arguments and captures the result. Every function you write is a variation on this shape.

def name(inputs): then an indented body. Nothing runs until you call it.

Parameters, arguments and returning a value

Most useful functions take inputs and hand back a result. The names in the header are parameters - placeholders. The real values you pass when calling are arguments. And the return statement is how a function gives an answer back to whoever called it:

python
def window_ratio(glass_area, floor_area):
    return glass_area / floor_area

living = window_ratio(2.4, 18.0)
print(round(living, 3))   # 0.133

Here glass_area and floor_area are parameters; 2.4 and 18.0 are the arguments. When Python reaches return, it stops the function and sends that value back, so living now holds 0.133. This is different from print, which only shows something on screen - return produces a value you can store, compare or feed into another function.

A function can take no inputs, one, or many, and it can return anything: a number, a string, a list, even a dictionary of results. If you never write return, the function still runs but hands back the special value None. A good rule while learning: if the function computes something, return it; if it only reports to a human, print it. Mixing the two is a classic beginner tangle - keep calculation and display separate and your functions stay reusable.

You can also hand back more than one thing at once. Returning several values - return floor_area, wall_area, perimeter - actually returns a tuple, which the caller can unpack into separate variables in one line: f, w, p = room_metrics(4, 5, 3). That keeps related results together without inventing a separate function for each. And a function can return early: the moment a return runs, the function ends and no later lines execute, which is handy for handling a special case up front (return zero if an area is negative, say) before doing the main work. Between inputs through parameters and answers through return, you now have the full contract of a function - a clear boundary of what goes in and what comes out, which is exactly what lets you trust and reuse it without re-reading the body every time.

Parameters are placeholders; arguments are the real values. return hands the answer back.

Scope: what happens inside stays inside

When a function runs, the names it creates live in their own private space called its local scope. They exist only while the function runs and vanish when it returns. This is a feature, not a nuisance - it means a variable named total inside one function cannot secretly clash with a total somewhere else.

python
def tile_count(area, tile):
    per_tile = area / tile      # local - lives only in here
    return per_tile

print(tile_count(20, 0.36))
print(per_tile)                 # NameError: not defined out here

The last line fails because per_tile never existed outside the function. Names created inside are invisible outside; that isolation is exactly what lets you drop a function into any script without worrying what it will disturb. A function can read a variable from the surrounding (global) scope if it does not have its own by that name, but leaning on that makes functions fragile and hard to move. The clean habit is simple: pass everything a function needs in through its parameters, and send everything it produces back through return. Treat each function as a sealed box with labelled inputs and outputs, and it will behave the same wherever you use it - which is precisely what makes it reusable.

There is a tempting shortcut that scope should steer you away from: reaching out to grab a global variable instead of passing it in. A function that quietly reads a global TILE_SIZE works fine today and then breaks the moment you copy it into another script that has no such variable - or worse, silently uses the wrong one. The failure is invisible because nothing in the function's header hints at the hidden dependency. Passing every input explicitly makes the function honest: its header lists everything it needs, so you can read one line and know exactly what it depends on. Constants that genuinely never change are the mild exception, but as a default, if a function uses a value, let it arrive as a parameter. This discipline is what lets you lift a function out of one project and drop it straight into the next.

DRY: DO NOT REPEAT YOURSELFBEFORE - copied & pasteda = 2.4 / 18.0b = 1.8 / 12.0c = 3.0 / 22.0Change the rule once andyou must fix every copy.AFTER - one functiona = ratio(2.4, 18.0)b = ratio(1.8, 12.0)c = ratio(3.0, 22.0)One place to fix, test andname the rule clearly.A function gives a rule one name, one home, and one place to change.
Zoom
Why functions matter: the same ratio copied three times (left) means three places a bug can hide and a rule change can be missed; one named function called three times (right) gives the rule a single home. DRY - Do not Repeat Yourself - made visible.

Locals are sealed inside. Inputs in through parameters, results out through return.

Default arguments and keyword arguments

Real design tools have sensible defaults you can override. Python lets a parameter carry a default value used when the caller does not supply one:

python
def paint_litres(wall_area, coats=2, coverage=10):
    return wall_area * coats / coverage

print(paint_litres(45))            # uses 2 coats, 10 m2/L -> 9.0
print(paint_litres(45, coats=3))   # override just the coats -> 13.5

Because coats and coverage have defaults, the everyday call is short, but the flexibility is there when a job needs it. Naming an argument in the call - coats=3 - is a keyword argument, and it is worth the few extra characters: it reads clearly and you can skip straight to the one you want without counting positions. One trap to know now: never use a mutable value like a list as a default (def f(items=[])) - that single list is shared across every call and causes baffling bugs. Use None and build the list inside instead.

Defaults are how you encode your practice's house rules while leaving room for exceptions. Two coats of paint, ten percent wastage on tiles, a 300mm setting-out grid - these are the values that are right most of the time, so they belong as defaults, letting the everyday call stay short and the unusual job override just the one parameter it needs. Order matters slightly: parameters with defaults must come after those without, because Python fills positional arguments left to right. And once you have a few defaults, prefer to override them by name in the call - paint_litres(45, coverage=8) is unmistakable, whereas paint_litres(45, 2, 8) forces the reader to remember what the third number means. Clear calls are as much a part of a good function as a clear body.

Defaults keep the common call short; keyword arguments keep the unusual call readable.

Docstrings, DRY, and packaging a design calculation

A function is also the natural home for a short note on what it does. A docstring - a string on the first line of the body - is that note, and tools (and help()) read it:

python
def window_ratio(glass_area, floor_area):
    """Return the window-to-floor ratio (glass area / floor area).

    Both areas in the same units (m2). A common daylight rule of
    thumb asks for at least 0.10 in habitable rooms.
    """
    return glass_area / floor_area

This is the payoff of the whole lesson and the principle behind it: DRY - Do not Repeat Yourself. Every rule your practice uses - a daylight ratio, a paint estimate, a stair-riser check - deserves to live in exactly one named, documented function rather than scattered as copied lines. Build a few of these and you have the beginnings of a personal toolkit: named, tested, reusable moves you trust. When a code or a standard changes, you fix the rule in one place and every script that calls it is instantly correct. That is how a pile of throwaway scripts slowly turns into leverage you keep.

Docstrings repay the small effort in a particular way for designers: they capture the assumptions that would otherwise be lost. Which units? Which rule of thumb, from which standard? What does the function do at the edges - a zero floor area, a negative input? Writing that down in the docstring, at the moment you understand it, saves the future colleague (often you, months later) from reverse-engineering the intent from the arithmetic. Aim for a one-line summary first, then a few lines of detail if the function earns it. You do not need heavy documentation for a fifteen-line script - but a single honest sentence about what a function returns and in what units is almost always worth typing. Between good names, small focused functions and a plain docstring, ordinary scripts start to read like something you could hand to another person - which, eventually, you will.

A docstring says what and why. DRY: one rule, one function, one place to fix.

Terms & tools you'll meet in this lesson

def / function

Naming a reusable block of instructions

The core of the lesson: define once with def, call by name as often as you like.

return

Sending a computed value back to the caller

Produces a value you can store or reuse, unlike print which only displays. No return means the function yields None.

parameter vs argument

Placeholder in the header vs the real value passed in

Parameters name the inputs; arguments are what you actually hand over when you call.

local scope

Names created inside a function stay inside

Isolation that makes a function safe to drop into any script - pass inputs in, return results out.

docstring

A short description as the first line of the body

Documents what a function does and why; help() and editors surface it. Part of writing code others (and future you) can use.

Hands-on workshop

Workshop - turn a design rule into a reusable function

You will take one calculation you do by hand and package it as a clean, documented function with a sensible default - the first tool in your personal kit.

Python 3 (any editor or notebook). No libraries required.

Given & goal
Goal: write and call a reusable design function
Inputs: one calculation you know (paint, tiles, a ratio)
Time: ~30 minutes
  1. 1Pick one rule you compute often. Write it once, inline, with real numbers, and check the answer is right.
  2. 2Wrap it in a function with a clear name and named parameters - for example def paintlitres(wallarea, coats, coverage): - and return the result instead of printing it.
  3. 3Give one parameter a sensible default (coats=2) so the common call is short, then call the function twice: once using the default, once overriding it with a keyword argument.
  4. 4Add a docstring on the first line saying what it returns, the expected units, and any rule-of-thumb it assumes. Run help(your_function) to see it.
  5. 5Prove scope: try to print one of the function's inside variables from outside and watch it fail - then explain in a comment why that failure is a good thing.

You’ll walk away with
A single .py file with one documented function that takes parameters, uses a default, returns a value, and is called at least twice with different inputs - plus a one-line note on why you returned rather than printed.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Your practice runs on repeated rules - encode them as functions. Daylight and window-to-floor ratios, occupancy loads, stair and ramp checks, area take-offs: each is a rule you apply again and again across projects. Wrapped as documented functions, they become a trusted in-house library that any script - or any colleague - can call, and when a code updates you correct it in exactly one place.

For the interior designerScripts for data, schedules & layouts

The estimates you redo on every project are functions waiting to happen. Paint litres, tile and flooring quantities with a wastage allowance, curtain fabric from window sizes, lighting counts per area - package each once with sensible defaults (two coats, ten percent wastage) and you turn a fiddly evening of arithmetic into a one-line call you can trust and reuse job after job.

For the studentA hireable computational skill

Functions are where scripts start to feel like real programming - and studios notice. They are also how you keep an assignment readable: a clear function with a docstring beats a wall of repeated lines every time. Master parameters, return values and scope now; every later part of this course, from pandas to Grasshopper, is built out of functions you write and call.

Misconception check

A function is only worth writing if you are going to use it many times.

Reuse is one reason, not the only one - and this myth keeps people writing long, tangled scripts. Even a function called exactly once earns its keep by naming a step and hiding its detail, so the main flow of your script reads like a summary: loadrooms(), then clean(), then writeschedule(). That readability is worth as much as reuse. Functions also give you a sealed unit you can test on its own and reason about in isolation. Write a function when a chunk of code deserves a name or a boundary, not only when you predict repetition - you will almost always be glad you did.
Try it

Do it yourself

Reason about each before you run it.

  1. 1In your own words, what is the difference between defining a function and calling it?
  2. 2What does return do that print does not?
  3. 3Which are parameters and which are arguments in def area(l, w): ... called as area(3, 4)?
  4. 4Why can't you read a function's local variable from outside it - and why is that useful?
  5. 5Rewrite paint_litres so coverage defaults to 10 and show one call that overrides it.
Take this with you

The one line to carry out

A function gives a useful piece of work a name, a set of inputs (parameters) and an output (return), so you write a rule once and reuse it everywhere - DRY made concrete. Keep functions sealed: inputs in, result out, and a docstring saying what and why.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01SubroutineWikipedia, 2026.
  2. 02The Python TutorialPython Software Foundation, 2026.
  3. 03Software documentationWikipedia, 2026.
  4. 04Modular programmingWikipedia, 2026.
Related lessons
Recap
A function packages instructions under a name you define with def and run by calling. Parameters are input placeholders, arguments the real values, and return hands a result back so you can store and reuse it. Names created inside live in a private local scope, which keeps functions safe to reuse anywhere. Defaults and keyword arguments make calls flexible and readable, and a docstring documents the rule - together turning repeated calculations into a trusted, DRY toolkit.
Carry forward →

Your own functions are one source of reusable code; the other is the enormous body of code other people have already written. Next we learn to import it - the standard library, pip and packages - so you rarely start from scratch.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →