python - 如何使类的字段与构造函数中的参数具有相同的类型
问题描述
我是一个完全“静态类型”的人,也是 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 中找不到任何类型的通用编程来完成该任务。或者我的想法完全错误。
解决方案
您的想法就像在 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
在这里,我们有两个面向外部的变量:previous
和current
,正如您在当前示例中所做的那样。因为我们想要设置这些变量的特定行为,所以我们使用@property
装饰器来声明它们。它们的实际值分别保存在“私有”变量_previous
和_current
中。
初始化时,我们检查初始对象的类型,并将该类型作为“私有”变量存储到类_type
中。
然后,每次某些东西(甚至是类的实例本身)尝试设置instance.previous
orinstance.current
时,我们将其重定向到适当的函数。在这个函数中,我们检查要设置的对象是否与我们初始化类的类型相同。如果不是,我们抛出一个错误。
例如,如果您要存储一个列表或其他集合,那么我认为没有任何合理的方法可以确保整个列表保持相同的类型(或者,实际上,对列表的内容做出任何假设,因为python本身没有。他们都是<type 'list'>
)。
一种可能的解决方法是使用元类并覆盖__instancecheck__()
元类方法,创建一个dict
仅包含特定类型对象的子类,然后PrevCurr
使用其中之一初始化您的实例。
我从类的名称推测你有一些方法可以将值.update()
复制到,并分配一个必须是相同类型的新值。在这种情况下,您可能希望使用 getter 和 setter 以使直接分配更难。current
previous
current
previous
使用示例:
>>> 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'>
推荐阅读
- angular - 根据子组件的异步值在父组件中显示加载动画
- c++ - 是否可以在不使用 C++ 中的模板的情况下从基类获取派生类的类型?
- c# - 从 NumericUpDown 增加/减少值
- flutter - 如何使用“onEvent”为事件实现去抖动器?
- javascript - AmChart V4:使类别轴中的每个标签都可点击,并为每个标签提供不同的功能
- wordpress-gutenberg - extraprops 覆盖现有的道具
- r - 如何使用 R 中的循环进行分组和求和?
- node.js - 我的 NodeJs 项目在我的主机中不起作用(CPanel)
- api - 在 ROBOT 框架中使用 JSON Schema 验证 JSON 响应
- selenium - SeleniumBasic 无法创建 Chrome 进程