【问题标题】:How to create a class where the type is "equal" to int?如何创建一个类型“等于”int的类?
【发布时间】:2020-10-16 12:00:47
【问题描述】:

我正在尝试创建一个可以从类型定义生成 clickhouse (sql-ish) 架构的数据类。

clickhouse 例如支持:

  • Int8
  • Int16
  • Uint8
  • Uint16

在 python 中,这些类型不存在(afaik)。只是int。我希望我仍然可以创建一个类型结构,让我表达它并自动生成我想要的模式,并可能对类型运行验证。我也希望我的类型提示(我使用 PyCharm,不确定 mypy)在传递 int 类型的值时不会抱怨

这是我目前的想法:

@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

有没有办法让 python/我的类型提示明白我希望 Int8 可以与 int 互换?

【问题讨论】:

  • 问题在于Int8int,但int 不是Int8MySchema 是否需要专门使用 Int8?如果是这样,您将遇到一个问题,即并非每个 int 都可以是 Int8,而且 afaik 无法静态检查。
  • 另外,您可以尝试使用typing's NewType 来消除Int8 等的一些样板,但这对您当前的问题没有帮助。
  • 非常感谢。我将研究 NewType。我对并非每个 int 都是 Int8 感到满意,因为我不太担心这一点,而不是人们(我)传递一个字符串。我尝试使用覆盖__instancecheck__ 的元类,但类型提示没有选择它。不过可能是类型提示问题:)
  • @Carcigenicate:不,SupportsIndex 更合适。 SupportsInt 包括 float 之类的内容。
  • @user2357112supportsMonica 你是对的。已删除。

标签: python types type-hinting python-dataclasses


【解决方案1】:

尝试使用直接继承来创建类型:

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

shadowranger 的评论提示编辑:

这里的一切都是 type(int(...))==type(int8(...))==True,除了类 实例化后的 __name__ 属性。当然还有 int8().__class__。 请记住,__new__() 构造函数在类中的任何其他内容被“视为对象化”之前被调用。因此在 __init__() 之前也是如此。 __init__() 在对象构造之后调用,而 __new__() 实际上在它之前调用。 __new() 接收类作为第一个参数,而 __init__() 接收已经构造的对象。 所以,如果使用多重继承,一些初始化应该去__new__(),主要是那些修改数据类型的,还有一些应该在__init__()中完成。 请注意 type(int)!=type(int8)==True 在这里,所以这并不是你问题的确切答案,但它应该是一个很好的指针,结合你已经知道的内容。

【讨论】:

  • 注意:第一个实现(return int(value))不会是int8,它只是int;返回int(value) 确实只是返回一个普通的int,如果奇怪的话,这是合法的。不过,这确实使课程变得毫无意义;一个普通的函数可以做同样的验证。我建议使用第二种方法,并将__slots__ = ()(或您需要的任何插槽)添加到定义中,这样它就不会比普通的int占用更多的内存。
  • @ShadowRanger :当然,您是对的,但关键是如果人们使用 int8() 值将由 assert 语句检查,并且提示器将识别类型为 int。是的,在这种情况下,该类毫无意义,因为可以使用函数来实现相同的目的。但是我想在不写很多解释的情况下展示一些这是如何工作的。一种挑战。第二种方式确实是一条路。内存“问题”无法避免,因为 OP 的实现似乎包括多重继承,而不仅仅是 int。但这不应该影响类型。
  • 我没有遇到 unicode (Py 2) 和 str (Py 3) 的多重继承问题。只要继承了 Python 数据类型并且其属性没有被彻底修改,即完全删除,一切正常。
猜你喜欢
  • 2020-02-27
  • 2015-04-08
  • 1970-01-01
  • 2023-01-05
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
相关资源
最近更新 更多