使用Python描述符:如何最好地编写通用验证getter setter

使用Python描述符:如何最好地编写通用验证getter setter,python,oop,validation,descriptor,Python,Oop,Validation,Descriptor,我所说的“最佳”是指以pythonical方式和尽可能少的代码行 我习惯于用Python中的属性定义类,就像这样 class MyClass(object): def __init__(self): self.value = 5 然而,我经常发现自己需要确保self.value只能接受特定类型的值。在过去(来自PHP OOP背景),我使用了类似于以下内容的getter setter方法: def value(self, new_value = -1)

我所说的“最佳”是指以pythonical方式和尽可能少的代码行

我习惯于用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)
设置它。通常我会将mangle
self.value
命名为
self.\uu value
以阻止其直接访问

我最近发现了(或正在发现)描述符。紧随其后,我写了如下内容:

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)
class MyUsefulClass(object):

    a = ValidatedAttribute(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属性。似乎和你的想法有关。您还可以查看

听起来您想使用属性

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

请,请,请不要执行
isinstance(新值,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

第二,我真正想要的是[东西]

好的,您只能在类上设置属性,而不能在实例上设置属性,因为您如何知道是要检索属性还是要检索属性阴影中的项

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

是的,就是这样

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

好的,您可以覆盖
\uuu getattribute\uuuu
,就像它在每个属性查找中所做的那样,但是它比使用描述符复杂得多

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

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)
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]

与其自己写这些东西,不如使用traits库:。使用库是解决大多数问题的python方法


然而,一般来说,您应该编写足够通用的代码,以使这种检查基本上是不必要的。用python很容易做到这一点

有时duck类型不起作用:与其他语言、系统、数据库等交互。这就是为什么我们有
isinstance
。代码运行良好,您只需在尝试访问它之前分配一个值。“代码运行良好,您只需在尝试访问它之前分配一个值。”Teehee,您不会相信我为了得出结论而搞砸了什么;)。是的,忽略这一点。属性是我考虑过的东西,但我的问题是,它们必须以这种方式为每个要验证的属性显式定义getter和setter函数。这或多或少就像为每个“private”属性编写一个getter setter函数一样冗长,这就是我目前所做的。(也就是说,编写
def myattr(self,value)
来获取和设置
self.\uu myattr
)我选择这个答案是因为它向我介绍了
\uuu getattribute\uuuuuu
,而我以前并不知道。我不认为这是最优雅的解决方案,但覆盖
\uuuu getattribute\uuuu
肯定是解决我的问题最普遍、最不臃肿的解决方案,至少在我迄今为止找到的解决方案中是如此。