Thursday, October 8, 2026

PYTHON | SEE WHAT A PROGRAM REALLY IS — How Source Code, the Python Runtime, Data, Functions, Modules, and the Operating System Become Working Software



A Python file can look almost disappointingly

simple.

name = "Ada"
print("Hello", name)

There are only two lines. Yet when you run them, something much larger happens.

Your operating system starts or reuses a process. A Python runtime loads your program. The program creates data in memory. Python interprets what the instructions mean. The operating system provides access to the screen, keyboard, files, network, and other resources. Eventually, the instruction to print something becomes visible output.

The interesting question, therefore, is not:

“What does this Python code mean?”

That is only the beginning.

A more useful question is:

What has to happen between writing Python code and getting a result from a computer?

Once you understand that journey, programming begins to look less like memorizing commands and more like learning how to construct systems.

That distinction matters because beginners often learn Python from the wrong direction. They collect syntax:

if
for
while
def
import
class

They can reproduce examples, but the computer remains mysterious.

A stronger foundation begins by understanding the relationship between code, runtime, data, operating system, and machine.


The program is not the file

Suppose you create a file called:

hello.py

and put this inside:

print("Hello")

It is tempting to think that hello.py is the program.

It is better to think of it as a description of a computation.

The file contains instructions written in a language humans can work with. Those instructions have to be processed by software capable of understanding Python before the computer can produce the behavior you requested.

This gives us our first important distinction:

Source code is not the same thing as a running program.

The source code is persistent.

The running program is an active computation.

You can save the source code today and run it tomorrow. When you run it, the computer creates a particular execution of that code.

That execution has state.

It has memory.

It consumes processor time.

It may open files.

It may communicate over a network.

It may create other resources.

It may encounter errors.

And when it finishes, the particular execution disappears even though the source code remains on disk.

This distinction becomes increasingly important as programs grow.

A web application, for example, may have thousands or millions of lines of source code but only certain parts of that code are executing at a particular moment.


Python sits between your ideas and the operating system

When you write:

print("Hello")

you are not directly instructing the CPU:

“Move these characters to this particular hardware device.”

You are expressing your intention through Python.

Python provides a programming language and an execution environment that lets you work at a much higher level of abstraction.

You can think about:

  • strings,
  • numbers,
  • lists,
  • files,
  • functions,
  • objects,
  • network requests,
  • data structures,

without having to manually manage every processor instruction involved in implementing those operations.

But Python cannot escape the underlying computer.

Eventually, software needs resources.

It needs memory.

It needs CPU time.

It may need a file system.

It may need network access.

It may need an operating-system process.

So there is a useful mental model:

Your idea
   ↓
Python source code
   ↓
Python runtime
   ↓
Operating system
   ↓
Computer hardware

The arrows do not mean that every operation literally travels through these layers in exactly this simple sequence. Modern systems are considerably more complicated.

The model is useful because it gives you somewhere to place each concept.

When something goes wrong, you can begin asking:

Which layer is responsible?


A line of Python is an instruction with meaning

Consider:

age = 44

A beginner might simply memorize that this creates a variable.

But what is actually happening?

Python needs to represent the number 44.

It needs a name, age, that refers to that value.

The runtime needs to maintain this relationship as the program executes.

So conceptually, you can think of the statement as establishing:

age ───────► 44

The name is not necessarily a physical box in memory containing the number.

It is better understood as a reference within Python's execution model.

This becomes especially important when several names refer to related objects.

For example:

a = [10, 20, 30]
b = a

Now consider:

b.append(40)

What should happen to a?

If you understand variables as simple physical boxes, the result may seem surprising.

If you understand that names can refer to objects, the result becomes much easier to reason about.

Both names can refer to the same list object:

a ─────┐
       ├────► [10, 20, 30]
b ─────┘

After the append:

a ─────┐
       ├────► [10, 20, 30, 40]
b ─────┘

This is one reason programming becomes easier when you understand the underlying model instead of merely memorizing syntax.


Data is what gives a program something to work on

A program without data has very little to manipulate.

Consider a simple calculator.

The instructions describe what to do:

total = price * quantity

But the computation depends on values.

Perhaps:

price = 2500
quantity = 3

Now the program has something to operate on.

The distinction between instructions and data runs throughout computing.

Instructions tell the system what operations to perform.

