Home Cheat Sheets MicroPython vs Python 3 Differences Cheat Sheet

MicroPython vs Python 3 Differences Cheat Sheet

by shedboy71

MicroPython vs Python 3 Differences

Compatibility Overview

MicroPython implements a substantial subset of Python 3 for microcontrollers and resource-constrained systems. Most everyday Python syntax is familiar, but library availability, memory management, hardware access and some language behaviours differ from standard CPython.

CPython is the reference implementation of Python, commonly used on Windows, Linux and macOS. MicroPython is a separate implementation with its own runtime, modules and platform-specific features.

Feature CPython 3 MicroPython
Target environment Desktop, server, embedded Linux Microcontrollers and constrained systems
Core syntax Full Python 3 implementation Substantial Python 3 subset
Standard library Extensive Reduced or adapted
Memory management Reference counting and cyclic GC in CPython Garbage-collected managed heap
Hardware GPIO Requires platform-specific libraries machine.Pin
Hardware I2C/SPI/UART External or platform libraries machine interfaces
Multithreading threading Limited _thread support on selected ports
Async programming asyncio Lightweight asyncio implementation
Package installation pip mip, mpremote or manual installation
Native extensions CPython extension interface Different native integration mechanisms
Filesystem Host operating system Board-dependent storage
Memory capacity Usually comparatively large Often limited to kilobytes or megabytes
Startup Script, module or application entry point Commonly boot.py and main.py

Compatibility depends on the MicroPython version, port and firmware build. A feature available on an ESP32 build may not be available on an RP2040, RP2350 or STM32 build.

Python Syntax Compatibility

Most common Python constructs work with little or no modification.

value = 25name = "Sensor"if value > 20:    print(name, "active")for index in range(5):    print(index)def square(number):    return number * number

Commonly supported features

Feature MicroPython support
Variables and assignments Yes
if, elif, else Yes
for and while Yes
Functions Yes
Default arguments Yes
Classes and inheritance Yes
List comprehensions Yes
Generators and yield Yes
Exceptions Yes
Context managers Yes
Decorators Yes, with implementation limitations
async and await Supported by modern builds
Type annotations Limited support; not full CPython compatibility
Structural pattern matching Do not assume support
Metaclasses and advanced introspection Reduced or implementation-dependent

MicroPython does not implement every feature introduced in recent CPython releases. Code using newer syntax should be checked against the exact MicroPython firmware version.

Standard Library Differences

One of the most common migration problems is importing a module that exists in CPython but is absent or reduced in MicroPython.

Common module comparison

CPython module MicroPython equivalent or status
os os, reduced functionality
sys sys, reduced functionality
time time, embedded timing extensions
json json, reduced functionality
math math, supported subset
struct struct, supported subset
socket socket, networking-dependent
select select, platform-dependent
asyncio Lightweight MicroPython implementation
threading No equivalent full implementation
subprocess Generally unavailable
multiprocessing Generally unavailable
pathlib Not normally included
sqlite3 Not normally included
ctypes Not supported as in CPython
logging Not normally included as a full standard module
machine MicroPython-specific
network MicroPython-specific

Older MicroPython releases and some compatibility libraries use names such as ujson, uos and utime. Modern MicroPython code generally uses the standard names where supported.

Check module availability

import sysprint(sys.platform)print(sys.version)help("modules")

This is more reliable than assuming every firmware build includes the same modules.

Data Types and Numeric Behaviour

Both implementations support integers, floating-point numbers, strings, bytes, lists, tuples and dictionaries.

The internal representations and limits may differ.

Integers

CPython integers have arbitrary precision, subject to available memory.

MicroPython supports small integers efficiently and can support larger integers through long-integer implementations. The available integer range depends on the firmware build and configuration.

number = 123456result = number * 1000

Do not assume that every MicroPython build supports arbitrarily large integer calculations.

Floating-point numbers

CPython normally uses IEEE 754 double-precision floating-point values.

MicroPython floating-point precision depends on its build configuration and architecture. Some builds use single precision; others use double precision.

value = 0.1 + 0.2print(value)

Floating-point calculations may produce different rounding results between CPython and MicroPython.

For sensor calibration, numerical filtering and accumulated calculations, determine the precision supported by the target firmware.

