首页 > 解决方案 > 如何使类的字段与构造函数中的参数具有相同的类型

问题描述

我是一个完全“静态类型”的人,也是 python 的初学者,我想在 python 中创建一个数据类型,它可以保存一些对象的 prev-next 状态。结构是这样的:

class PrevCurr:
    def __init__(self, obj_initial):
       self.previous = obj_initial
       self.current = obj_initial

如果我obj_initial是这样的,没问题[None, None]。但是想象一下,我需要将这个类包装到不同的大类型上,比如字典/列表/1000 多个元素的集合或/和用户定义的类。然后我想要这样的东西:

class PrevCurr:
    def __init__(self, obj_type, *args, **kwargs):
       '''make self.previous and self.orientation of the same type as obj_type'''
       self.previous = ...
       self.current = ...

我的问题是:如何确定字段(或者在 python 中是否有其他名称?)设置为与我要包装的对象的类型相同?我有上面提出的想法,我可能会以某种方式传递类型和其他信息,比如大小等作为参数而不是对象本身来节省内存,但是如何确保我的字段设置为与对象的类型完全相同?afaik 我无法在 python 中将构造函数作为参数传递,我在 python 中找不到任何类型的通用编程来完成该任务。或者我的想法完全错误。

标签: python

解决方案


您的想法就像在 C 或 Java 等静态类型语言中一样,您必须在为特定类型的对象分配任何内容之前为它们预先分配内存。这是python中的错误方法。

相反,让我们考虑一下您想要什么:一个可以表示任何单一类型的类,但是一旦初始化,它就只能表示该特定类型。在 Java 中,您会为此使用泛型,但 python 没有这些。我们在 python 中可以做的是确保只有正确类型的对象才能分配给它。

如果程序员错误地使用了类,那么在 python 中执行此类操作的惯用方式是在运行时引发错误;对于静态类型检查器来说,并没有像在 Java 中那样抛出编译时错误的好方法。我提出以下内容供您参考:

class PrevCurr:
    @property
    def previous(self):
        return self._previous
    @previous.setter
    def previous(self, value):
        if not isinstance(value, self._type):
            raise ValueError(f"Previous value must be of type {self._type}")
        self._previous = value

    @property
    def current(self):
        return self._current
    @current.setter
    def current(self, value):
        if not isinstance(value, self._type):
            raise ValueError(f"Current value must be of type {self._type}")
        self._current = value

    def __init__(self, obj_initial):
        self._type = type(obj_initial)
        self._previous = obj_initial
        self._current = obj_initial

在这里,我们有两个面向外部的变量:previouscurrent,正如您在当前示例中所做的那样。因为我们想要设置这些变量的特定行为,所以我们使用@property装饰器来声明它们。它们的实际值分别保存在“私有”变量_previous_current中。

初始化时,我们检查初始对象的类型,并将该类型作为“私有”变量存储到类_type中。

然后,每次某些东西(甚至是类的实例本身)尝试设置instance.previousorinstance.current时,我们将其重定向到适当的函数。在这个函数中,我们检查要设置的对象是否与我们初始化类的类型相同。如果不是,我们抛出一个错误。


例如,如果您要存储一个列表或其他集合,那么我认为没有任何合理的方法可以确保整个列表保持相同的类型(或者,实际上,对列表的内容做出任何假设,因为python本身没有。他们都是<type 'list'>)。

一种可能的解决方法是使用元类并覆盖__instancecheck__()元类方法,创建一个dict仅包含特定类型对象的子类,然后PrevCurr使用其中之一初始化您的实例。

我从类的名称推测你有一些方法可以将值.update()复制到,并分配一个必须是相同类型的新值。在这种情况下,您可能希望使用 getter 和 setter 以使直接分配更难。currentpreviouscurrentprevious


使用示例:

>>> a = PrevCurr(3)
>>> a.previous = 2
>>> a.previous = 4.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in previous
ValueError: Previous value must be of type <class 'int'>

推荐阅读