混合使用可变/不可变的“数据容器”python

Having a mix of mutable/immutable "data container" python

提问人:Abdulrahman Sheikho 提问时间:10/10/2023 最后编辑:Abdulrahman Sheikho 更新时间:10/11/2023 访问量:59

问:

我想知道是否有办法在相同的“数据容器”中定义可变/不可变/默认值的值。

我所说的“数据容器”是指任何像 、 、 、tupledictclassEnumdataclass

这个例子清楚地表明了我的观点:

如果我想拥有 as 值。Pagefirst_pagecurrent_pagelast_page

如果我使用该方法,则实现将是这样的:dataclass

from dataclasses import dataclass

@dataclass(slots=True)
class Pages:
    first_page: int = 0 # need to be immutable
    current_page: int   # need to be initialized and mutable
    last_page: int      # need to be initialized and immutable

这能做到吗?或者有没有办法做到这一点?

Python 不可变性 数据类

评论

0赞 Scott Hunter 10/10/2023
您想允许 ?Enum
1赞 Matt Pitkin 10/10/2023
对于一个类,请使用 getter 和 setter,例如,参见 stackoverflow.com/questions/2627002/...
0赞 Abdulrahman Sheikho 10/10/2023
@ScottHunter我知道的工作方式是 ,它违反了 beyond 的逻辑,但我提到它是因为它属于同一类别。这意味着我正在将属性存储到特定Enumtupleenums

答:

0赞 Matt Pitkin 10/10/2023 #1

对于类,您可以使用 getter 和 setter。例如,要保持不可变,您可以创建一个名为(在 Python 中实际上不是私有的)的准私有属性,您可以通过 (getter) 属性属性访问该属性:first_page_first_pagefirst_page

class Pages:
    _first_page: int = 0
    
    @property
    def first_page(self):
        return self._first_page

有了这个,你可以得到 ,但如果你尝试设置,你会得到一个:first_pagefirst_pageAttributeError

p = Pages()
print(p.first_page)
0
p.first_page = 2
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[17], line 1
----> 1 p.first_page = 2

AttributeError: can't set attribute

你可以有一个等效的属性,它显式返回一条消息,该消息是不可变的,什么都不做,例如:setterfirst_page

class Pages:
    _first_page: int = 0
    
    @property
    def first_page(self):
        return self._first_page

    @first_page.setter
    def first_page(self, _):
        print("first_pass is immutable")
        pass

对于要可变的值,可以像往常一样设置类属性,或者(如上所述)使用 和 修饰函数来设置等效的准私有属性。@property@setter

评论

0赞 Abdulrahman Sheikho 10/11/2023
我知道我可以使用通常的类实现来解决我的问题。也许我很懒惰,正在寻找一种简单明了的方法,因为使用它们清楚地说明了为什么我使用它们与普通课程不同。 另外,我知道这一点,并且更快,更容易制作。enumdataclassenumtuple
0赞 Yuri R 10/11/2023 #2

您可以使用 from 。fielddataclasses

@dataclass(frozen=True)  # Making the dataclass frozen makes its fields immutable.
class Pages:
    _last_page: int = field(repr=False)  # Private field, and won't be shown in repr.
    _first_page: int = field(default=0, repr=False)
    current_page: int = field(default_factory=int, hash=False)  # Making it mutable by setting hash to False.

    @property
    def first_page(self) -> int:  # Property for the first_page to allow only reading.
        return self._first_page

    @property
    def last_page(self) -> int:  # Property for the last_page to allow only reading.
        return self._last_page

测试

p = Pages(current_page=5, _last_page=10)
print(p.first_page)  # 0
print(p.current_page)  # 5
print(p.last_page)  # 10

# This will raise an exception: dataclasses.FrozenInstanceError
# p.first_page = 2

确保除非另有指定,否则属性保持不可变。frozen=True

评论

0赞 Abdulrahman Sheikho 10/11/2023
但是通过分配这个值会使整体不可变,并且尝试更改为另一个值会提高frozen=TruePagescurrent_pagedataclasses.FrozenInstanceError
0赞 link89 10/11/2023 #3

您可以使用类型批注。虽然它不会改变实际的运行时行为,但至少当你尝试覆盖该值时,你的类型检查器会引发错误。Final

from dataclasses import dataclass
from typing import Final

@dataclass(slots=True)
class Pages:
    current_page: int   # need to be initialized and mutable
    last_page: Final[int]      # need to be initialized and immutable
    first_page: Final[int] = 0 # need to be immutable