__init__上的Python类属性验证

时间:2018-07-05 09:04:34

标签: python oop getter-setter

我正在尝试使用以下代码中的setter来验证类的一个属性。我要验证的属性称为“ __x”,并设置为在“ init”方法中传递的参数。当我将'self__x'更改为'self.x'时,它按预期运行。我想要的是它如何与'self.x'一起使用,而在getter和setter方法中的任何地方都没有返回'x'属性,为什么它不能与'self .__ x'一起使用?

class P:
    def __init__(self, x):
        self.__x = x  # not working
        # self.x = x  # working

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        else:
            self.__x = x


p = P(-5)
print(p.x)  # prints -5

2 个答案:

答案 0 :(得分:0)

就是这样。想象一下,有一个学校欺负者,我们称他为Dan,以您为目标。还有贝丝,你非常喜欢他。通常,您想避开Dan并与Beth见面,但Dan不在乎,如果看到您,就会将您甩在头上。

现在,您还与Joe交了朋友。他是一个温柔的巨人。很好他说要到他的位置来,并且要确保他不会让Dan进入。一切都很好:Dan来到Joe的家时,他转身离开了;当贝丝来时,乔让她进来。

关键点是:仅在Dan打开门时它才起作用。如果您听到门铃,然后出门自己,它就不起作用了。


因此,在这里,如果您执行self.x = -5,则Joe检查该数字,看到它是Dan,然后将其打包并寄给他零。但是,如果您做self.__x = -5,乔就永远不会见丹。你头上跳了个屁。

self.__x只是一个变量,它不能自己进行任何检查。 self.x是一个函数(实际上有两个,一个用于阅读,一个用于编写),它可以做任何想要的事情-设置self.__x或拒绝。

答案 1 :(得分:0)

让我们从“ @decorator”语法开始。实际上只是语法糖,所以

@decorate
def myfunc():
    pass

只是速记

def myfunc():
    pass

myfunc = decorate(myfunc)

请注意,python函数也是对象(以及FWIW的类和模块),因此您可以将函数作为参数传递给其他函数,从函数返回函数,将函数存储为变量或属性等。

现在有了property类(是的,这是一个类):它只是the descriptor protocol的通用实现,它是支持计算属性的python机制。

property的朴素python实现看起来像(我忽略了fdel__del__部分):

class propertytype(type):
    # this is what will allow you 
    # to use `property` as decorator,
    # it will return a new `property` instance
    # with `func` as setter
    def __call__(cls, func):
        return cls(func)

class property(metaclass=propertytype):

    def __init__(self, fget, fset=None):
        self.fget = fget
        self.fset = fset

   # this is the getter
   def __get__(self, instance, cls=None):
       if instance is None:
           return self
       return self.fget(instance)

   # this is the setter (if there's one)
   def __set__(self, instance, value):
       if not self.fset:
           raise AttributeError("Attribute is read-only")
       self.fset(instance, value)

   # and this allows you to use`@myprop.setter` 
   # in your class definition
   def setter(self, func):
       self.fset = func
       return self

最后:虽然在初始化程序中创建对象的所有实例属性(__init__方法)是一种好习惯,但实际上,您可以随时随地设置现有属性或新属性。除了少数几种类型(主要是出于实现原因)使用完全不同的方式存储属性(如果您想了解更多有关此信息的信息,可以寻找slots),普通的Python对象主要是伪装的字典。 ,因此myobj.foo = 'bar'通常只会将'bar'存储在self.__dict__['foo']中。好吧,如果您不使用计算属性,当然;)

好吧,现在我们有了构建块,让我们分析一下您的班级正在发生什么:

class P:
    # let's ignore the initializer for now

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        else:
            self.__x = x

这可以改写为

class P:
    # let's ignore the initializer for now

    def _getx(self):
        return self.__x

    def _setx(self):
        if x < 0:
            self.__x = 0
        else:
            self.__x = x

    x = property(_getx, setx)

现在使用

p = P()

当我们这样做时:

p.x = 5

属性解析规则(在object.__setattr__(self, name, value)中实现)实际上将在“ P”上查找“ x”,找到我们的“ x”属性,并且由于它是绑定描述符(它具有__set__方法),调用x.__set__(p, 5),依次调用self.fset(p, 5)(参见property.__set__()定义),该调用将调用p._setx(5)

如果我们返回了初始化程序:

class P:
    def __init__(self, x):
        self.x = x

    # getter / setter / property definition  here

然后发生非常精确的事情(除了P实例被命名为self而不是p之外),它实际上最终调用了P._setx(self, x)

与原始实现的唯一区别在于,使用property具有修饰符,getter和setter函数不会成为类的方法,它们只能作为fget和{{1 fset属性对象的}}属性。