6

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?

ragazzojp
  • 477
  • 3
  • 14

5 Answers5

0

Cross-reference: inheritance from str or int

Even more detailed in the same question: https://stackoverflow.com/a/2673802/1091677


If you would like to subclass Python's str, you would need to do the following way:

class MyStr(str):
  # Class instances construction in Python follows this two-step call:
  # First __new__, which allocates the immutable structure,
  # Then __init__, to set up the mutable part.
  # Since str in python is immutable, it has no __init__ method.
  # All data for str must be set at __new__ execution, so instead
  # of overriding __init__, we override __new__:
  def __new__(cls, *args, **kwargs):
    return str.__new__(cls, *args, **kwargs)

Then:

x = MyStr("Hello World")

isinstance(x, MyStr)

returns True as expected

SonicARG
  • 499
  • 9
  • 14
  • 1
    Thanks, but I don't need to subclass `str`: I need to define custom types and make them work as normal types. `str` is just an example. – ragazzojp Nov 04 '21 at 18:58
  • 2
    `MyStr` shall not be a `str`, but a different type, therefore I used `NewType`. – ragazzojp Jan 11 '22 at 10:39
0

As at python 3.10...

I'd speculate that the answer to "Why isn't Python NewType compatible with isinstance and type?" ... is "It is a limitation of NewType".

I'd speculate that the answer to "What am I doing wrong?" ... is "nothing". You are assuming NewType makes a new runtime type, it appears that it doesn't.

And for what it's worth, I wish it did work.

Trevor Taylor
  • 176
  • 2
  • 7
  • I don't think NewType will ever work with isinstance properly, my impression is that NewType is only meant to create "type-check-time" type hints; NewType doesn't have (or is not meant to have) any run-time presence, in the same way that generic parameters are type-check-time only – Trevor Taylor Sep 22 '22 at 06:45
0

maybe you want a type that is methods like str does but is not a str?

A simple way to get that effect is just:

class MyStr:
    value:str
    def __init__(self,value:str):
       self.value=value
    pass

... but that means using all the string methods is "manual", e.g.

x=MyStr('fred')
x.value.startswith('fr')

... you could use @dataclass to add compare etc.

This is not a one-size-fits-all answer, but it might suit your application.

Trevor Taylor
  • 176
  • 2
  • 7
0

then make that simple

class MyStr:
    value:str
    def __init__(self,value:str):
       self.value=value

...generic like Str in (incomplete) https://github.com/urnest/urnest/blob/master/xju/newtype.py

... and you can write:

class MyStrType:pass; class MyOtherStrType:pass
class MyStr(Str[MyStrType]):
    pass
class MyOtherStr(Str[MyOtherStrType]):
    pass

x=MyStr('fred')
y=MyOtherStr('fred')
x < y # ! mypy error distinct types MyStr and MyOtherStr

That's what I was after, which might be what you were after? I have to provide Int,Str separately but in my experience distinct int, str, float, bool, bytes types give a lot of readability and error-rejection leverage. I will add Float, Bool, Bytes to xju.newtype soon and give them all the full set of methods.

Trevor Taylor
  • 176
  • 2
  • 7
  • xju.newtype at that link now has Float,Int,Str with fairly full complement of methods (not yet pickle or to-from-hex though) – Trevor Taylor Sep 22 '22 at 06:47
  • added pickle support – Trevor Taylor Sep 22 '22 at 06:54
  • Hi Trevor Taylor. I am surprised by the number of independent solutions which you provided to the problem described at the top of this page. Some even with additional comments. Could you maybe delete most of that and [edit] on of your posts to provide a single multi-angle answer? I mean, you do try to answer according to [answer], aren'T you? It almost looks like you mistkae this for a chat forum. – Yunnosch Oct 03 '22 at 15:20
-1

looks might have been "fixed" in python 3.10:

https://docs.python.org/3/library/typing.html?highlight=typing#newtype

says:

Changed in version 3.10: NewType is now a class rather than a function. There is some additional runtime cost when calling NewType over a regular function. However, this cost will be reduced in 3.11.0.

I don't have 3.10 handy as I write this to try your example.

Trevor Taylor
  • 176
  • 2
  • 7
  • no, not fixed:(Pdb) import sys (Pdb) sys.version '3.10.7 (main, Sep 16 2022, 07:55:53) [GCC 9.3.0]' (Pdb) from typing import NewType (Pdb) MyStr = NewType("MyStr", str) (Pdb) x = MyStr("Hello World") (Pdb) isinstance(x, MyStr) *** TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union – Trevor Taylor Sep 17 '22 at 00:52
  • ... but it seems to get worse: (Pdb) type(7) (Pdb) isinstance(type(7),type) True (Pdb) isinstance(type(MyStr),type) True (Pdb) isinstance(7,MyStr) *** TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union – Trevor Taylor Sep 17 '22 at 00:54