Data supplies the information those operations act upon.

Python gives you structures for representing that information.

A string:

customer = "Ada"

A number:

quantity = 3

A list:

products = ["Laptop", "Mouse", "Keyboard"]

A dictionary:

product = {
    "name": "Laptop",
    "price": 350000,
    "available": True
}

The structures become more interesting when programs begin transforming them.

That is where programming starts becoming computation rather than merely storage.


Computation is transformation

Imagine a small online store.

The system receives:

Product price = ₦50,000
Quantity = 2

The program transforms those values:

50,000 × 2
      ↓
100,000

Then perhaps it applies delivery:

100,000 + 5,000
      ↓
105,000

Then perhaps it applies a discount:

105,000 − 10,000
      ↓
95,000

The program is performing a sequence of transformations.

This gives us another powerful mental model:

A program takes a state, applies rules, and produces a new state or result.

At a very simple level:

INPUT
  ↓
PROCESSING
  ↓
OUTPUT

But real programs are usually more accurately represented as:

State
  ↓
Instruction
  ↓
New state
  ↓
Another instruction
  ↓
Another state
  ↓
...
  ↓
Result

Once you see programs this way, variables, conditions, loops, functions, databases, APIs, and user interfaces begin to fit into the same picture.


Why conditions exist

Suppose a shopping application needs to decide whether a customer receives free delivery.

The rule might be:

If the order is at least ₦100,000, delivery is free.

Python can express that decision:

if total >= 100000:
    delivery = 0
else:
    delivery = 5000

The important concept is not the if keyword.

The important concept is branching.

The program has reached a point where the next operation depends on the current state.

                total >= 100000?
                       │
              ┌────────┴────────┐
             YES                NO
              │                  │
        delivery = 0       delivery = 5000

Almost every useful software system contains decisions like this.

Authentication systems decide whether credentials are valid.

Banking systems decide whether a transaction can proceed.

E-commerce systems decide whether stock is available.

Search systems decide which results to return.

Games decide what happens after an action.

The syntax changes.

The underlying idea does not.


Why loops exist

Now imagine there are 10,000 products.

You could write:

print(products[0])
print(products[1])
print(products[2])

That obviously does not scale.

Instead:

for product in products:
    print(product)

The loop expresses a relationship:

Perform this operation for each item in this collection.

Again, the important concept is not the word for.

It is repetition controlled by a rule.

Large systems contain enormous amounts of repetition.

A server may process thousands of requests.

A data pipeline may process millions of records.

An email system may examine thousands of messages.

A search engine may evaluate enormous numbers of documents.

The computer is extremely good at repeating precise operations.

Programming languages give humans ways to describe those repetitions without writing every individual operation.


Functions change the scale of your thinking

Eventually, you discover a problem.

If you keep writing the same sequence of instructions, your program becomes difficult to maintain.

Suppose many parts of a system need to calculate a total.

You could repeat:

subtotal = price * quantity
total = subtotal + delivery

everywhere.

Or you can give the operation a name:

def calculate_total(price, quantity, delivery):
    subtotal = price * quantity
    return subtotal + delivery

Now another part of the program can say:

total = calculate_total(50000, 2, 5000)

A function is therefore more than a syntax feature.

It allows you to package a piece of reasoning.

That is a major step in programming.

You move from thinking:

“What instructions do I execute next?”

toward:

“What capability does this system need?”

Then you can give that capability a name.

For example:

calculate_total()
validate_customer()
send_email()
create_invoice()
resize_image()
fetch_products()

The names become a vocabulary for the system.

Good software is partly the construction of a useful vocabulary.


A function creates a boundary

Consider:

def calculate_total(price, quantity, delivery):
    subtotal = price * quantity
    return subtotal + delivery

Inside the function, certain details exist.

Outside it, another part of the program may only need to know:

calculate_total(...)

This is an important idea called abstraction.

Abstraction does not mean hiding reality because reality is unimportant.

It means hiding unnecessary detail so that a larger system can be understood at the level that matters.

You do not need to know how every transistor in your CPU works before you can write:

total = calculate_total(...)

And the person using your shopping application does not need to know that your calculation is implemented in Python.

Each layer provides an abstraction for the layer above it.


Modules allow a program to become a system

A small Python file can contain everything.

That works until the program grows.

