Guide Python Beginner

4.12 Output Statement

Python print() in detail: syntax, formatting, separators, streams, files, JSON, logging, and practical AWS, Kubernetes, Docker, and CI/CD output examples.

7 min read
Python Output

What Is print()?

print() writes values to a text stream, usually the terminal. It accepts zero or more positional values, converts them to text, separates them with sep, and appends end.

print("Hello", "World")

Output:

Hello World

print() is useful for human-readable status messages, command-line tools, quick debugging, and small scripts. For long-running production services, use the logging module so messages have levels, timestamps, and configurable destinations.

Basic Syntax

print(*objects, sep=" ", end="\n", file=None, flush=False)
ArgumentPurposeDefault
*objectsValues to write; any number is allowedNone
sepText placed between valuesOne space
endText written after the final valueNewline (\\n)
fileText stream receiving the outputStandard output (sys.stdout)
flushForce buffered output to be written immediatelyFalse

All arguments after the values are keyword arguments. This is valid:

print("deployment", "complete", sep=" | ", end="!\n")

Output:

deployment | complete!

Printing Values

service = "orders-api"
print(service)

Python converts each value to text for display.

service = "orders-api"
replicas = 3
healthy = True

print(service, replicas, healthy)

Output:

orders-api 3 True

This is usually clearer than manually concatenating strings, and it avoids type errors such as trying to concatenate a string and an integer.

Calling print() with no arguments writes one blank line.

print("Deployment summary")
print()
print("Status: healthy")

Lists and dictionaries are displayed using their normal Python representation.

regions = ["us-east-1", "eu-west-1"]
configuration = {"replicas": 3, "environment": "staging"}

print(regions)
print(configuration)

For machine-readable output, use JSON rather than relying on the Python representation. See Structured JSON Output.

The sep Parameter

sep controls the text inserted between multiple positional values. Its default is one space.

print("api", "staging", "healthy", sep=" | ")

Output:

api | staging | healthy

Useful separators include commas, colons, tabs, and empty text:

print("2026-09-23", "10:30:00", "INFO", sep=" ")
print("name", "status", "latency_ms", sep=",")
print("loading", end="")
print(".", end="")
print(".")

sep is ignored when there is only one object.

The end Parameter

end controls what is written after the final value. The default is a newline.

print("first")
print("second")

Output:

first
second

Set end="" to continue on the same line:

print("Checking", end=" ")
print("database", end=" ")
print("OK")

Output:

Checking database OK

It can also be used for a simple progress display:

for step in range(1, 4):
  print(f"step {step}", end=" ", flush=True)
print("complete")

For a real progress bar, use a library such as tqdm rather than building terminal control behavior manually.

Escape Characters

Escape characters represent special characters inside strings. The complete reference is 4.13 Escape Characters.

print("Line 1\nLine 2")
print("Name\tStatus")
print("She said, \"healthy\"")

Output:

Line 1
Line 2
Name    Status
She said, "healthy"

Use a raw string for text containing many backslashes, such as a Windows path or regular expression:

print(r"C:\\Users\\deploy\\logs")

Formatted Output with f-Strings

f-strings are the preferred way to place expressions inside output text.

service = "payments-api"
status = "healthy"
latency_ms = 42.7

print(f"{service}: {status}, latency={latency_ms:.1f} ms")

Common formatting options:

completion = 0.9375
replicas = 3

print(f"Completion: {completion:.1%}")
print(f"Replicas: {replicas:02d}")
print(f"Cost: ${12.5:,.2f}")

Output:

Completion: 93.8%
Replicas: 03
Cost: $12.50

Expressions can be placed inside braces, but keep complicated logic outside the output statement:

is_healthy = replicas > 0
print(f"Ready: {is_healthy}")

str() and repr() Output

print() uses the readable string form of values. Use repr() when debugging and you need to see quotes, escape characters, or whitespace.

value = "  staging\n"
print(value)
print(repr(value))

Output:

  staging

'  staging\n'

This distinction is useful when diagnosing configuration values that contain unexpected spaces or newlines.

Writing Output to a File or Stream

By default, output goes to sys.stdout. The file parameter sends it to another text stream.

with open("deployment-report.txt", "w", encoding="utf-8") as report:
  print("Deployment completed", file=report)
  print("Environment: staging", file=report)

Use sys.stderr for errors or diagnostic messages that should be separate from normal output:

import sys

print("Deployment started")
print("Warning: replica count is low", file=sys.stderr)

This separation allows a shell or CI system to redirect normal results and errors independently.

Buffering and flush=True

Some streams buffer output before writing it. This can make a long-running script appear stuck, especially when output is sent to a pipe or CI log. flush=True requests immediate flushing.

import time

for item in ["download", "extract", "deploy"]:
  print(f"Starting {item}...", flush=True)
  time.sleep(1)

