簡體   English   中英

如何使數據類中的屬性只讀?

[英]How to make attribute in dataclass read-only?

假設我有一個像這樣的 class :

class C:

    def __init__(self, stuff: int):
        self._stuff = stuff

    @property
    def stuff(self) -> int:
        return self._stuff

那么stuff是只讀的:

c = C(stuff=10)
print(c.stuff)  # prints 10

c.stuff = 2

按預期失敗

AttributeError:無法設置屬性

如何使用數據類獲得相同的行為? 如果我也想有一個setter ,我可以這樣做:

@dataclass
class DC:
    stuff: int
    _stuff: int = field(init=False, repr=False)

    @property
    def stuff(self) -> int:
        return self._stuff

    @stuff.setter
    def stuff(self, stuff: int):
        self._stuff = stuff

但是如果沒有@stuff.setter部分,我怎么能做到呢?

為了減少dataclass提供的樣板文件,我發現唯一的方法是使用描述符。

In [236]: from dataclasses import dataclass, field
In [237]: class SetOnce:
     ...:     def __init__(self):
     ...:         self.block_set = False
     ...:     def __set_name__(self, owner, attr):
     ...:         self.owner = owner.__name__
     ...:         self.attr = attr
     ...:     def __get__(self, instance, owner):
     ...:         return getattr(instance, f"_{self.attr}")
     ...:     def __set__(self, instance, value):
     ...:         if not self.block_set:
     ...:             self.block_set = True
     ...:             setattr(instance, f"_{self.attr}", value)
     ...:         else:
     ...:             raise AttributeError(f"{self.owner}.{self.attr} cannot be set.")

In [239]: @dataclass
     ...: class Foo:
     ...:     bar:str = field(default=SetOnce())

In [240]: test = Foo("bar")

In [241]: test.bar
Out[241]: 'bar'

In [242]: test.bar = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-242-9cc7975cd08b> in <module>
----> 1 test.bar = 1

<ipython-input-237-bddce9441c9a> in __set__(self, instance, value)
     12             self.value = value
     13         else:
---> 14             raise AttributeError(f"{self.owner}.{self.attr} cannot be set.")
     15

AttributeError: Foo.bar cannot be set.

In [243]: test
Out[247]: Foo(bar='bar')

因為在 class 定義中使用裝飾器本質上會觸發@dataclass裝飾器以使用屬性 object 作為默認字段,所以效果不佳。 可以在外部設置屬性,例如:

>>> from dataclasses import dataclass, field
>>> @dataclass
... class DC:
...     _stuff: int = field(repr=False)
...     stuff: int = field(init=False)
...
>>> DC.stuff = property(lambda self: self._stuff) # dataclass decorator cant see this
>>> dc = DC(42)
>>> dc
DC(stuff=42)
>>> dc.stuff
42
>>> dc.stuff = 99
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
from dataclasses import dataclass

@dataclass(frozen=True)
class YourClass:
    """class definition"""

https://docs.python.org/3/library/dataclasses.html#frozen-instances

class 實例化后,當嘗試更改其任何屬性時,將引發異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM