我正在尝试使用以下代码中的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
答案 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
属性对象的}}属性。