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_sorted internally uses the native windll.Shlwapi.StrCmpLogicalW API, guaranteeing a 1:1 match with Windows Explorer.
  • On macOS/Linux: It utilizes icu.Collator (provided by the PyICU package) to match Nautilus/Finder sorting rules. If PyICU is 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 avoid folder/ being sorted after folder (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, None and NaN are treated as -Infinity and sorted to the front. This flag treats them as +Infinity and 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']