How to create a class where the type is "equal" to int?

Viewed 197

I am trying to create a dataclass that can generate a clickhouse (sql-ish) schema from type definitions.

clickhouse for example supports:

  • Int8
  • Int16
  • Uint8
  • Uint16

In python, these types don't exist (afaik). It's just int. I am hoping I can still create a type structure that let's me express it and auto generate my desired schema and maybe run validation on the types. I also hope to have my type hinter (I use PyCharm, not sure about mypy) not complain when I pass a value of type int

This is my idea so far:

@dataclass
class ClickhouseType:
    @classmethod
    def schema_type(cls):
        return cls.__name__

    @classmethod
    def validate(cls, value):
        # just included as an example, not important as part of this discussion :) 
        pass


@dataclass
class Int8(int, ClickhouseType):
    @classmethod
    def validate(cls, value):
        # just included as an example, not important as part of this discussion :)
        assert -128 <= value <= 127

@dataclass
class MySchema:
    some_number: Int8


x = MySchema(some_number=4)  # type hint complaints that I pass an int when an Int8 is required

Is there a way to make python/my type hinter understand that I want Int8 to be interchangeable with int?

1 Answers

Try creating your types using direct inheritance:

class int8 (int):
    def __new__ (cls, value):
        assert -128<=value<=127
        return int(value) # Simplified
        # Or if you need more things:
        self = int.__new__(cls, value)
        self.something = "something else"
        return self

Edit prompted by shadowranger's comment:

Everything here will be type(int(...))==type(int8(...))==True, except the classes
__name__ attribute after instancing. And int8().__class__ of course.
Remember that the __new__() constructor is called before anything else in the class is "considered objectified". Therefore before the __init__() as well. __init__() is called after the object's construction, while __new__() is called practically before it.
__new() receives the class as a first argument, while __init__() receives already constructed object.
So, if multiple inheritance is used, some of initialization should go to __new__(), mostly those modifying the data type, and some of it should be done in __init__().

Note that type(int)!=type(int8)==True here, so this is not really an exact answer to your question, but it should be a good pointer combined with what you already know.
Related