描述符简单的来说就是一个类。只不过他定义了另一个类中属性的访问方式。也就是说一个类可以将属性管理权委托给描述符类。
在每次查找属性时。描述符协议中的方法都由类对象的特殊方法__getattribute__()调用。也就是说每一次使用类对象.属性的调用方式时,都会隐式的调用__getattribute__(),他分如下顺序查找属性。
class Person:
def __init__(self, name=None, age=23):
self.name = name
self.age = age
def __get__(self, instance, owner):
return self.name
def __set__(self, instance, value):
self.age = value
class Person1:
a = Person('小王', 30)
b = 100
d = Person1
print("这里执行__get__方法:", d.a)
d.a = 1000000
print("这里执行赋值操作:", d.a)
这里执行__get__方法: 小王
这里执行赋值操作: 1000000
不难看出如果一个类的属性具有数据描述符,在每次查询这个属性时都会调用描述符的__get__()方法返回他的值。 每次对这个属性赋值时也会调用__set__()方法
通过类对象.属性访问定义在类中的属性。其实这种做法是欠妥的。他破坏了类的封装原则。正常情况下类包含的属性应该是隐藏的。只允许通过类提供的方法来间接的实现对类属性的访问和 操作。所以在不破坏类封装原则的基础上为了能够有效操作类中的属性。类中应包含多个读或写的getter或setter方法,这样就可以通过类对象.方法(参数)的方式操作属性。
class Person:
def __init__(self, name):
self.name = name
def getName(self):
return self.name
def setName(self, name):
self.name = name
a = Person("小王")
print(a.getName())
a.setName("小张")
print(a.getName())
小王
小张
由于这种方式过于繁琐。所以python提供了property()函数。可以实现不破坏类封装的前提条件下,可以让开发者依旧使用类对象.属性的方式操作类中的属性。
class Person:
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
def setName(self, name):
self.__name = name
def deleteName(self):
self.__name = '---'
name = property(getName, setName, deleteName, '这个函数的作用时操作name属性')
help(Person.name)
a = Person("四川")
# 访问getName
print(a.name)
# 访问setName
a.name = "甘肃"
print(a.name)
# 访问deleteName
del a.name
print(a.name)
Help on property:
这个函数的作用时操作name属性
四川
甘肃
---
第一个参数表示读取该属性值的方法。第二个参数表示设置该属性的方法。第三个参数表示删除该属性的方法。第四个参数表示函数的描述。而且这四个参数可以指定1个或者2个或者3个或者全部。 即参数的指定不是随意的。
需要说明的是。由于getName()要返回name的属性。如果使用self.name的话。其实本身有在调用getName()。这就出出现无限循环。 为了避免这种情况 。name属性必须设置为私有属性(后续详解)。即使用__name(两个下划线)。