Python 静态不可变属性

Python static immutable properties

在Python中实现静态不可变属性的正确方法是什么?

最小示例:

一个程序模块'Family'有一个classParent,定义如下:

class Parent():
    def __init__(self, type):
        self.type = type

父项可以是以下两种字符串类型之一:'mother''father'。这个class的外部用户应该可以在构造的时候设置类型,然后查询parent.type得到这两个值中的一个。此外,Family 模块的其他部分使用 Parent class 并依赖于返回这两个值中的任何一个。因此,对类型的要求如下:


一种天真的方法可以鼓励通过传递字符串来设置类型:

parent = Parent('mother')

但这允许意外拼写错误(例如 Parent('omther'))。为了防止这种情况,我使用以下方法:

class Parent():
    TYPE_MOTHER = 'mother'
    TYPE_FATHER = 'father'
    TYPES = [TYPE_MOTHER, TYPE_FATHER]

    def __init__(self, type):
        assert type in self.TYPES, ('Invalid type "%s"' % type)
        self.type = type

parent = Parent(Parent.TYPE_MOTHER)

但是,没有什么可以阻止用户随意更改这些静态变量,例如:

parent = Parent('mother')
Parent.TYPE_MOTHER = 'burrito'
print(parent.type == Parent.TYPE_MOTHER)
#> False

为了解决这个问题,我考虑使用 @staticmethod@property 注释:

class Parent():
    @property
    @staticmethod
    def TYPE_MOTHER():
        return 'mother'

    @property
    @staticmethod
    def TYPE_FATHER():
        return 'father'

这不会阻止用户将字符串传递给构造函数(例如 Parent('mother')),但至少可以防止他们通过更改父类型来搞砸 Family 模块。


我在使用此方法时遇到的问题:

给你的问题:

这不就是类的目的吗。只需 MotherFather 类 继承自:

class Parent():
    def __init__(self, type):
        self.type = type

class Mother(Parent):
    def __init__(self):
        super().__init__('mother')

class Father(Parent):
    def __init__(self):
        super().__init__('father')

parent = Mother()

总的来说这个方法很好,就是有点长。如果您不想实现继承,最短的方法是使 TYPES 列表成为受保护的 class 变量:

class Parent():
    def __init__(self, type):
        self._TYPES = ['mother', 'father']
        assert type in self._TYPES, ('Invalid type "%s"' % type)
        self.type = type

我为我的问题找到了一个简洁的答案 - Enum class (doc).

from enum import Enum
class Parent():
    class TYPES(Enum):
        MOTHER = 'mother'
        FATHER = 'father'

    def __init__(self, type:TYPES):
        assert type in self.TYPES, ('Invalid type "%s"' % type)
        self.type = type

parent = Parent(Parent.TYPES.MOTHER)

在这种情况下,用户仍然可以覆盖 Parent.TYPES。除了使用 __setattr__ 并捕获恶意写入之外,我想不出另一种方法来防止它。如果您能在这里想到什么,请分享您的想法。

这种模式具有以下优势:

  • 由于 Enum 的政策,无法覆盖单独的属性 MOTHER 和 FATHER
  • 由于 __setattr__
  • ,无法覆盖 TYPES
  • 无法使用原始字符串代替 Parent.TYPES.MOTHER(或 .FATHER)
  • 兼容assert type in self.TYPESassert isinstance(type, self.TYPES)

到目前为止,我可以看出两个用法上的差异:

  • 如果我想获得实际值 - 即。 'mother' 或 'father',那么我需要使用枚举的 .value 属性。因此,我需要使用 parent.type 而不是 parent.type(return "TYPES.MOTHER"),我需要使用 parent.type.value 正确 returns 'mother'.
  • 要打印 TYPES 的内容我需要使用 list(self.TYPES)

我认为使用 Enum 我也可以放弃 ALL_CAPS 标准,因为我不需要表示 types 是静态的。