Numeric comparison

Characteristic CPython MicroPython
Integer precision Arbitrary precision Build-dependent
Float precision Normally 64-bit Commonly 32-bit or 64-bit
Complex numbers Supported Not universally supported
Decimal arithmetic decimal module Usually unavailable
Large integer operations Memory-limited More constrained
Numeric formatting Extensive Some format features may differ

Memory Management

CPython normally uses reference counting combined with cyclic garbage collection.

MicroPython uses a garbage-collected heap for dynamically allocated objects.

MicroPython memory inspection

import gcgc.collect()print("Free:", gc.mem_free())print("Allocated:", gc.mem_alloc())

The gc.mem_free() and gc.mem_alloc() functions are MicroPython-specific.

They report managed heap information rather than the total physical RAM available on the microcontroller.

Memory allocation differences

Operation CPython MicroPython
Create list Allocates memory Allocates constrained heap memory
String concatenation Allocates new objects Can rapidly consume limited heap
Garbage collection Mostly automatic Automatic, with manual control available
Inspect free heap External tools or runtime facilities gc.mem_free()
Preallocate buffers Useful optimisation Often important for reliability
Memory fragmentation Possible Particularly significant on small heaps

Avoid repeated allocations

Less suitable for a memory-constrained loop:

while True:    data = bytearray(1024)    process(data)

Here, process() represents an application-defined function.

A better arrangement is to allocate the buffer once:

buffer = bytearray(1024)while True:    process(buffer)

The second version avoids repeatedly allocating the same buffer.

Where supported, use readinto() methods to fill existing buffers instead of creating new bytes objects.

Hardware Access

CPython running on a desktop computer does not provide a universal GPIO, ADC, PWM or microcontroller peripheral interface.

MicroPython exposes embedded hardware through modules such as machine.

MicroPython GPIO

from machine import Pinled = Pin(15, Pin.OUT)led.on()

The selected pin must be valid for the target board.

Equivalent CPython code on a Raspberry Pi running Linux would use a separate GPIO library, with different APIs and pin-numbering conventions.

Hardware module comparison

Function CPython MicroPython
GPIO Platform-specific library machine.Pin
ADC External hardware/library machine.ADC
PWM Platform-specific library machine.PWM
I2C Platform-specific library machine.I2C
SPI Platform-specific library machine.SPI
UART Serial library or OS interface machine.UART
Hardware timer Platform-specific interface machine.Timer
Deep sleep OS or platform-specific machine.deepsleep() where supported

The machine module is not available in ordinary CPython installations.

Time and Delay Functions

Both environments support delays through the time module.

import timetime.sleep(1)

MicroPython additionally provides embedded-oriented timing functions.

import timetime.sleep_ms(100)time.sleep_us(50)start = time.ticks_ms()

Timing API comparison

Operation CPython MicroPython
Delay in seconds time.sleep() time.sleep()
Delay in milliseconds time.sleep(0.1) time.sleep_ms(100)
Delay in microseconds time.sleep(0.00005) time.sleep_us(50)
Monotonic elapsed time time.monotonic() time.ticks_ms() / ticks_us()
High-resolution timing time.perf_counter() time.ticks_us() / ticks_cpu() where supported
Tick wraparound handling Usually unnecessary for monotonic clock time.ticks_diff()

Correct elapsed-time calculation

import timestart = time.ticks_ms()# Application workelapsed = time.ticks_diff(time.ticks_ms(), start)print(elapsed)

MicroPython tick counters wrap around. ticks_diff() handles this for intervals within its supported comparison range.

Do not directly subtract MicroPython tick values when wraparound is possible.

Neither CPython nor MicroPython guarantees precise real-time execution merely because a delay function accepts microsecond values.

Filesystem Differences

CPython normally accesses files through the host operating system.

MicroPython often uses internal flash storage, external flash, SD cards or another board-supported filesystem.

Portable basic file access

with open("settings.txt", "w") as file:    file.write("interval=1000\n")
with open("settings.txt", "r") as file:    print(file.read())

Filesystem comparison