Imagine one file containing:

  • customer registration,
  • authentication,
  • payments,
  • invoices,
  • product management,
  • email,
  • reports,
  • database operations,
  • administration.

Eventually, the file becomes difficult to understand.

Python therefore provides mechanisms for separating code into modules.

You might have:

shop/
    customers.py
    payments.py
    products.py
    invoices.py
    reports.py

One module can use functionality from another.

For example:

from payments import process_payment

Now the system has structure.

This is another transition in programming:

instructions
   ↓
functions
   ↓
modules
   ↓
applications
   ↓
systems

The complexity has not disappeared.

It has been organized.

That distinction is crucial.

Good software architecture does not eliminate complexity.

It gives complexity somewhere sensible to live.


The Python runtime is part of the story

When you type:

python program.py

something has to interpret what you wrote.

The exact implementation depends on the Python environment, but the common Python implementation most people encounter is CPython.

The runtime reads your Python program and executes it according to Python's language rules.

There are several layers involved in that process.

Very roughly:

program.py
    ↓
Python implementation
    ↓
Python execution machinery
    ↓
machine-level operations
    ↓
operating system
    ↓
hardware

The reality is more sophisticated than this diagram.

Python implementations can use parsing, compilation to an intermediate representation such as bytecode, virtual-machine execution, optimization mechanisms, and interactions with native libraries.

The important lesson is not to memorize the internal pipeline.

It is to stop imagining that:

Python source code = CPU instructions

They are different things.

Python gives you a higher-level language.

The Python implementation handles the work necessary to execute that language.


The operating system enters the picture

Suppose your Python program wants to read a file:

with open("customers.txt") as file:
    data = file.read()

Python provides the programming interface.

But Python does not own the physical storage device.

The operating system manages access to files and hardware resources.

Your program asks for a file.

The runtime and libraries interact with the operating system.

The operating system determines how that request is handled.

Eventually, the storage hardware performs the underlying operation.

The same pattern appears with networking.

You might write:

import requests

and then make an HTTP request.

You are expressing a high-level networking operation.

Underneath that operation are layers involving:

  • Python libraries,
  • sockets,
  • operating-system networking,
  • network interfaces,
  • TCP or another transport mechanism,
  • IP,
  • routers,
  • remote servers,
  • and the Internet.

This is why programming cannot be understood completely in isolation.

A program lives inside a computing system.


Your Python program becomes a process

When the operating system starts your program, it creates an execution environment for it.

One of the most important concepts here is the process.

A process is not simply the source-code file.

It is a running instance of a program with resources and execution state managed by the operating system.

That process may have:

  • memory,
  • threads,
  • open files,
  • environment information,
  • permissions,
  • network connections,
  • and other operating-system resources.

This connects programming directly to computer science.

Earlier, you may have thought:

I wrote a Python program.

Now you can think:

I wrote source code.

The Python runtime provides a way to execute it.

The operating system creates and manages the running process.

The process uses hardware resources to perform computation.

That is a much stronger mental model.


Memory is where the program's working state lives

Consider:

customer = "Ada"
balance = 50000

During execution, the program needs somewhere to keep these values.

That working state is represented in memory.

When you later write:

balance = balance + 10000

Python needs to retrieve the current value, perform a computation, and establish the resulting state.

Memory therefore becomes part of the program's execution environment.

This also explains why a program can behave differently from a file sitting on disk.

The file may contain:

balance = 50000

but after execution, the running program has an evolving state.

It may have:

balance = 60000

because the program changed the state during execution.

When the process terminates, that particular working state disappears unless the program deliberately saves it somewhere persistent.

That is why databases and files exist.


Persistent data changes the nature of an application

Imagine a banking application.

If a customer's balance existed only in RAM, shutting down the application could destroy the information.

That would obviously be unacceptable.

So the application needs persistent storage.

Conceptually:

User action
    ↓
Application
    ↓
Change in program state
    ↓
Database
    ↓
Persistent record

When the application starts again, it can retrieve the information.

This creates another distinction:

memory is primarily working state; storage provides persistence.

Real systems are more complicated because they use caches, databases, files, queues, replication, backups, and other layers.

But the basic problem remains:

How do we preserve information beyond the lifetime of a particular execution?

That question eventually leads into databases, distributed systems, cloud infrastructure, and application architecture.


