`NewType` pattern in Python

In Rust and Golang, there is this popular pattern called the new type pattern where we can define types that are a subtype of the supertype and implement methods on the subtype that constrains the value of the subtype itself by holding certain invariances.

For example, we can make a new type FourCharsStr like so:

from typing import TYPE_CHECKING

    from typing import NoReturn, Union

class FourCharsStr(str):
    def _validate_str(cls, value: "str") -> "Union[str, NoReturn]":
        assert len(value) == 4, f"`FourCharsStr` must have a length of 4, it has a length of {len(value)}"
        return value

    def __new__(cls, value: "str") -> "FourCharsStr":
        new_str = super().__new__(cls, cls._validate_str(value))
        return new_str

It works as follows:

except AssertionError as err:
    print(err) # `FourCharsStr` must have a length of 4, it has a length of 5
four_chars_str = FourCharsStr("heyu")
assert type(four_chars_str + 'o') == str # may not be ideal, 
# we may want to operate on `four_chars_str` like a regular `str` 
# but would want to prevent any operations that violate its invariance, 
# e.g. it must continue to be a string of only 4 characters.

This is not ideal in some use cases because perhaps we want to retain the behavior of the supertype and any operations of the supertype on the subtype should either raise an Exception if it violates the invariance of the subtype or return a (unfortunately) fresh instance of the subtype.

I made this library called python-newtypes so that one can easily create subtypes that behave like its supertype and raises exceptions if any operations on the subtype violate its invariances. However, this does not work on complex types like pandas’ DataFrame or numpy’s NDArray.

Github Repo: GitHub - jymchng/python-newtype: Extending your Python types using the `NewType` pattern easily!

Do comment on its implementation, looking forward to suggestions on its improvements.

1 Like

You may want to reconsider the name since typing.NewType is an existing and slightly different thing in Python.

1 Like