Use flushing for progress or heartbeat messages, not for every normal log line without a reason.

Structured JSON Output

Human-readable output is useful at a terminal, but automation works better with stable JSON keys.

import json

result = {
  "service": "orders-api",
  "environment": "staging",
  "healthy": True,
  "replicas": 3,
}

print(json.dumps(result))

Pretty-print JSON for people reviewing a report:

print(json.dumps(result, indent=2, sort_keys=True))

Do not print secrets, access tokens, passwords, or complete cloud API responses if they may contain sensitive data.

Use print() for short scripts, simple user feedback, and quick demonstrations. Use logging for production services and automation that needs levels, timestamps, handlers, or filtering.

import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
logging.info("Deployment started")
logging.warning("Rollback threshold is near")

Avoid logging credentials, authorization headers, full environment variables, or sensitive customer data.

Cloud and DevOps Examples

Display an AWS Resource Summary

The SDK response is converted into a small stable summary rather than printing the entire response, which may contain unnecessary details.

import boto3

ec2 = boto3.client("ec2", region_name="us-east-1")
response = ec2.describe_instances(
  Filters=[{"Name": "instance-state-name", "Values": ["running"]}]
)

running_instances = [
  instance
  for reservation in response["Reservations"]
  for instance in reservation["Instances"]
]

print(f"Running instances: {len(running_instances)}")
for instance in running_instances:
  print(instance["InstanceId"], instance.get("InstanceType"), sep=" | ")

In real automation, obtain the region and credentials from the AWS configuration or workload identity rather than hardcoding secrets in output code.

import json

deployment = {
  "application": "orders-api",
  "region": "us-east-1",
  "version": "2026.09.23",
  "status": "success",
}
print(json.dumps(deployment))

This format can be consumed by a CI job, monitoring system, or another script.

Report Kubernetes Pod Status

import subprocess

result = subprocess.run(
  ["kubectl", "get", "pods", "--namespace", "staging", "--no-headers"],
  capture_output=True,
  text=True,
  check=True,
)

for line in result.stdout.splitlines():
  columns = line.split()
  if len(columns) >= 3:
    print(f"{columns[0]} | ready={columns[1]} | status={columns[2]}")

Using an argument list avoids shell parsing problems. Do not pass unchecked user input into shell=True.

Show Docker Image Build Progress

import subprocess

image_tag = "orders-api:2026.09.23"
print(f"Building {image_tag}...", flush=True)
subprocess.run(["docker", "build", "--tag", image_tag, "."], check=True)
print(f"Built {image_tag}")

For detailed command output, let the subprocess inherit the terminal or CI stream instead of capturing and printing it only after the command finishes.

Produce a CI/CD Check Result

import json

checks = {
  "unit_tests": "passed",
  "security_scan": "passed",
  "image_scan": "warning",
}

failed = [name for name, status in checks.items() if status == "failed"]
summary = {"checks": checks, "failed": failed, "success": not failed}
print(json.dumps(summary))

A CI job can parse the JSON and decide whether a pipeline stage should continue. Human-facing status messages can still use f-strings.

from collections import Counter

levels = ["INFO", "ERROR", "INFO", "WARNING", "ERROR"]
counts = Counter(levels)

for level, count in sorted(counts.items()):
  print(f"{level:<8} {count:>4}")

Output:

ERROR       2
INFO        2
WARNING     1

Output Design for Automation

Choose output based on who or what consumes it:

ConsumerGood output choice
Person at a terminalClear f-string messages with useful spacing
Shell pipelineOne stable value per line or a documented delimiter
CI/CD jobJSON or a clearly defined exit status and stream
Monitoring systemStructured logs through logging
Error handlingsys.stderr plus a non-zero process exit code
Large command outputStream it rather than storing the entire result

Output should be stable when another program parses it. Avoid adding decorative text to a machine-readable stream such as JSON.

Quick Interview Answer

“print() writes values to a text stream. It accepts any number of objects and supports sep between objects, end after the final object, file for a destination such as a file or sys.stderr, and flush for immediate output. I use f-strings for human-readable messages, json.dumps() for machine-readable results, and logging instead of scattered print() calls in production services.”

Common Mistakes

  • Forgetting that print() adds a newline by default and then getting unexpected line breaks.
  • Using string concatenation with numbers instead of multiple arguments or an f-string.
  • Printing an entire cloud SDK response, which can produce noisy output or expose sensitive fields.
  • Sending errors to standard output instead of sys.stderr, making shell and CI redirection confusing.
  • Using human-readable labels in output that another program expects to parse as JSON.
  • Omitting flush=True when a long-running progress or heartbeat message must appear immediately.
  • Using print() as the logging system for a production service instead of configuring logging.
  • Logging passwords, tokens, authorization headers, or complete environment variables.

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form