代码之家  ›  专栏  ›  技术社区  ›  SultanOrazbayev

如何正确定义访问子属性值的类方法?

  •  1
  • SultanOrazbayev  · 技术社区  · 1 年前

    在Python中,如何正确定义引用子类属性的父类的类方法?

    from enum import Enum
    
    
    class LabelledEnum(Enum):
        @classmethod
        def list_labels(cls):
            return list(l for c, l in cls.__labels.items())
    
    
    class Test(LabelledEnum):
        A = 1
        B = 2
        C = 3
    
        __labels = {
            1: "Label A",
            2: "Custom B",
            3: "Custom label for value C + another string",
        }
    
    
    print(Test.list_labels())
    # expected output
    # ["Label A", "Custom B", "Custom label for value C + another string"]
    

    在上面的代码中,我希望 Test.list_labels() 将正确打印出标签,但是因为 __labels 字典是用双下划线定义的,我无法正确访问它。

    我想要使用双下划线的原因是为了确保在遍历枚举器时标签不会显示,例如list(Test)不应显示包含标签的字典。

    2 回复  |  直到 1 年前
        1
  •  2
  •   Jonny Henly Anton Gogolev    1 年前

    你可以做一些非常丑陋的事情,比如:

    getattr(cls, f"_{cls.__name__}__labels", {})
    

    不过,我不确定它是否能保证在任何情况下都有效。

    from enum import Enum
    
    
    class LabelledEnum(Enum):
        @classmethod
        def list_labels(cls):
            # account for private name mangling
            labels = getattr(cls, f"_{cls.__name__}__labels", {})
            return list(l for c, l in labels.items())
    
        2
  •  2
  •   juanpa.arrivillaga    1 年前

    如果您使用Python,您的最佳选择是>=3.11是使用 enum.nonmember 和一个 单个下划线 :

    In [8]: import enum
       ...:
       ...: class LabelledEnum(enum.Enum):
       ...:     @classmethod
       ...:     def list_labels(cls):
       ...:         return list(l for c, l in cls._labels.items())
       ...:
       ...:
       ...: class Test(LabelledEnum):
       ...:     A = 1
       ...:     B = 2
       ...:     C = 3
       ...:
       ...:     _labels = enum.nonmember({
       ...:         1: "Label A",
       ...:         2: "Custom B",
       ...:         3: "Custom label for value C + another string",
       ...:     })
       ...:
    
    In [9]: list(Test)
    Out[9]: [<Test.A: 1>, <Test.B: 2>, <Test.C: 3>]
    
    In [10]: Test.list_labels()
    Out[10]: ['Label A', 'Custom B', 'Custom label for value C + another string']
    
    如果您至少使用Python 3.7,则可以使用特定于枚举的“sunder”名称,并将“_lables”添加到忽略列表中:
    class Test(LabelledEnum):
        A = 1
        B = 2
        C = 3
        _ignore_ = ["_labels"]
        _labels = {
            1: "Label A",
            2: "Custom B",
            3: "Custom label for value C + another string",
        }
    
    另一种方法是动态构建字符串,这相当笨拙,尽管它会起作用(坦率地说,“enum”应该忽略*single*下划线,而不是双下划线,但唉):
    @classmethod
    def list_labels(cls):
        labels = getattr(cls, f"_{cls.__name__}__labels"
        return list(labels.values())
    
        3
  •  0
  •   Ethan Furman    1 年前

    getattr 方法有效,一个更优雅的解决方案是 LabelledEnum 一个mix-in,并使用以下值定义标签:

    from enum import Enum
    
    class LabelledEnumMixin:
    
        labels = {}
    
        def __new__(cls, value, label):
            member = object.__new__(cls)
            member._value_ = value
            member.label = label
            cls.labels[value] = label
            return member
    
        @classmethod
        def list_labels(cls):
            return list(l for c, l in cls.labels.items())
    
    
    class Test(LabelledEnumMixin, Enum):
        A = 1, "Label A"
        B = 2, "Custom B"
        C = 3, "Custom label for value C + another string"
    

    披露:我是的作者 Python stdlib Enum 这个 enum34 backport ,以及 Advanced Enumeration ( aenum ) 图书馆