Python类属性验证__

2024-09-29 01:21:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在下面的代码中使用setter验证类的一个属性。我要验证的属性名为''uux',并设置为通过'init'方法传递的参数。当我期待自我改变的时候。我想知道的是它如何与“self.x”一起工作,而在getter和setter方法中的任何地方都不返回“x”属性,以及为什么它不能与“self.\ux”一起工作?在

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

Tags: 方法代码self参数属性initdef地方
2条回答

是这样的。想象一下有一个学校里的恶霸,我们叫他丹,目标是你。还有贝丝,你很喜欢她。正常情况下,你想避开丹和贝丝见面,但丹不在乎,如果他看到你,他会狠狠揍你一顿。在

现在你也和乔交朋友了。他是个温柔的巨人。非常好的人。他说到他家来,他一定不让丹进来。这一切都很好:当丹来到乔的门口时,他已经转过身去;当贝丝来了,乔让她进来。在

关键的一点是:只要丹开门,它就有效。如果你听到门铃,你自己出去,它就不起作用了。在


所以在这里,如果你要self.x = -5,Joe会检查数字,看到是Dan,然后给他发送一个0。但如果你这么做了,乔就再也见不到丹了。你头上挨了一拳。在

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

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

@decorate
def myfunc():
    pass

只是一个简写

^{pr2}$

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

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

一个简单的python实现property通常类似于(我忽略了fdel和{}部分):

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对象主要是伪装的dict,因此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)(cfself.fset(p, 5)(cfproperty.__set__()定义),它将调用p._setx(5)。在

如果我们拿回初始值:

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

    # getter / setter / property definition  here

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

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

相关问题 更多 >