python @property setter/deleter
获取/更新对象属性:getattr/setattr,但是没有办法检查检查参数,导致可以任意设置对象属性.
为了解决这个问题,引入property:
Python内置的@property装饰器就是负责把一个方法变成属性调用
用property来获取/更新/删除属性
class property(object):
"""
Property attribute.
fget
function to be used for getting an attribute value
fset
function to be used for setting an attribute value
fdel
function to be used for del'ing an attribute
doc
docstring
Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
"""
def deleter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the deleter on a property. """
pass
def getter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the getter on a property. """
pass
def setter(self, *args, **kwargs): # real signature unknown
""" Descriptor to change the setter on a property. """
pass
使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
由于python进行属性的定义时,没办法设置私有属性,因此要通过@property的方法来进行设置。这样可以隐藏属性名,让用户进行使用的时候无法随意修改。
class DataSet(object):
def __init__(self):
self._images = 1
self._labels = 2 # 定义属性的名称
@property
def images(self): # 方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户没办法随意修改。
return self._images
@property
def labels(self):
return self._labels
l = DataSet()
# 用户进行属性调用的时候,直接调用images即可,而不用知道属性名_images,因此用户无法更改属性,从而保护了类的属性。
print(l.images) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()
注意事项:
1.定义的方法名称必须要是变量名(如属性名是name,那函数名称就为name,不能为其他的)
2.属性的方法名不要和实例变量重名
错误示例1:方法名和实例变量重名
class Test(object):
@property
def name(self):
return self.name
@name.setter
def name(self, value):
print("seting")
if len(value) > 4:
print("value error")
exit(1)
else:
self.name = value
@name.deleter
def name(self):
del self.name
if __name__ == '__main__':
tes = Test()
print(getattr(tes, "name", None))
tes.name = "Lisa"
print(getattr(tes, "name", None))
del tes.name
print(getattr(tes, "name", None))
tes.name = "LisaA"
# RecursionError: maximum recursion depth exceeded
这是因为在 name 属性的 setter 方法中,对属性 self.name 进行了赋值,这会再次调用 setter 方法,从而陷入无限循环递归调用的状态,最终导致了栈溢出,报错RecursionError
修改代码:
class Test(object):
@property
def name(self):
return self._name
@name.setter
def name(self, value):
print("seting")
if len(value) > 4:
print("value error")
exit(1)
else:
self._name = value
@name.deleter
def name(self):
del self._name
if __name__ == '__main__':
tes = Test()
print(getattr(tes, "name", None))
tes.name = "Lisa"
print(getattr(tes, "name", None))
del tes.name
print(getattr(tes, "name", None))
tes.name = "LisaA"
# None
# seting
# Lisa
# None
# seting
# value error
错误示例2:要修改的变量名和方法名不一致,在执行修改操作的时候,并没有调用 @get_value.setter,get_value 方法只有一个 getter,但在代码中使用了 setter 和 deleter 装饰器来定义它们,应该将它们的名称修改为 get_value
class Test(object):
def __init__(self):
self.name = "Tom"
self.age = 20
@property
def get_value(self):
return self.name, self.age
@get_value.setter
def set_value(self, age):
print("seting")
print(age < 100)
if age > 0 or age < 100:
self.age = age
else:
raise ValueError('age must between 0 and 100!')
@get_value.deleter
def del_value(self, value):
print("deleting")
if getattr(self, value, None):
delattr(self, value)
if __name__ == '__main__':
tes = Test()
print(tes.get_value)
tes.name = "Lisa"
tes.age = 130
print(tes.get_value)
del tes.name
print(getattr(tes, "name", None), getattr(tes, "age", 0))
# ('Tom', 20)
# ('Lisa', 130)
# None 130
修改代码:
class Test(object):
def __init__(self):
self.name = "Tom"
self.age = 20
@property
def get_value(self):
return self.name, self.age
@get_value.setter
def get_value(self, age):
print("setting")
if age >= 0 and age <= 100:
self.age = age
else:
raise ValueError('age must be between 0 and 100!')
@get_value.deleter
def get_value(self):
print("deleting")
if getattr(self, 'name', None):
delattr(self, 'name')
if getattr(self, 'age', None):
delattr(self, 'age')
if __name__ == '__main__':
tes = Test()
print(tes.get_value)
tes.name = "Lisa"
tes.age = 130
print(tes.get_value)
del tes.name
print(getattr(tes, "name", None), getattr(tes, "age", 0))
设置多个参数值:
class Test(object):
def __init__(self):
self._value1 = "Tom"
self._value2 = "Lisa"
@property
def values(self):
return self._value1, self._value2
@values.setter
def values(self, values):
print("seting")
if len(values) == 2:
self._value1, self._value2 = values
else:
raise ValueError('Two parameters are required.!')
@values.deleter
def values(self):
print("deleting")
del self._value1
del self._value2
if __name__ == '__main__':
tes = Test()
print(tes.values)
tes.values = "KS1", "KS2"
print(tes.values)
del tes.values
print(getattr(tes, "_value1", None), getattr(tes, "_value1", 0))
tes.values = "A1", "A2", "A3"
print(tes.values)
# ('Tom', 'Lisa')
# seting
# ('KS1', 'KS2')
# deleting
# None 0
# seting
# ValueError: Two parameters are required.!