Errors reveal the boundaries of the system

A program does not always behave as expected.

Consider:

number = int("hello")

Python cannot convert "hello" into an integer.

An exception occurs.

This is useful because the error tells you something about the program's assumptions.

Perhaps the program expected a number.

Perhaps the user entered text.

Perhaps an external API returned unexpected data.

Perhaps a database contained an invalid record.

Programming therefore involves more than constructing the successful path.

You must understand what happens when reality disagrees with your assumptions.

This is why errors should not be viewed merely as interruptions.

They are evidence.

A useful debugging question is:

What assumption did the program make that turned out not to be true?

That question is often more productive than simply asking:

“Why is my code broken?”


Debugging is investigation

Suppose this program produces the wrong result:

price = 100
quantity = 5
discount = 20

total = price * quantity - discount

The computer may execute it perfectly.

There may be no syntax error.

Yet the business rule might actually mean:

Apply a 20% discount.

The program has therefore exposed a deeper problem.

The code can be technically correct while the model is wrong.

The correct calculation might be:

subtotal = price * quantity
discount_amount = subtotal * 0.20
total = subtotal - discount_amount

This distinction is extremely important.

Software failures can come from different places:

Syntax problem
     ↓
Runtime problem
     ↓
Logic problem
     ↓
Data problem
     ↓
Environment problem
     ↓
Requirement problem

A programmer who only knows syntax can fix only a small part of this landscape.

A capable developer learns to investigate the entire chain.


The terminal changes your relationship with Python

Graphical interfaces are convenient, but the command line exposes the machinery more directly.

You can create a file:

program.py

and run:

python program.py

You can inspect your current directory.

You can create virtual environments.

You can install packages.

You can run tests.

You can inspect errors.

You can automate repeated operations.

The terminal therefore becomes more than a place where programmers type commands.

It becomes an interface to the development environment.

This is one reason programming eventually connects to software development tools such as:

  • editors,
  • IDEs,
  • version control,
  • package managers,
  • virtual environments,
  • testing frameworks,
  • debuggers,
  • command-line utilities,
  • deployment systems.

Python itself is only one part of the development environment.


Libraries extend what the language can do

Python's built-in capabilities are useful, but developers rarely build serious applications entirely from scratch.

They use libraries.

For example:

import math

gives access to mathematical functionality.

Other libraries can help with:

  • HTTP communication,
  • data analysis,
  • image processing,
  • databases,
  • machine learning,
  • web applications,
  • automation,
  • testing,
  • scientific computing.

This creates an important distinction.

A programming language gives you a way to express computation.

A library gives you reusable capabilities.

A framework may go further by providing a structure within which an application is expected to operate.

A package is a distribution mechanism for reusable Python code, although the exact terminology becomes more nuanced in real Python packaging.

These distinctions matter because software development is largely built on reuse.

You do not need to reinvent every capability.

You need to understand what already exists, determine whether it is appropriate, and integrate it correctly.


Reuse introduces dependencies

Suppose your application depends on a library.

Now your system is no longer only:

Your code

It is:

Your code
   ↓
Library
   ↓
Other libraries
   ↓
Python runtime
   ↓
Operating system
   ↓
Hardware

The dependency chain can become much larger.

That creates both power and risk.

Reuse lets you build sophisticated software quickly.

But dependencies can introduce:

  • compatibility problems,
  • security vulnerabilities,
  • breaking changes,
  • licensing considerations,
  • maintenance obligations,
  • version conflicts.

This is why professional programming eventually becomes partly about managing relationships between pieces of software.

The ability to write a function is useful.

The ability to build and maintain a system of dependent components is much more powerful.


Python can reach beyond the computer

Consider an application that retrieves weather information from an online service.

The Python program might send an HTTP request:

Python application
      ↓
HTTP request
      ↓
Internet
      ↓
Remote API
      ↓
Response
      ↓
Python application

The program is no longer operating only on local data.

It has become part of a networked system.

The same pattern can connect Python to:

  • payment services,
  • email providers,
  • cloud platforms,
  • databases,
  • artificial intelligence APIs,
  • social platforms,
  • business systems,
  • IoT devices.

This is where programming becomes especially powerful.

You are no longer merely writing software that runs on your computer.

You are building a component inside a larger digital ecosystem.


A useful investigation

