python - python 中用户定义的不可变变量,不带子类化

标签 python python-3.x immutability

我知道你显然实际上不应该能够在Python中定义你自己的不可变对象(immutable对象)类(为什么不呢?),但我有一个很好的理由想要这样做所以。

该对象需要具有任意属性(即,其属性在实例化时指定,唯一的要求是它们的值是可散列的),但需要是不可变的,因为我的架构要求将它用作字典键(如果有人关心的话,可以查找回调函数)。

这是我到目前为止所得到的:

class InputEvent:
    """Instances of this class represent a discrete input event, such as a keyboard button being depressed or released,
    the mouse moving, or the OS asking us to terminate. These are immutable. The .eventtype attr is a string with a
    machine name identifying what caused the event; other parameters may be present dependent on the event type.

    Additional parameters may be passed in to the constructor as keyword arguments. All parameters must be hashable."""


    def __new__(cls,eventtype,**kwargs):
        newevent=super().__new__(cls)
        newevent.eventtype=eventtype
        for attr,value in kwargs.items():
            try:
                hash(value)
            except TypeError:
                raise TypeError("Tried to add a {0} instance, which is unhashable, as a parameter of an InputEvent".format(value.__class__))
            setattr(newevent,attr,value)
        newevent.__setattr__=newevent.__ro_setattr__
        return newevent

    def __hash__(self):
        return sum(hash(theval) for theval in self.__dict__.values())

    def __eq__(self, other):
        return hash(self)==hash(other)

    def __ro_setattr__(self, key, value):
        raise AttributeError("'{0}' object attribute '{1}' is read-only".format(self.__class__.__name__,key))

对于那些被认为不可能的事情来说,它非常有效;唯一的问题是 newevent.__setattr__=newevent.__ro_setattr__ 没有效果;如果我将其定义为 __setattr__ 且没有“ro_”,它就会达到预期的效果,但这会产生令人不快的副作用,即我无法在 __new__ 中设置属性() 两者之一。

我知道 Python 是成年人之间同意的,但另一方面,错误也会经常发生。因此,我想在浪费我几天的时间之前,将那些特别偷偷摸摸的行为消灭在萌芽状态,比如不小心改变了字典键的值。是的,我可以放弃并子类 string,但这将是 wroooong~。我还可以修改 __setattr__ 来展开堆栈,并且如果调用者是 InputEvent.__new__ 则不会出错,但这很丑陋,可以说更糟 从正确性的角度来看,并且在这一点上我开始担心性能 - 这是一个视频游戏,输入需要快速发生!

那么我怎样才能堵住这最后一个漏洞呢?我怎样才能让我所谓的不可变实际上拒绝除了它自己的类之外的所有内容的属性写入 __new__() 而不诉诸丑陋的堆栈恶作剧?

最佳答案

在我看来,您应该能够使用 __slots__ 并且 @property 装饰器应该做得很好。

In [1]: class Foo(object):
...         __slots__ = ['__thisattr', '__thatattr']
...         def __init__(self, **kwargs):
...             for name,val in kwargs.items():
...                setattr(self, "__"+name, val)
...         @property
...         def thisattr(self):
...             return self.__thisattr
...         @property
...         def thatattr(self):
...             return self.__thatattr

In [2]: f = Foo(thisattr="this", thatattr="that")

In [3]: f.thisattr
Out[3]: this

In [4]: f.thatattr
Out[4]: that

In [5]: f.thisattr = "Something Else"
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-160d2a000ade> in <module>()
----> 1 f.thisattr = "something else"

AttributeError: can't set attribute

您当然仍然可以执行f._Foo__thatattr = "Something else",但此时您是在故意破坏安全词,不是吗?如果你到处试图破坏东西,那不是真正的“同意的成年人”!

关于python - python 中用户定义的不可变变量,不带子类化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31508274/

相关文章:

python - 如何在python中将文件保存到特定目录并选择文件名?

python - 在pygame中的数组支持网格中交换颜色的问题

python - 如何让敌人跟随玩家? pygame

python-3.x - $PATH、sys.path 和 os.environ 之间的区别

javascript - 如何在 Python3 中创建带有倒数计时器的网页?

Python 字符串引用

python - 如何在python中多次更新windowText?

python - 在Python中将列表从一个函数传输到另一个函数

functional-programming - 不可变的游戏对象,基本的函数式编程问题

javascript - ngrx-默认值被传播运算符覆盖了吗?