通常会将类的成员变量称为属性,在创建类实例后,可以通过类实例访问这些属性,也就是读写属性的值。不过直接在类中定义成员变量,尽管可以读写属性的值,但无法对读写的过程进行监视。例如,在读取属性时无法对属性值进行二次加工,在写属性值时无法校验属性值是否有效。在Python语言中可以通过property函数解决这个问题,该函数可以将一对方法与一个属性绑定,当读写该属性值时,就会调用相应的方法进行处理。
property函数可以与三个方法绑定,该函数会创建一个属性,并通过返回值返回这个属性。property函数的第1个参数需要指定用于监控读属性值的方法,第2个参数需要指定用于监控写属性值的方法,第3个参数需要指定删除该属性时调用的方法。
class Rectangle:
def __init__(self):
super().__init__()
self.left = 0
self.right = 0
# 用于监控position属性的写操作,可以同时设置left属性和top属性
def setPosition(self, position):
print('setPosition属性已被调用')
self.left, self.top = position
# 用于监控position属性的读操作,可以同时获取left属性和top属性
def getPosition(self):
print('getPosition属性已被调用')
return self.left, self.top
# 用于监控position属性的删除操作
def deletePosition(self):
print('position属性已被删除')
# 重新初始化left和top属性
self.left = 0
self.right = 0
# 通过property函数将上面3个方法与position属性绑定,对position属性进行相关操作时会调用相关的方法
position = property(getPosition, setPosition, deletePosition)
r = Rectangle()
r.left = 19
r.top = 39
print('left', '=', r.left)
print('top', '=', r.top)
# 通过position属性获取left属性和top属性的值,在获取属性值的过程中,getPosition方法被调用
r.position = 100, 200
# 通过position属性设置left属性和top属性的值,在设置属性值的过程中,setPosition方法被调用
print('position', '=', r.position)
# 删除position属性,deletePostion方法被调用,left属性和top属性被重新设置为0
del r.position
print(r.position)
r.position = 20, 40
print('r.position', '=', r.position)
输出结果:
left = 19
top = 39
setPosition属性已被调用
getPosition属性已被调用
position = (100, 200)
position属性已被删除
getPosition属性已被调用
(0, 200)
setPosition属性已被调用
getPosition属性已被调用
r.position = (20, 40)
习惯上将与getPosition和setPosition类似的方法称为getter方法和setter方法。
注意
1.通过property函数设置的与属性绑定的方法的名称没有任何限制,例如,方法名可以为ab,xy都可以。但是,方法的参数需要符合要求。也就是说,用于监控属性读和删除属性操作方法值能有一个self参数,用于监控属性写操作的方法除了self,还需要一个参数,用于接收设置属性的值。
2.删除对象的属性只是调用了通过property函数绑定的回调方法,并没有真正删除对象的属性。删除对象属性的实际意义,需要在该回调方法(本例中deleteProperty)中定义。本例中在deleteProperty方法中初始化了left属性和top属性的值。
class Rectangle:
def __init__(self):
super().__init__()
self.left = 0
self.right = 0
self.width = 0
self.height = 0
# 对属性执行写操作时调用该方法,当设置size属性和position属性时实际上设置了width属性和height属性,以及left属性和top属性的值
def __setattr__(self, name, value):
print("{}被设置,新值为{}".format(name, value))
if name == 'size':
self.width, self.height = value
elif name == 'position':
self.left, self.top = value
else:
# __dict__是内部维护的一个特殊成员变量,用于保存成员变量的值,所以这条语句必须加上
self.__dict__[name] = value
return super().__setattr__(name, value)
# 对属性执行读操作时调用该方法,当读取size属性和position属性值时实际上返回的是width属性,height属性和left属性,top属性
def __getattr__(self, name):
print("{}被获取".format(name))
if name == "size":
return self.width, self.height
elif name == "position":
return self.left, self.top
# 当删除属性时调用该方法,当删除size属性和position属性时,实际上是重新将width属性,height属性和left属性,top属性设置为0
def __delattr__(self, name):
if name == 'size':
self.width, self.height = 0, 0
elif name == 'position':
self.left, self.top = 0, 0
r = Rectangle()
# 设置size属性的值
r.size = 300, 500
# 设置position属性的值
r.position = 100, 400
# 获取position属性的值
print('position', '=', r.position)
# 获取size属性的值
print('size', '=', r.size)
# 删除size属性和position属性
del r.size, r.position
print(r.size)
print(r.position)
输出结果:
left被设置,新值为0
right被设置,新值为0
width被设置,新值为0
height被设置,新值为0
size被设置,新值为(300, 500)
width被设置,新值为300
height被设置,新值为500
position被设置,新值为(100, 400)
left被设置,新值为100
top被设置,新值为400
position = (100, 400)
size = (300, 500)
width被设置,新值为0
height被设置,新值为0
left被设置,新值为0
top被设置,新值为0
(300, 500)
(100, 400)
注意
在Rectangle类的__setattr__方法中使用了一个__dict__成员变量,这是系统内置的成员变量,用于保存对象中所有属性的值。如果不在类中定义__setattr__方法,则系统默认在设置值时将这些值都保存在__dict__变量中,但是,如果定义了__setattr__方法,则要靠自己将这些属性的值保存到字典__dict__中。