Simple yet flexible natural sorting in Python that enables developers to sort strings containing numbers in a natural, human-expected order rather than lexicographical order.
—
Functions for generating reusable sorting key functions that can be used with Python's built-in sorting functions. This approach is more efficient when you need to sort multiple times with the same algorithm, as it pre-computes the sorting logic.
Generate a key function for natural sorting that can be reused with built-in sorting functions.
def natsort_keygen(key=None, alg=ns.DEFAULT):
"""
Generate a key to sort strings and numbers naturally.
This key is designed for use as the 'key' argument to functions
such as the sorted() builtin or list.sort().
Parameters:
- key: callable, optional - A key function applied before parsing for numbers
- alg: ns enum, optional - Algorithm control flags (default: ns.DEFAULT)
Returns:
Callable - A function suitable for use as a sorting key
Examples:
>>> key_func = natsort_keygen()
>>> sorted(['num10', 'num2', 'num1'], key=key_func)
['num1', 'num2', 'num10']
>>> # Custom algorithm with case-insensitive real numbers
>>> key_func = natsort_keygen(alg=ns.REAL | ns.IGNORECASE)
>>> data = ['Item-5.2', 'item10.1', 'ITEM2.7']
>>> sorted(data, key=key_func)
['Item-5.2', 'ITEM2.7', 'item10.1']
"""Pre-generated natural sort key function using default settings.
natsort_key: Callable[[Any], Tuple[Any, ...]]
"""
The default natural sorting key.
This is the output of natsort_keygen() with default values.
Equivalent to natsort_keygen() but pre-computed for efficiency.
Examples:
>>> sorted(['num10', 'num2', 'num1'], key=natsort_key)
['num1', 'num2', 'num10']
"""Generate a key function that replicates your operating system's file browser sort order.
def os_sort_keygen(key=None):
"""
Generate a sorting key to replicate your file browser's sort order.
Creates platform-specific sorting keys that match the behavior of
file explorers on different operating systems.
Parameters:
- key: callable, optional - A key function applied before OS sorting
Returns:
Callable - A function suitable for OS-native path sorting
Notes:
- On Windows: Uses StrCmpLogicalW API for Explorer-compatible sorting
- On macOS/Linux: Uses ICU collation if available, otherwise locale-based
- Results will differ intentionally between platforms
"""Pre-generated OS sort key function using default settings.
os_sort_key: Callable[[Any], Tuple[Any, ...]]
"""
The default key to replicate your file browser's sort order.
This is the output of os_sort_keygen() with default values.
Platform-specific implementation for consistent OS behavior.
Examples:
>>> import os
>>> files = os.listdir('.')
>>> sorted(files, key=os_sort_key) # Matches file browser order
"""from natsort import natsort_keygen, ns
import time
# Large dataset for performance testing
data = [f'item{i}' for i in range(10000, 0, -1)] # ['item10000', 'item9999', ...]
# Method 1: Using natsorted (recreates key function each time)
start_time = time.time()
for _ in range(100):
from natsort import natsorted
result = natsorted(data)
end_time = time.time()
print(f"natsorted method: {end_time - start_time:.4f} seconds")
# Method 2: Using pre-generated key (more efficient for repeated sorting)
key_func = natsort_keygen()
start_time = time.time()
for _ in range(100):
result = sorted(data, key=key_func)
end_time = time.time()
print(f"key generation method: {end_time - start_time:.4f} seconds")from natsort import natsort_keygen, ns
# Sort a list in-place using a generated key
file_list = ['file10.txt', 'file2.txt', 'file1.txt', 'file20.txt']
print(f"Original: {file_list}")
# Create a key function for natural sorting
nat_key = natsort_keygen()
file_list.sort(key=nat_key)
print(f"Sorted in-place: {file_list}")
# Sort with custom algorithm (case-insensitive, real numbers)
mixed_data = ['Value-5.2', 'value10.1', 'VALUE2.7']
print(f"Original mixed: {mixed_data}")
real_key = natsort_keygen(alg=ns.REAL | ns.IGNORECASE)
mixed_data.sort(key=real_key)
print(f"Sorted mixed: {mixed_data}")from natsort import natsort_keygen
from pathlib import Path
# Sort file paths by filename only, ignoring directory structure
paths = [
'/home/user/documents/file10.txt',
'/var/log/file2.txt',
'/tmp/file1.txt',
'/home/user/file20.txt'
]
# Create a key that extracts filename before natural sorting
filename_key = natsort_keygen(key=lambda x: Path(x).name)
sorted_paths = sorted(paths, key=filename_key)
print("Sorted by filename:")
for path in sorted_paths:
print(f" {path}")
# Sort by file extension, then by natural order of filename
def extension_then_name(path):
p = Path(path)
return (p.suffix, p.stem) # Sort by extension first, then stem
ext_key = natsort_keygen(key=extension_then_name)
files = ['document.pdf', 'image10.jpg', 'image2.jpg', 'readme.txt', 'data.pdf']
sorted_by_ext = sorted(files, key=ext_key)
print(f"Sorted by extension then name: {sorted_by_ext}")from natsort import os_sort_keygen
import os
from pathlib import Path
# Get current directory files
current_dir = Path('.')
files = [f.name for f in current_dir.iterdir() if f.is_file()]
# Sort using OS-native ordering (matches file browser)
os_key = os_sort_keygen()
os_sorted_files = sorted(files, key=os_key)
print("Files sorted in OS-native order:")
for filename in os_sorted_files[:10]: # Show first 10
print(f" {filename}")
# Compare with regular natural sorting
from natsort import natsort_key
nat_sorted_files = sorted(files, key=natsort_key)
print("\nDifferences between OS and natural sorting:")
for i, (os_file, nat_file) in enumerate(zip(os_sorted_files, nat_sorted_files)):
if os_file != nat_file:
print(f" Position {i}: OS='{os_file}' vs Natural='{nat_file}'")from natsort import natsort_keygen, ns
# Complex data requiring multiple algorithm flags
scientific_data = [
'Experiment-1.5E+10_ResultA',
'experiment-2.3e-5_resultB',
'EXPERIMENT-1.2E+3_resultC',
'Experiment-5.0_resultD'
]
# Combine multiple algorithm flags:
# - REAL: handle signed floats and scientific notation
# - IGNORECASE: case-insensitive comparison
# - LOWERCASEFIRST: lowercase items come first
complex_key = natsort_keygen(
alg=ns.REAL | ns.IGNORECASE | ns.LOWERCASEFIRST
)
sorted_scientific = sorted(scientific_data, key=complex_key)
print("Scientific data sorted with complex algorithm:")
for item in sorted_scientific:
print(f" {item}")
# Using path-specific sorting for nested directory structures
paths = [
'data/experiment1/result10.txt',
'data/experiment1/result2.txt',
'data/experiment10/result1.txt',
'data/experiment2/result1.txt'
]
path_key = natsort_keygen(alg=ns.PATH)
sorted_paths = sorted(paths, key=path_key)
print("\nPaths sorted with PATH algorithm:")
for path in sorted_paths:
print(f" {path}")from natsort import natsort_keygen, ns
# Create different specialized keys for different data types
version_key = natsort_keygen(alg=ns.FLOAT) # For version strings like "1.2.10"
filename_key = natsort_keygen(alg=ns.PATH | ns.IGNORECASE) # For filenames
human_key = natsort_keygen(alg=ns.LOCALE | ns.IGNORECASE) # For human-readable text
# Version strings
versions = ['v1.2.10', 'v1.2.2', 'v1.10.1', 'v2.0.0']
print(f"Versions: {sorted(versions, key=version_key)}")
# Filenames (case-insensitive, path-aware)
filenames = ['README.txt', 'src/main.py', 'src/utils.py', 'tests/test_main.py']
print(f"Files: {sorted(filenames, key=filename_key)}")
# Human text (locale-aware, case-insensitive)
names = ['Müller', 'müller', 'Anderson', 'Åberg']
print(f"Names: {sorted(names, key=human_key)}")Install with Tessl CLI
npx tessl i tessl/pypi-natsort