Usage Guide
natsort handles much more than simple strings with integers. It is a robust engine capable of parsing version strings, signed floats, OS-level filesystem paths, and handling mixed types without crashing. This guide details these advanced scenarios.
Sorting Semantic Versions
natsort intuitively handles common versioning schemes like MAJOR.MINOR, MAJOR.MINOR.PATCH, or YEAR.MONTH.DAY right out of the box. It naturally breaks down the periods and alphanumeric suffixes into comparable segments.
from natsort import natsorted
versions = ['1.9.9a', '1.11.0', '1.9.9b', '1.11.4', '1.10.1']
print(natsorted(versions))
# Output: ['1.9.9a', '1.9.9b', '1.10.1', '1.11.0', '1.11.4']
Sorting OS Paths (os_sorted)
If you want to sort file paths exactly how your operating system's native file browser (e.g., Windows Explorer, macOS Finder, GNOME Nautilus) sorts them, use the os_sorted() function.
How it works:
- On Windows:
os_sortedinternally uses the nativewindll.Shlwapi.StrCmpLogicalWAPI, guaranteeing a 1:1 match with Windows Explorer. - On macOS/Linux: It utilizes
icu.Collator(provided by thePyICUpackage) to match Nautilus/Finder sorting rules. IfPyICUis not installed, it falls back to a best-effort internal algorithm (ns.LOCALE | ns.PATH | ns.IGNORECASE).
from natsort import os_sorted
paths = [
'/folder (10)/file.txt',
'/folder (1)/file (1).txt',
'/folder/file.txt',
'/folder (1)/file.txt'
]
# Safely handles parent directories and file extensions logically.
print(os_sorted(paths))
Sorting by Real Numbers (realsorted)
By default, natsorted() searches for unsigned integers. This means a string like "pos-3.dat" is parsed as the string "pos-", the integer 3, and the string ".dat".
If your data contains signed floats (e.g., scientific data, coordinate logs), you should use the realsorted() convenience function. This tells the regex parser to respect -, +, ., and exponential e markers as part of the number.
from natsort import realsorted
# The numbers inside these strings represent: +5.10, -3.00, +5.30, +2.00
data = ['pos5.10.dat', 'pos-3.dat', 'pos5.3.dat', 'pos2.dat']
# Standard natsorted will treat the "-" as text and fail to rank it lowest.
# realsorted() evaluates them correctly:
print(realsorted(data))
# Output: ['pos-3.dat', 'pos2.dat', 'pos5.10.dat', 'pos5.3.dat']
Locale-Aware Sorting ("Human Sorting")
"Human sorting" orders non-numeric characters based on their semantic meaning rather than strict Unicode ordinal values. Crucially, it accounts for locale-dependent decimal and thousands separators (e.g., , vs .).
For this, use humansorted().
import locale
from natsort import humansorted
data = ['Apple', 'apple15', 'Banana', 'apple14,689', 'banana']
# Set locale (e.g., US English where ',' is a thousands separator)
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
# Notice how 'apple14,689' is recognized as 14689 and placed correctly.
print(humansorted(data))
# Output: ['apple15', 'apple14,689', 'Apple', 'banana', 'Banana']
Best Practice: If utilizing humansorted(), ensure PyICU is installed, as Python's native locale module is often buggy on UNIX systems.
Algorithm Customization: The ns Enum
natsort's true power lies in its modular algorithm, controlled by the ns Enum. You can combine multiple configurations using the bitwise OR operator (|).
Actually, functions like realsorted() and humansorted() are just aliases for natsorted(..., alg=ns.REAL) and natsorted(..., alg=ns.LOCALE).
from natsort import natsorted, ns
data = ['Apple', 'apple15', 'Banana', 'apple14.689', 'banana']
# Sort as real numbers AND ignore casing globally
print(natsorted(data, alg=ns.REAL | ns.IGNORECASE))
# Output: ['Apple', 'apple14.689', 'apple15', 'Banana', 'banana']
Key ns Flags to Know:
ns.PATH: Treats strings as filesystem paths, splitting directories and file extensions correctly to avoidfolder/being sorted afterfolder (1)/.ns.IGNORECASE: Ignores upper/lowercase when sorting.ns.PRESORT: Sorts the input as raw strings before applying the natural sort algorithm. This guarantees a stable sort if two different strings represent the same number (e.g.,"a1"vs"a01").ns.NANLAST: By default,NoneandNaNare treated as-Infinityand sorted to the front. This flag treats them as+Infinityand moves them to the end.
Sorting Mixed Data Types
Unlike standard Python 3 sorting, which raises TypeError: '<' not supported between instances of 'int' and 'str', natsort gracefully handles mixed types in a single list.
from natsort import natsorted
# Mix of strings, floats, and integers
data = ['4.5', 6, 2.0, '5', 'a']
print(natsorted(data))
# Output: [2.0, '4.5', '5', 6, 'a']
Handling Byte Strings
natsort explicitly avoids guessing text encodings, meaning it does not natively support sorting bytes against str. If you must sort bytes, use the built-in helper functions (as_utf8, as_ascii, or decoder) via the key argument to safely decode them during the sort evaluation.
from natsort import natsorted, as_utf8
data = [b'a56', b'a5', b'a6', b'a40']
# Using the as_utf8 helper to decode on-the-fly
print(natsorted(data, key=as_utf8))
# Output: [b'a5', b'a6', b'a40', b'a56']