我知道你显然不是假设能够在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?
答案 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__
;这很可能导致未来的复杂化,但它确实有效。但是,我不确定每次访问属性时执行检查的性能特征是什么。