如何子类化 int 并使其可变

How to subclass int and make it mutable

是否有可能 subclass int 并使其可变?

考虑以下 class:

class CustomBitVector(int):

    # bit 7
    @property
    def seventh_property(self):
        return bool(self & (1 << 7))

    @seventh_property.setter
    def seventh_property(self, val):
        self |= bool(val) << 7

    # bit 6
    @property
    def sixth_property(self):
        return bool(self & (1 << 6))

    @sixth_property.setter
    def sixth_property(self, val):
        self |= bool(val) << 6


    # ... a few more of these ...

    # bit 0
    @property
    def zeroth_property(self):
        return bool(self & (1 << 0))

    @zeroth_property.setter
    def zeroth_property(self, val):
        self |= bool(val) << 0

我正在尝试为位向量创建一个漂亮的接口。我正在从套接字读取专有协议,并且我已经制作了 classes 来表示我是 sending/receiving 的消息。通常这些消息包含位向量,像这样处理它们会很好。

这对于读取位向量值已经很有效了,但是设置它们不起作用,因为 int 是不可变的。

如果我像这样重写其中一个设置器:

@sixth_property.setter
def sixth_property(self, val):
    print 'before:', self
    self |= bool(val) << 6
    print 'after:', self

然后我得到这个行为:

In [2]: c = CustomBitVector()

In [3]: c.sixth_property
Out[3]: False

In [4]: c.sixth_property = True
before: 0
after: 64

In [5]: c
Out[5]: 0

In [6]: c.sixth_property
Out[6]: False

我可以看到我的愚蠢......我正在分配给 self 而不是修改它。在这种情况下如何修改 self

有什么疯狂的黑客来实现这一目标吗?也许使用 metaclass 什么的?


更新

我忘了提一个要求:

CustomBitVector 的实例必须表现得像 int。特别是,我需要能够将它们传递给 struct.pack

Is it possible to subclass int and make it mutable?

有点。你可以添加你想要的所有可变部分,但你不能触及 int 部分,所以你可以添加的可变性程度对你没有帮助。

相反,不要使用 int subclass。使用存储 int 的常规对象。如果您希望能够像 int 一样将它传递给 struct.pack,请实现一个 __index__ 方法来定义如何将您的对象解释为 int:

class IntLike(object): # not IntLike(int):
    def __init__(self, value=0):
        self.value = value
    def __index__(self):
        return self.value
    ...

您可以实施其他方法,例如 |__or__ 和就地可变 |=__ior__。不过,不要试图过分追求与 int 的完全互操作性;例如,不要试图使您的对象可用作字典键。毕竟,它们是可变的。

如果您的 class 是一个 int 子 class 对您来说真的很重要,您将不得不牺牲您想要的 c.sixth_property = True 语法。您必须选择 c = c.with_sixth_property(True) 之类的替代方案,并以非可变方式实施。

这是我的解决方案。它还使一个整数可迭代到。

import ctypes

class MutableInt(int):
    def __init__(self, val = 0):
        self._val = val

    def __int__(self):
        return self._val

    def __index__(self):
        return self._val

    def __str__(self):
        return str(self._val)

    def __repr__(self):
        return repr(self._val)

    def __iter__(self):
        self._iter_cnt = 0
        return self

    def __next__(self):
        if self._iter_cnt == 0:
            self._iter_cnt = 1
            return self._val
        else:
            raise StopIteration

    def set(self, val):
        self._val = val
        ob_digit = ctypes.c_long.from_address(id(self)+24)
        ob_digit.value = val

我只是打包了我的概念验证代码并将一个 mutableint 包上传到 PyPi。

https://pypi.org/project/mutableint/

这里是用法。

from mutableint import MutableInt

# create a mutable integer with value 10 
a = MutableInt(10)

# change the value of a to 11
a.set(11)