【问题标题】:Using Python descriptors: How best to write a general validation getter-setter使用 Python 描述符:如何最好地编写通用验证 getter-setter
【发布时间】:2013-09-26 00:43:29
【问题描述】:

最好,我的意思是 Python 和尽可能少的代码行。

我习惯于像这样在 Python 中定义具有属性的类。

class MyClass(object):

    def __init__(self):
        self.value = 5

然而,我经常发现自己需要确保self.value 只能采用特定类型的值。过去(来自 PHP OOP 背景)我使用类似于此的 getter-setter 方法完成了此操作:

    def value(self, new_value = -1)
        if new_value == -1:
            return self.value
        if isinstance(new_value, int) and new_value > -1:
            self.value = new_value
        else:
            raise TypeError

然后我使用self.value() 访问self.value,并使用self.value(67) 设置它。通常我会将self.value 命名为self.__value 以阻止其直接访问。

我最近发现(或正在发现)描述符。密切关注excellent discussion here,我写了一些看起来像这样的东西:

class ValidatedAttribute(object):

    def __init__(self, key, kind):
        self.key = key
        self.kind = kind

    def __get__(self, instance, owner):
        if self.key not in instance.__dict__:
            raise AttributeError, self.key
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        if not isinstance(value, self.kind):
            raise TypeError, self.kind
        instance.__dict__[self.key] = value     


class MyUsefulClass(object):

    a = ValidatedAttribute("a", int)

但我有几个问题!

首先,MyUsefulClass().a 到底是什么?它是 ValidatedAttribute 类的一个实例,以某种方式绑定到 MyUsefulClass 类的实例吗?

其次,我真正想要的是,

class MyUsefulClass(object):

    def __init__(self):
        self.value = Checker(int, 99)

    def Checker(self, kind, default):
        # Code for verifying that associated attribute is of type kind.

并以某种方式将属性访问绑定到某种拦截方法,而不必使用整个其他描述符类。

如果我的理解不足,我很抱歉 - 我仍在掌握新型 Python 类。任何正确方向的帮助或指示将不胜感激。

【问题讨论】:

    标签: python oop validation descriptor


    【解决方案1】:

    首先,MyUsefulClass().a 到底是什么?它是 ValidatedAttribute 类的一个实例,是否以某种方式绑定到 MyUsefulClass 类的一个实例?

    是的,就是这样。

    其次,我真正想要的是 [...] 以某种方式将属性访问绑定到某种拦截方法,而不必使用整个其他描述符类。

    好吧,你可以覆盖__getattribute__,因为它会拦截每个属性查找,但它比使用描述符要复杂得多。

    描述符完全符合您的要求(自动验证)。您可以做的唯一改进是还使用元类自动为您设置密钥,然后您的类代码如下所示:

    class MyUsefulClass(object):
    
        a = ValidatedAttribute(int)
    

    但首先要熟悉描述符,元类很复杂,很难理解。

    哦,我要对你的 ValidatedAttribute 做一个小改动:

    def __init__(self, key, default):
    
        self.key = key
        self.kind = type(default)
        self.default = default
    
    def __get__(self, instance, owner):
        if self.key not in instance.__dict__:
            return self.default
        return instance.__dict__[self.key]
    

    【讨论】:

    • 我选择了这个答案,因为它把我介绍给了__getattribute__,这是我以前不知道的。我不认为这是最优雅的解决方案,但覆盖 __getattribute__ 肯定是解决我的问题的最通用和最不臃肿的解决方案,至少在我迄今为止找到的那些解决方案中。
    【解决方案2】:

    请,请,请不要这样做isinstance(new_value, int)

    可能可能侥幸逃脱

    from numbers import Integral
    isinstance(new_value, Integral)
    

    但拒绝打字完全是一种诅咒。

    首先,MyUsefulClass().a 到底是什么?它是否是一个 ValidatedAttribute 类的实例以某种方式绑定到 MyUsefulClass 类的一个实例?

    为了找到MyUsefulClass().a,Python 询问班级。该类告诉它它有一个属性的getter-setter,并告诉Python询问该属性。属性说找到它,然后告诉类该属性是什么。该属性不是 getter-setter。

    MyUsefulClass.a 是 getter-setter(属性),而不是 MyUsefulClass().a

    其次,我真正想要的是 [stuff] 之类的东西

    AFAIK,您只能在类上设置属性,而不是在实例上,因为您怎么知道您是要检索属性还是要检索属性阴影的项目?

    【讨论】:

    • 有时duck-typing 不起作用:与其他语言、系统、数据库等交互。这就是我们有isinstance 的原因。并且代码运行良好,您只需在尝试访问它之前分配一个值。
    • "代码运行良好,您只需在尝试访问之前分配一个值。" Teehee,你不会相信我为了得出我的结论而搞砸的;)。是的,不管那个。
    【解决方案3】:

    不要自己编写这些东西,而是使用特征库:https://pypi.python.org/pypi/traits。使用库是解决大多数问题的 Python 方法。

    但是,一般来说,您应该编写足够通用的代码,以至于这种检查在很大程度上是不必要的。在python中很容易做到。

    【讨论】:

      【解决方案4】:

      我认为您正在寻找的是 python 属性。 Correct approach to validate attributes of an instance of class 似乎与您的问题有关。你也可以看看Python descriptor vs property

      【讨论】:

        【解决方案5】:

        听起来你想使用属性

        class MyUsefulClass(object):
        
            def __init__(self):
                self._value = 0
        
            @property
            def value(self):
                return self._value
        
            @value.setter
            def value(self, value):
                self._value = self.Checker(value, int, 99)
        
            def Checker(self, value, kind, default):
                return value if isinstance(value, kind) else default
        

        【讨论】:

        • 属性是我考虑过的,但我对它们的问题是必须为每个要以这种方式验证的属性显式定义 getter 和 setter 函数。它或多或少与为每个“私有”属性编写单个 getter-setter 函数一样冗长,这就是我目前所做的。 (例如,写def myattr(self, value) 来获取和设置self.__myattr。)
        猜你喜欢
        • 2012-07-06
        • 1970-01-01
        • 2021-09-20
        • 2020-04-15
        • 1970-01-01
        • 2016-07-20
        • 2016-11-30
        • 2019-01-07
        • 2020-07-01
        相关资源
        最近更新 更多