具体问题:我在Python中创建了一个类,它是float的子类,这里作为示例只是添加了一个“name”字段。该课程似乎如我所愿地工作,但我注意到我无法对这些对象执行 copy.copy() 或 copy.deepcopy() 。类和复制命令是:
import copy
class NamedFloat(float):
def __new__(cls, val: float, name: str):
if val is None:
return None
else:
return super().__new__(cls, val)
def __init__(self, val: float, name: str):
self.name = name
x = NamedFloat(4.23, 'ClientA')
x2 = copy.copy(x)
报错信息是:
Traceback (most recent call last):
File "C:\Users\Noughbee\Python\nf\named_float.py", line 18, in <module>
x2 = copy.copy(x)
^^^^^^^^^^^^
File "C:\Users\Noughbee\AppData\Local\Programs\Python\Python311\Lib\copy.py", line 102, in copy
return _reconstruct(x, None, *rv)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Noughbee\AppData\Local\Programs\Python\Python311\Lib\copy.py", line 265, in _reconstruct
y = func(*args)
^^^^^^^^^^^
File "C:\Users\Noughbee\AppData\Local\Programs\Python\Python311\Lib\copyreg.py", line 105, in __newobj__
return cls.__new__(cls, *args)
^^^^^^^^^^^^^^^^^^^^^^^
TypeError: NamedFloat.__new__() missing 1 required positional argument: 'name'
是否有缺失的 dunder 方法或其他方法可以帮助这门课很好地处理复制?
元问题:我真的很喜欢将浮点数或 NumPy ndarrays 子类化的类的想法,因为主要数据是一个单一的东西——一个浮点数或一个 NDArray——恰好有与之相关的元数据……和使用这些子类,当我想访问主要内容时我可以写“value”,而在访问元数据时我可以写“value.name”或其他任何内容。这很有趣,而且对我来说,当您可以使用数量编写熟悉的数学表达式并“忘记”它携带的元数据时,可以生成更清晰的代码。但是这个问题和我最近发布的一个有点相关的问题(How to avoid an object that subclasses NumPy's ndarray being "demoted" to a regular ndarray when placed in a containing ndarray?) may be telling me that this "trick" is'没有那么强大,我应该克服被它迷住的感觉。你怎么说?
为了避免这个错误,你应该为参数定义一个默认值
name
:
import copy
class NamedFloat(float):
def __new__(cls, val: float, name: str = None):
if val is None:
return None
else:
return super().__new__(cls, val)
def __init__(self, val: float, name: str):
self.name = name
x = NamedFloat(4.23, "ClientA")
x2 = copy.copy(x)
copy.copy
函数不知道类的构造函数的签名,这就是它引发该错误的原因,但它允许您通过 __copy__
方法提供自己的浅拷贝实现:
def __copy__(self) -> 'NamedFloat':
return NamedFloat(self, self.name)
尝试定义
__getnewargs__()
来告诉 __copy__()
的默认实现如何重建 NamedFloat
的实例:
In [25]: import copy
...:
...:
...: class NamedFloat(float):
...: __slots__ = ('name',)
...:
...: def __new__(cls, value, name):
...: self = super().__new__(cls, value)
...: self.name = name
...: return self
...:
...: def __getnewargs__(self):
...: return float(self), self.name
...:
...:
...:
...: x = NamedFloat(4.23, 'ClientA')
...: y = NamedFloat(4.23, 'ClientB')
...: x2 = copy.copy(x)
...: y2 = copy.copy(y)
...: print('x', id(x), x, repr(x), type(x), x.name)
...: print('x2', id(x2), x2, repr(x2), type(x2), x2.name)
...: print('y', id(y), y, repr(y), type(y), y.name)
...: print('y2', id(y2), y2, repr(y2), type(y2), y2.name)
x 4371847520 4.23 4.23 <class '__main__.NamedFloat'> ClientA
x2 4371844592 4.23 4.23 <class '__main__.NamedFloat'> ClientA
y 4371844208 4.23 4.23 <class '__main__.NamedFloat'> ClientB
y2 4371854624 4.23 4.23 <class '__main__.NamedFloat'> ClientB