Search code examples
pythonpython-typingmypy

Can I simplify deriving from a string type in Python?


I derive from str to get strongly typed string references. They look like below, which are redundant, and I'd like to avoid the redundancy:

class VarRef(str):
    def __new__(cls, *args, **kw):
        return str.__new__(cls, *args, **kw)

class SceneRef(str):
    def __new__(cls, *args, **kw):
        return str.__new__(cls, *args, **kw)

class AmbienceRef(str):
    def __new__(cls, *args, **kw):
        return str.__new__(cls, *args, **kw)

This ensures, for example, that I need an explicit cast to get a VarRef but that otherwise it functions like a string.

I have many more and hoping there's a way to copying the same __new__ constructor for each one.

I use these only for MyPy type checking, so at runtime it'd be fine if they were normal strings. If there is a mechanism to tag them only for type checking I'd be happy to use that approach.


Solution

  • Use NewType to declare a type that is functionally identical but nominal different from the base type.

    from typing import NewType
    
    URL = NewType("URL", str)
    fail: URL = "Hello World"  #  Incompatible types in assignment (expression has type "str", variable has type "URL")
    docs: URL = URL("https://docs.python.org/3/library/typing.html")
    

    Keep in mind that operations on the value are still defined as usual, including their types. For example, docs + '#newtype' will still produce a str instead of a URL.