✦ Python Intermediate & OOP Code quality

L2 · Concept 02 · Code Quality & Testing

Docstrings & Clean Code
Write Python Others Can Understand.

Working code is only the start. Clear names, focused functions, and useful documentation help people understand it later.

Learning goalUse clear names, docstrings, and small refactorings to make Python code easier to read and maintain.
01 Learn02 Work locally03 Check your understanding

Learning as a guest

Learning as a guest? Save your progress!

Create a free account to learn with Sloby AI, check your understanding, and track your progress.

The entire platform is free throughout 2026. Foundations stays free forever.

Clean code communicates its intent.

A program can give the right answer and still be difficult to understand. Clean code favors understandable names, consistency, and simple structure instead of a fixed list of absolute rules.

def f(x, y):
    return x - x * y / 100

def cena_sa_popustom(cena: float, popust: float) -> float:
    return cena - cena * popust / 100

Both functions use the same algorithm. The second name tells us what the result means, and its parameters and type hints show what values belong there.

Readable code is not about making every line longer. It is about helping the next reader understand the important idea with less guessing.

↳

A practical question: Could a classmate understand this function tomorrow without first asking you what each short name means?

Docstrings document how code is used
def cena_sa_popustom(cena: float, popust: float) -> float:
    """Izračunava cenu nakon procentualnog popusta."""
    return cena - cena * popust / 100

print(cena_sa_popustom.__doc__)
help(cena_sa_popustom)

A docstring is a string literal at the beginning of a function, class, or module body. It explains purpose and use. Python stores it in __doc__, and help() can display it.

A multi-line docstring
def cena_sa_popustom(cena: float, popust: float) -> float:
    """Izračunava cenu nakon popusta.

    Args:
        cena: Početna cena proizvoda.
        popust: Procenat popusta.

    Returns:
        Cena nakon primenjenog popusta.
    """
    return cena - cena * popust / 100

Args and Returns are common documentation headings, not special Python keywords. A one-line docstring is often enough for a small, obvious function; use more detail when it helps a reader use the code correctly.

A comment is different
# The supplier price changes every Monday.
cena = ucitaj_cenu()

def prikazi_cenu(iznos: float) -> None:
    """Prints a formatted product price."""
    print(f"{iznos:.2f} RSD")

Use a comment to explain a reason or important decision in the source code. Use a docstring to document a function, class, or module. A comment should not merely repeat an obvious Python statement.

Type hints, docstrings, and names work together
def izracunaj_porez(iznos: float, stopa: float) -> float:
    """Vraća iznos poreza za zadatu procentualnu stopu."""
    return iznos * stopa / 100

Type hints describe the values a function expects and returns. A docstring explains what the function does and how to use it. Clear names communicate intent directly in the code.

Five practical habits for readable code

  1. 1. Choose clear namesPrefer ukupna_cena and izracunaj_popust over x and f when the longer names reveal the purpose.
  2. 2. Give each function one understandable responsibilityIf one function reads input, calculates a total, prints a report, and saves a file, consider separating those jobs into small named functions.
  3. 3. Avoid needless repetitionWhen the same calculation appears in several places, a small helper function can give that calculation one name and one place to update.
  4. 4. Simplify conditions when possibleUse direct, readable conditions and avoid deep nesting when an early return or a clearly named boolean would make the decision easier to follow.
  5. 5. Comment the why, not the obvious whatA useful comment explains why a choice matters. broj += 1 usually does not need a comment that only says it adds one.

Refactor without changing the promise

  • Refactoring is not a new featureRefactoring improves the internal structure of code while intentionally preserving its visible behavior and expected results.
  • Check behavior after a changeRun the same examples before and after a refactor. Later lessons will show how tests can make this check more reliable.

Clean Up the Messy Store

Do this in VS Code

Download a small working store program. Improve names, docstrings, type hints, and repetition without changing what the program does.

Download starter project
Messy store starter project
docstrings_clean_code_prodavnica/
├── main.py
├── prodavnica.py
├── README_SR.md
└── README_EN.md

Run the program before changing it, save its deterministic receipt, then compare the result after every small refactoring step.

Refactor the project locally

  1. Open a local folder in VS CodeOpen the downloaded working program in VS Code, read it first, and identify names or repeated code that make it harder to understand.
  2. Keep the behavior the sameThe goal will be clearer code, not a different program. Compare the output before and after each small improvement.
↳

For now: python main.py Run it before and after a refactor to confirm that it still behaves as expected.

A useful refactoring habit

  • Change one small idea at a timeRename or extract one part, then run the program again. Small steps make it easier to notice an unintended behavior change.

What will this code print?

The program does not call pozdrav(). Notice what __doc__ contains.

def pozdrav(ime: str) -> str:
    """Returns a greeting for the given name."""
    return f"Hello, {ime}!"

print(pozdrav.__doc__)
Choose the output

Build confidence independently

Describe a cleaner version

Imagine this working but unclear function. Do not write a complete replacement yet; first explain the improvements you would make.

Your goalSuggest a better function name, clearer parameter names, a useful docstring, and suitable type hints for a function that calculates a discounted price.
def f(x, y):
    return x - x * y / 100

Work in your local VS Code folder

Progressive hints
  • Start by naming what the function returns.
  • Then name what each input represents and what type of value it should hold.
  • Write one sentence that explains the function’s purpose before thinking about the implementation.

Ask Sloby for one guiding question about a name or docstring; do not ask it to replace the whole exercise with a finished solution.

Sloby, the Python AI Tutor mascot

Your learning mentor

Sign in to ask Sloby

Create a free account or sign in to get AI guidance while you learn.

Sign in to check your understanding

Create a free account or sign in to receive AI feedback and save your learning progress.

Python AI Tutor · Learn by understanding, not just by running code.