This doesn't seem to work:
from typing import NewType
MyStr = NewType("MyStr", str)
x = MyStr("Hello World")
isinstance(x, MyStr)
I don't even get False
, but TypeError: isinstance() arg 2 must be a type or tuple of types
because MyStr
is a function and isinstance
wants one or more type
.
Even assert type(x) == MyStr
or is MyStr
fails.
What am I doing wrong?
The purpose of NewType
is purely for static type checking, but for dynamic purposes it produces the wrapped type. It does not make a new type at all, it returns a callable that the static type checker can see, that's all.
When you do:
x = MyStr("Hello World")
it doesn't produce a new instance of MyStr
, it returns "Hello World"
entirely unchanged, it's still the original str
passed in, even down to identity:
>>> s = ' '.join(["a", "b", "c"]) # Make a new string in a way that foils interning, just to rule out any weirdness from caches
>>> ms = MyStr(s) # "Convert" it to MyStr
>>> type(ms) # It's just a str
str
>>> s is ms # It's even the *exact* same object you passed in
True
The point is, what NewType(...)
returns is effectively a callable that:
NewType
itself is a class that begins with __call__ = _idfunc
, that's literally just saying when you make a call with an instance, return the argument unchanged.|
to produce Union
s like other typing
-friendly things.but you can't use it usefully for isinstance
, not because it's not producing instances of anything.
As other answers have mentioned, if you need runtime, dynamic checking, subclassing is the way to go. The other answers are doing both more (unnecessarily implementing __new__
) and less (allowing arbitrary attributes, bloating instances of the subclass for a benefit you won't use) than necessary, so here's what you want for something that:
str
for both static and runtime checking purposesclass MyStr(str): # Inherit all behaviors of str
__slots__ = () # Prevent subclass from having __dict__ and __weakref__ slots, saving 16 bytes
# per instance on 64 bit CPython builds, and avoiding weirdness like allowing
# instance attributes on a logical str
That's it, just two lines (technically, a one-liner like class MyStr(str): __slots__ = ()
is syntactically legal, but it's bad style, so I avoid it), and you've got what you need.