class Property:
def __init__(self,fget=None, fset=None, fdel=None):
self._fget = fget
self._fset = fset
self._fdel = fdel
# 这是实例调用的装饰器@obj.setter
def setter(self, fn=None):
print("Property setter", fn)
self._fset = fn
return self
# 这是实例调用的装饰器@obj.deleter
def deleter(self, fn=None):
print("Property deleter",fn)
self._fdel = fn
return self
# obj.name 中的 . 将触发 这个方法
def __get__(self, instance, owner):
return self._fget(instance)
# obj.name = value 中的 = 触发 __set__
def __set__(self, instance, value):
self._fset(instance, value)
# del obj.name del触发 __delete
def __delete__(self, instance):
# 描述器初始化,del触发__delete__(),然后再 来 调用对应的属性方法
self._fdel(instance)
# 测试类
class Test:
def __init__(self, age=18):
self.__age = age
self._city = 'beijing'
@Property # city = Property(city) ,这里触发的是装饰的__init__(),相当于是一个建立一个类属性的Propery实例对象,也就是描述器
def city(self):
return self._city
@city.setter # 这里触发的是装饰的 setter()方法,装饰器是需要返回一个值的
def city(self, city):
self._city = city
@city.deleter # city = city.deleter(city) => Property(city).deleter(city)
def city(self):
print("Property delete city:{}".format(self._city))
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def del_age(self):
print('Property delete age:{}'.format(self.__age))
age = Property(get_age, set_age, del_age)
# ===========================
t = Test()
# 属性装饰器测试---> city
print(getattr(t, "_Test__age"), getattr(t, '_city'))
print("=-+"*20)
print(t.__dict__)
print(t.city)
t.city = "shanghai"
print(t.__dict__)
print(t.city)
del t.city
# 属性装饰器测试---> city
print("=-+"*20)
print(t.age)
t.age = 29
print(t.age)
print(t.__dict__)
del t.age
【执行结果】
Property setter <function Test.city at 0x0000000000D80E18>
Property deleter <function Test.city at 0x0000000000D80EA0>
18 beijing
=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+
{'_Test__age': 18, '_city': 'beijing'}
beijing
{'_Test__age': 18, '_city': 'shanghai'}
shanghai
Property delete city:shanghai
=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+=-+
18
29
{'_Test__age': 29, '_city': 'shanghai'}
Property delete age:29
【程序执行总结】
编译运行后,
先初始化 类中装饰包装的类方法,构建包装的新方法。
同时绑定了类变量的装饰器实例化对象,也就构建了数据描述器。
当运行到实例属性的类同名属性的相关操作时,触发描述器的相关方法,
obj.name => __get__()
obj.name = value => __set__()
del obj.name => __delete__()