You can make these ideas concrete without building a large application.

Create:

name = "Ada"
numbers = [10, 20, 30]

total = sum(numbers)

print(name)
print(total)

Now do not merely ask whether it works.

Investigate it.

Ask:

Where is the source code?

It exists as a file on storage.

What happens when you run it?

A Python execution environment is started or invoked.

Where does name exist while the program runs?

It becomes part of the program's runtime state.

Where does numbers exist?

The list is represented as an object in the running program's memory.

What does sum(numbers) do?

It performs a computation over the values.

Where does print() send its output?

In a normal terminal execution, the output is directed to standard output, which the surrounding environment normally displays in the terminal.

Now change:

numbers = [10, 20, 30]

to:

numbers = [10, 20, "30"]

What happens?

You have deliberately violated an assumption about the data.

The resulting behavior gives you evidence about what the operation expects.

This is programming as investigation.


Change one thing at a time

A useful way to learn Python is to stop treating examples as finished answers.

Take a working program and modify one element.

Start with:

price = 100
quantity = 3

total = price * quantity

print(total)

Change the quantity.

Then change the price.

Then replace the number with a string.

Then add a condition.

Then put the calculation inside a function.

Then put several products into a list.

Then calculate all of them.

Each change should force a question:

What changed in the system?

This is much more valuable than copying ten unrelated examples.

You are gradually developing a predictive model.

The goal of learning programming is not to remember what happened in an example.

It is to become able to predict what will happen when you change the system.


From code to capability

At the beginning, Python may appear to be a collection of commands.

Then you discover variables.

Then data structures.

Then control flow.

Then functions.

Then modules.

Then libraries.

Then files and databases.

Then networks and APIs.

Then applications.

Eventually, the picture becomes:

Python
  ↓
Computational capability
  ↓
Reusable software components
  ↓
Applications
  ↓
Connected systems
  ↓
Services
  ↓
Digital products

The language is not the final capability.

It is one of the instruments through which capability is constructed.

A programmer who knows Python syntax but cannot model a problem has limited power.

A programmer who can take a real problem, represent its data, define its rules, design its components, test its assumptions, connect its dependencies, and turn the result into a reliable system has a much broader capability.

That is the direction in which programming education should move.


The computer is still underneath everything

It is easy, after working with Python for a while, to forget that the language is sitting on top of a physical machine.

Your program eventually requires computation.

Computation requires hardware.

The processor executes machine-level operations.

Memory holds working state.

Storage preserves information.

The operating system manages resources.

The Python runtime provides the environment in which Python code can execute.

Libraries provide reusable capabilities.

Your application organizes those capabilities around a problem.

The layers form a stack:

REAL-WORLD PROBLEM
        ↓
APPLICATION
        ↓
PROGRAM LOGIC
        ↓
PYTHON
        ↓
PYTHON RUNTIME / LIBRARIES
        ↓
OPERATING SYSTEM
        ↓
COMPUTER HARDWARE

The programmer normally works at the upper layers.

But the lower layers remain relevant.

When a program is slow, memory becomes important.

When a file cannot be accessed, operating-system permissions become important.

When a network request fails, networking becomes important.

When a dependency breaks, software architecture becomes important.

When computation becomes expensive, algorithms and hardware become important.

Programming is therefore not an isolated skill.

It is a way of controlling computation inside a larger computing system.


The first mental model to keep

If you remember only one picture from this chapter, make it this:

                 REAL-WORLD PROBLEM
                         ↓
                    DATA + RULES
                         ↓
                    PYTHON CODE
                         ↓
              FUNCTIONS + MODULES
                         ↓
             PYTHON RUNTIME + LIBRARIES
                         ↓
                  OPERATING SYSTEM
                         ↓
                  COMPUTER RESOURCES
                         ↓
                       RESULT

The important thing is not the exact number of layers.

It is the relationship between them.

Your Python program is not floating independently in the computer.

It is part of a chain.

The source code describes computation.

The runtime provides an environment for executing that computation.

The operating system provides and manages resources.

The hardware performs the underlying work.

And the result eventually becomes something meaningful to a person or another system.

Once you can see that chain, Python stops looking like a bag of commands.

It becomes a way of expressing computational systems.

And that is where the deeper study of programming begins. 


Image: Dev Community 

No comments:

Post a Comment