python中的用户定义不可变项w/o子类化
我知道你显然不能用Python定义你自己的不可变对象类(为什么不呢?),但我有一个很好的理由想这么做 这个对象需要具有任意属性(即,它的属性在实例化时指定,唯一的要求是它们的值是可散列的),而且还需要是不可变的,因为我的体系结构要求它用作字典键(如果有人关心的话,可以查找回调函数) 以下是到目前为止我得到的信息:python中的用户定义不可变项w/o子类化,python,python-3.x,immutability,Python,Python 3.x,Immutability,我知道你显然不能用Python定义你自己的不可变对象类(为什么不呢?),但我有一个很好的理由想这么做 这个对象需要具有任意属性(即,它的属性在实例化时指定,唯一的要求是它们的值是可散列的),而且还需要是不可变的,因为我的体系结构要求它用作字典键(如果有人关心的话,可以查找回调函数) 以下是到目前为止我得到的信息: class InputEvent: """Instances of this class represent a discrete input event, such as a
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.\uuuuu setattr\uuuuu=newevent.\uuuuu ro\u setattr\uuuuu
无效;如果我将其定义为无“ro
”的\uuuu setattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
我知道Python是在成年人之间进行的,但另一方面,错误也经常发生。所以我想在他们浪费我几天的时间之前,把那些特别狡猾的事情扼杀在萌芽状态,比如不小心更改了dict键的值。是的,我可以屈服并将string
子类化,但那将是非常痛苦的。我也可以修改\uuuuu setattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
那么我如何才能填补这最后一个漏洞呢?我如何才能使我假定的不可变实际上拒绝从所有对象写入属性,但它是自己的类“\uuuu new\uuuu()
”,而不诉诸丑陋的堆栈hax?在我看来,您应该能够使用\uu slots\uuuu
并且@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
[1]中的:类Foo(对象):
... __slots_uuuu=['''uuuuuuu thisattr',''uuuuu thattattr']
... 定义初始(自我,**kwargs):
... 对于名称,kwargs.items()中的val:
... setattr(self,“\uuu”+名称,val)
... @财产
... def thisattr(自身):
... 返回自我
... @财产
... def thatattr(自身):
... 返回自我
在[2]中:f=Foo(thisattr=“this”,thatattr=“that”)
在[3]中:f.thisattr
Out[3]:这个
在[4]中:f.thattr
Out[4]:那
在[5]中:f.thisattr=“其他东西”
---------------------------------------------------------------------------
AttributeError回溯(最近一次呼叫上次)
在()
---->1 f.thisattr=“其他东西”
AttributeError:无法设置属性
你当然仍然可以做f.\u Foo\u thattattr=“Something other”
,但在这一点上,你是故意违反安全规定的,不是吗?如果你到处试图破坏东西,那就不是真正的“成年人同意” 这并不能满足这个问题,但为了完整起见,我现在准备了以下代码:
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)
这只是查看调用它的函数是否命名为\uuuuu new\uuuuu
;这很可能在将来导致并发症,但它确实起作用。不过,我不确定每次访问属性时进行检查的性能特征是什么。\uuuu\uuuu
不是选项,除非我可能使用元类。事件对象的属性必须是任意的。但这是个好主意!当然,我可以让其中一个插槽是名称、值对元组的元组。