Feature CPython MicroPython
open() Supported Supported where filesystem exists
os.listdir() Supported Commonly supported
os.mkdir() Supported Commonly supported
os.stat() Supported Commonly supported
pathlib.Path Standard library Usually unavailable
File permissions OS-dependent Often limited or absent
Symbolic links Filesystem-dependent Usually unsupported
Storage capacity Host-dependent Frequently limited
Write endurance Depends on storage Important for onboard flash

MicroPython applications should avoid unnecessary flash writes, especially when logging high-frequency sensor measurements.

Threads and Concurrency

CPython provides the threading module and other concurrency mechanisms.

MicroPython offers limited _thread support on selected ports, but its behaviour and available features differ from CPython.

Comparison

Feature CPython MicroPython
threading.Thread Supported Not normally available
_thread.start_new_thread() Supported Port-dependent
threading.Lock Supported Use _thread facilities where available
multiprocessing Supported on suitable systems Generally unavailable
asyncio Full standard-library implementation Reduced embedded implementation
Hardware interrupt callbacks Not a standard desktop Python feature Supported by selected peripherals

MicroPython asyncio

import asyncioasync def sensor_task():    while True:        print("Sensor task")        await asyncio.sleep_ms(1000)async def main():    await sensor_task()asyncio.run(main())

MicroPython asyncio uses cooperative scheduling. Tasks must yield execution using await for other tasks to run.

A blocking hardware operation or long computation can delay the entire event loop.

Interrupt Handling

MicroPython supports hardware interrupt callbacks on selected peripherals.

CPython does not provide an equivalent general-purpose microcontroller interrupt interface.

MicroPython GPIO interrupt

from machine import Pinevent_count = 0def on_edge(pin):    global event_count    event_count += 1input_pin = Pin(14, Pin.IN, Pin.PULL_UP)input_pin.irq(    trigger=Pin.IRQ_FALLING,    handler=on_edge)

This assumes GPIO14 supports the selected interrupt mode.

The callback increments a counter but does not debounce a mechanical switch.

Interrupt restrictions

MicroPython hard interrupt handlers must avoid operations that allocate heap memory.

Common restrictions include:

  • Avoid allocating lists, dictionaries or strings.
  • Avoid floating-point operations that may allocate.
  • Avoid blocking delays.
  • Avoid filesystem operations.
  • Avoid network communication.
  • Keep execution time short.

Some ports support soft interrupt callbacks or deferred execution using micropython.schedule().

CPython signal handlers and operating-system callbacks have different execution semantics and are not direct substitutes.

Package Installation

CPython commonly uses pip to install packages from Python package repositories.

MicroPython uses a different package ecosystem and deployment process.

CPython

python -m pip install requests

MicroPython

Modern MicroPython versions support mip for compatible packages.

From a MicroPython REPL on a device with suitable network access:

import mipmip.install("package_name")

Replace package_name with an actual compatible package.

mip is not available on every firmware build, and direct installation requires an appropriate network connection.

Packages may also be transferred using development tools such as mpremote.

A package designed for CPython is not automatically compatible with MicroPython.

Packages requiring CPython C extensions, substantial RAM, operating-system processes or unavailable standard-library modules generally cannot run unchanged.

JSON and Binary Data

Both environments provide JSON and binary-data handling, but MicroPython implementations may omit optional features.

JSON

import jsondata = {"temperature": 23.5}encoded = json.dumps(data)decoded = json.loads(encoded)

Common json.dumps() and json.loads() operations work on modern MicroPython builds.

Advanced formatting options, custom encoders and other CPython features may be absent.

Binary data

import structpacket = struct.pack("<HB", 1000, 25)value, status = struct.unpack("<HB", packet)

The format string specifies:

  • < — little-endian byte order.
  • H — unsigned 16-bit integer.
  • B — unsigned 8-bit integer.

The example produces a three-byte packet.

MicroPython’s struct module supports common binary formats, but advanced features and platform-dependent representations should be checked before porting code.

Exception Handling Differences

The basic exception syntax is shared.

try:    value = int("invalid")except ValueError:    print("Conversion failed")

However, MicroPython may provide less detailed exception information because of memory constraints.

Some builds also omit or simplify traceback-related features.

MicroPython emergency exception buffer

import micropythonmicropython.alloc_emergency_exception_buf(100)

This reserves memory for exception information generated in hard interrupt contexts.

