Skip to content

Examples

This guide provides practical examples of using python-newtype in various scenarios.

Basic Examples

Enhanced String Type

from newtype import NewType

class EnhancedStr(NewType(str)):
    def reverse(self):
        return self[::-1]

    def count_words(self):
        return len(self.split())

    def is_palindrome(self):
        s = ''.join(c.lower() for c in self if c.isalnum())
        return s == s[::-1]

# Usage
text = EnhancedStr("A man a plan a canal Panama")
print(text.is_palindrome())  # True
print(text.count_words())    # 6
print(text.reverse())        # "amanaP lanac a nalp a nam A"

Validated List

class IntList(NewType(list)):
    def append(self, item):
        if not isinstance(item, int):
            raise TypeError("Only integers allowed")
        super().append(item)

    def extend(self, items):
        if not all(isinstance(x, int) for x in items):
            raise TypeError("All items must be integers")
        super().extend(items)

# Usage
numbers = IntList([1, 2, 3])
numbers.append(4)           # OK
numbers.extend([5, 6, 7])  # OK
try:
    numbers.append("8")     # Raises TypeError
except TypeError:
    print("String not allowed")

Cached Dictionary

from functools import lru_cache

class CachedDict(NewType(dict)):
    @lru_cache(maxsize=100)
    def get_or_default(self, key, default=None):
        return self.get(key, default)

# Usage
cache = CachedDict({'a': 1, 'b': 2})
result1 = cache.get_or_default('a', 0)  # Cache miss
result2 = cache.get_or_default('a', 0)  # Cache hit

Advanced Examples

Context Manager Support

class TransactionalDict(NewType(dict)):
    def __enter__(self):
        self._backup = dict(self)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            self.clear()
            self.update(self._backup)
        return False

# Usage
d = TransactionalDict({'key': 'value'})
try:
    with d:
        d['key'] = 'new_value'
        raise ValueError("Something went wrong")
except ValueError:
    print(d['key'])  # Prints: value (rolled back)

Custom Iteration

class FilteredDict(NewType(dict)):
    def __iter__(self):
        return (k for k in super().__iter__() if not k.startswith('_'))

    def items(self):
        return ((k, v) for k, v in super().items() if not k.startswith('_'))

    def values(self):
        return (v for k, v in super().items() if not k.startswith('_'))

# Usage
d = FilteredDict({'a': 1, '_b': 2, 'c': 3})
print(list(d))  # ['a', 'c']

Logging Support

import logging

class LoggedDict(NewType(dict)):
    def __init__(self, logger=None):
        super().__init__()
        self.logger = logger or logging.getLogger(__name__)

    def __setitem__(self, key, value):
        self.logger.info(f"Setting {key}={value}")
        super().__setitem__(key, value)

    def __delitem__(self, key):
        self.logger.info(f"Deleting {key}")
        super().__delitem__(key)

# Usage
logging.basicConfig(level=logging.INFO)
d = LoggedDict()
d['key'] = 'value'  # Logs: Setting key=value
del d['key']        # Logs: Deleting key

Type Validation

class SchemaDict(NewType(dict)):
    def __init__(self, schema=None):
        super().__init__()
        self.schema = schema or {}

    def __setitem__(self, key, value):
        if key in self.schema:
            expected_type = self.schema[key]
            if not isinstance(value, expected_type):
                raise TypeError(f"Expected {expected_type}")
        super().__setitem__(key, value)

# Usage
schema = {'name': str, 'age': int}
d = SchemaDict(schema)
d['name'] = "John"    # OK
d['age'] = 30        # OK
try:
    d['age'] = "30"  # Raises TypeError
except TypeError:
    print("Invalid type")

Performance Monitoring

from time import time

class MetricsDict(NewType(dict)):
    def __init__(self):
        super().__init__()
        self.metrics = {
            'gets': 0,
            'sets': 0,
            'get_time': 0,
            'set_time': 0
        }

    def __getitem__(self, key):
        start = time()
        result = super().__getitem__(key)
        self.metrics['get_time'] += time() - start
        self.metrics['gets'] += 1
        return result

    def __setitem__(self, key, value):
        start = time()
        super().__setitem__(key, value)
        self.metrics['set_time'] += time() - start
        self.metrics['sets'] += 1

# Usage
d = MetricsDict()
for i in range(1000):
    d[i] = i
    _ = d[i]
print(f"Average get time: {d.metrics['get_time'] / d.metrics['gets']:.6f}s")
print(f"Average set time: {d.metrics['set_time'] / d.metrics['sets']:.6f}s")

Real-World Examples

Configuration Management

import os
import json

class ConfigDict(NewType(dict)):
    @classmethod
    def from_env(cls, prefix='APP_'):
        instance = cls()
        for key, value in os.environ.items():
            if key.startswith(prefix):
                config_key = key[len(prefix):].lower()
                instance[config_key] = value
        return instance

    @classmethod
    def from_json(cls, filename):
        with open(filename) as f:
            return cls(json.load(f))

    def to_env(self, prefix='APP_'):
        for key, value in self.items():
            os.environ[f"{prefix}{key.upper()}"] = str(value)

# Usage
config = ConfigDict.from_json('config.json')
config.to_env('MY_APP_')

Data Validation

import re

class ValidatedDict(NewType(dict)):
    def __init__(self, schema):
        super().__init__()
        self.schema = schema

    def __setitem__(self, key, value):
        if key in self.schema:
            rules = self.schema[key]
            if not isinstance(value, rules['type']):
                raise TypeError(f"Expected {rules['type']}")
            if 'pattern' in rules and isinstance(value, str):
                if not re.match(rules['pattern'], value):
                    raise ValueError(f"Invalid format for {key}")
        super().__setitem__(key, value)

# Usage
schema = {
    'email': {'type': str, 'pattern': r'^[\w\.-]+@[\w\.-]+\.\w+$'},
    'age': {'type': int},
}
data = ValidatedDict(schema)
data['email'] = 'user@example.com'  # OK
data['age'] = 25                    # OK

These examples demonstrate various ways to extend built-in Python types using NewType. Each example includes practical use cases and common patterns you might encounter in real-world applications.