Python
UTF-8
Encoding
Source Code
Programming

Working with UTF-8 encoding in Python source

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In modern Python, UTF-8 is the default source encoding, so everyday Unicode use is much simpler than it was in older Python versions. The remaining confusion usually comes from mixing three different concerns: the encoding of the source file itself, the encoding of external files, and the boundary between str and bytes.

Source Encoding Versus Runtime Data

Python 3 reads source code as Unicode code points. If your .py file is saved as UTF-8, you can write non-ASCII characters directly in string literals, comments, and even identifiers.

python
message = "café"
city = "Montréal"
print(message, city)

That works because the source file is interpreted as UTF-8 by default. In most projects, you do not need an encoding declaration at the top of the file.

An explicit encoding comment is only needed when the file is stored in a different encoding. For example, a legacy Windows-1252 file might start like this:

python
# -*- coding: cp1252 -*-
price_label = "coût"
print(price_label)

If the file is actually UTF-8, adding that comment is unnecessary noise.

The Important Boundary: str and bytes

Inside Python 3, text should usually live as str. Encoding becomes relevant when text enters or leaves the process.

  • reading from a file decodes bytes into text
  • writing to a file encodes text into bytes
  • network and subprocess APIs often expose raw bytes

That is why a clean mental model matters: UTF-8 is usually a transport or storage detail, not the type you manipulate in application logic.

python
1text = "naïve café"
2raw = text.encode("utf-8")
3round_trip = raw.decode("utf-8")
4
5print(type(text).__name__)
6print(type(raw).__name__)
7print(round_trip)

If you keep text as str until the edges of the program, encoding bugs become easier to isolate.

Reading and Writing Files Safely

Source encoding and file content encoding are separate topics. A UTF-8 Python file can still open a Latin-1 data file incorrectly, and a correctly decoded file can still be processed by a script whose own source is broken.

For external text files, be explicit:

python
1from pathlib import Path
2
3path = Path("greeting.txt")
4path.write_text("olá, 東京\n", encoding="utf-8")
5content = path.read_text(encoding="utf-8")
6print(content)

Being explicit avoids platform-dependent defaults. That matters on teams where the same script runs on macOS, Linux, and Windows.

When an Encoding Declaration Still Matters

In Python 3, the declaration at the top of the source file matters only if the file is not UTF-8, or if you want to document an unusual encoding for a legacy environment. It does not change how open() reads some other file on disk. Developers often overestimate what the header comment controls.

For example, this declaration:

python
# -*- coding: utf-8 -*-

does not mean every file your program opens is UTF-8. It only tells Python how to decode the source file containing that line.

Diagnosing Unicode Errors

Most encoding failures show up as either UnicodeDecodeError or UnicodeEncodeError. The fastest debugging questions are:

  1. What bytes do I actually have?
  2. At which boundary am I decoding or encoding them?
  3. Which encoding does the other system expect?

A small reproducible script often exposes the problem faster than reading a large application stack trace.

python
1bad_bytes = b"caf\xe9"
2try:
3    print(bad_bytes.decode("utf-8"))
4except UnicodeDecodeError as exc:
5    print("decode failed:", exc)
6
7print(bad_bytes.decode("latin-1"))

That example shows the core issue: the same bytes can succeed or fail depending on the codec you apply.

Common Pitfalls

A common mistake is assuming that adding a UTF-8 declaration to the source file fixes decoding problems from external inputs. It does not.

Another pitfall is silently relying on the platform default encoding when opening files. That may work on one machine and fail on another.

The third pitfall is converting back and forth between str and bytes too often. Each unnecessary boundary is another chance to use the wrong codec or lose information.

Summary

  • Python 3 treats source files as UTF-8 by default.
  • Use an encoding declaration only when the source file is not UTF-8 or when legacy tooling requires it.
  • Keep text as str inside the program and encode or decode only at I/O boundaries.
  • Use open(..., encoding="utf-8") or Path.read_text and write_text explicitly for external files.
  • Debug Unicode issues by identifying the exact bytes and the exact codec used at the failure point.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.