用户在python w / o子类化中定义了不可变量

时间:2015-07-20 03:03:43

标签: python python-3.x immutability

我知道你显然不是假设能够在Python中定义自己的不可变对象类(为什么不能?),但我有一个很好的理由想要这样做。

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

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

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__()中设置属性1}}要么。

我知道Python是在同意的成年人之间,但另一方面,错误发生 - 定期。因此,我想特别狡猾地扼杀,比如在他们浪费我几天的时间之前不小心改变了字母键的价值。是的,我可以陷入并继承string,但那将是 wroooong 〜。我还可以修改__setattr__以展开堆栈,如果调用者是InputEvent.__new__则不会出错,但是这很丑陋,可以说是甚至更糟来自正确性立场,此时我开始担心性能 - 这是一个视频游戏,输入需要快速

那我怎么能关闭这最后一个漏洞呢?我怎样才能使我所谓的不可变实际拒绝属性写入来自除了它自己的类之外的所有内容。 __new__()没有诉诸丑陋的堆栈hax?

2 个答案:

答案 0 :(得分:1)

在我看来,你应该可以使用__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",但是那时候你有意破坏安全的话语不属于你吗?它不是真的&#34;同意成年人&#34;如果你四处寻找破坏的东西!

答案 1 :(得分:0)

这不符合这个问题,但这里是为了完整性我现在已经存在的代码:

    def __setattr__(self, key, value):
        if not sys._getframe(1).f_code.co_name=="__new__":
            raise AttributeError("'{0}' object attribute '{1}' is read-only".format(self.__class__.__name__,key))
        else:
            object.__setattr__(self,key,value)

这只是查看调用它的函数是否被命名为__new__;这很可能导致未来的复杂化,但它确实有效。但是,我不确定每次访问属性时执行检查的性能特征是什么。