Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python中的用户定义不可变项w/o子类化_Python_Python 3.x_Immutability - Fatal编程技术网

python中的用户定义不可变项w/o子类化

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

我知道你显然不能用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.\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
不是选项,除非我可能使用元类。事件对象的属性必须是任意的。但这是个好主意!当然,我可以让其中一个插槽是名称、值对元组的元组。