It has no direct equivalent in ordinary CPython programming.

The buffer size is application-dependent and consumes additional RAM.

Performance Differences

MicroPython is optimised for reduced memory consumption and embedded execution rather than matching CPython’s full feature set.

Performance varies considerably by processor, clock frequency, memory architecture and firmware configuration.

MicroPython-specific optimisation tools

Feature Purpose
@micropython.native Compile supported functions to native code
@micropython.viper Use typed native-code emitter
@micropython.asm_thumb Inline Thumb assembly on supported Arm ports
const() Define constants with MicroPython-specific optimisation
.mpy files Precompiled MicroPython bytecode modules

These facilities are not universally available across MicroPython ports.

Native emitter example

import micropython@micropython.nativedef add_values(a, b):    return a + b

The native emitter can improve some workloads, but actual results depend on the function and target architecture.

It does not automatically make every function faster.

Constant definitions

from micropython import constLED_PIN = const(15)BUFFER_SIZE = const(128)

The const() facility allows MicroPython to optimise supported constant references.

Ordinary CPython does not include the micropython module.

Code Portability Techniques

Use shared Python syntax

Prefer ordinary functions, classes, loops and data structures where possible.

Avoid platform-specific code outside the sections that require hardware access.

Separate hardware interfaces

A sensor-processing function can be written independently of the hardware driver.

def celsius_to_fahrenheit(celsius):    return celsius * 9 / 5 + 32

This function works in both CPython and MicroPython.

Conditional imports

import sysif sys.implementation.name == "micropython":    from machine import Pinelse:    Pin = None

This distinguishes the runtime without assuming a particular board.

The desktop branch must still provide an appropriate hardware abstraction if the application needs GPIO access.

Compatible JSON imports

For modern MicroPython and CPython versions:

import json

Prefer the standard module name rather than using older ujson naming unless compatibility with a specific firmware requires it.

Avoid unnecessary dependencies

A small embedded application may be easier to maintain with a few standard modules than with a large collection of third-party packages.

Check transitive dependencies as well as the package’s main API.

Runtime Detection

The sys module provides useful runtime information.

import sysprint(sys.implementation.name)print(sys.platform)print(sys.version)

Typical implementation names:

Runtime sys.implementation.name
CPython cpython
MicroPython micropython

On MicroPython, sys.platform identifies the port, not necessarily the exact development board.

For instance, several different boards may report the same platform identifier.

Troubleshooting Ported Code

Symptom Likely cause Corrective action
ImportError Module missing Check firmware and available modules
AttributeError API differs Inspect supported methods
MemoryError Heap exhausted Reduce allocations and buffer sizes
OverflowError Numeric range exceeded Check integer implementation and values
TypeError Different API signature Check target implementation
SyntaxError Unsupported syntax Replace newer Python constructs
Unexpected numeric result Different float precision Check rounding and numeric representation
Filesystem error Path or storage unavailable Check mounted filesystem
Package installation fails Incompatible package or unavailable installer Use a MicroPython-compatible package
Application stalls Blocking call Use cooperative scheduling or timeouts
Interrupt fails Unsupported callback operation Remove allocations and blocking work
GPIO code fails on desktop machine unavailable Use platform-specific hardware interface
Timing differs Different scheduler or clock behaviour Use monotonic timing and measure execution
Threading code fails threading unavailable Use asyncio or supported _thread APIs

Compatibility Checklist

  1. Identify the CPython and MicroPython versions involved.
  2. Confirm the MicroPython port and firmware build.
  3. Check syntax support before transferring source files.
  4. Verify all imported modules.
  5. Check optional arguments and return types of library functions.
  6. Confirm integer range and floating-point precision requirements.
  7. Measure available heap memory.
  8. Replace desktop-specific hardware libraries with appropriate embedded interfaces.
  9. Review filesystem paths and storage assumptions.
  10. Replace unsupported threading and process-management code.
  11. Avoid memory allocation in hard interrupt callbacks.
  12. Check timing functions and tick-counter wraparound.
  13. Confirm that third-party packages support MicroPython.
  14. Separate portable application logic from hardware-dependent code.
  15. Test error handling, reset recovery and long-running memory behaviour on the target device.

You may also like