以下是python学习手册P973\974的代码。
其中CardHolder.init,为什么self.name=name,会调用到Name.set去?
class Name:
def __get__(self, instance, owner): # Class names: CardHolder locals
print('get', 'name')
return self.name
def __set__(self, instance, value):
value = value.lower().replace(' ', '_')
self.name = value
print('set',id(self.name))
class CardHolder:
acctlen = 8 # Class data
retireage = 59.5
def __init__(self, acct, name, age, addr):
self.acct = acct # Instance data
self.name = name # These trigger __set__ calls too。此处为何会调用Name.__set__()???
self.age = age # __X not needed: in descriptor
self.addr = addr # addr is not managed
# remain has no data
name = Name() #是类CardHolder的属性,可直接访问
class Age:
def __get__(self, instance, owner):
return self.age # Use descriptor data
def __set__(self, instance, value):
if value < 0 or value > 150:
raise ValueError('invalid age')
else:
self.age = value
age = Age()
class Acct:
def __get__(self, instance, owner):
return self.acct[:-3] + '***'
def __set__(self, instance, value):
value = value.replace('-', '')
if len(value) != instance.acctlen: # Use instance class data
raise TypeError('invald acct number')
else:
self.acct = value
acct = Acct()
class Remain:
def __get__(self, instance, owner):
return instance.retireage - instance.age # Triggers Age.__get__
def __set__(self, instance, value):
raise TypeError('cannot set remain') # Else set allowed here
remain = Remain()
bob = CardHolder('1234-5678', 'Bob Smith', 40, '123 main st')
print(bob.acct, bob.name, bob.age, bob.remain, bob.addr, sep=' / ')
bob.name = 'Bob Q. Smith'
bob.age = 50
bob.acct = '23-45-67-89'
print(bob.acct, bob.name, bob.age, bob.remain, bob.addr, sep=' / ')
sue = CardHolder('5678-12-34', 'Sue Jones', 35, '124 main st')
print(sue.acct, sue.name, sue.age, sue.remain, sue.addr, sep=' / ')
try:
sue.age = 200
except:
print('Bad age for Sue')
try:
sue.remain = 5
except:
print("Can't set sue.remain")
try:
sue.acct = '1234567'
except:
print('Bad acct for Sue')
解答:
1、虽然在类中对任何属性(存在或者不存在)self.x的赋值,都会创建或修改实例属性。
2、但是当有描述符Name()存在时,init中self.name相当于测试代码中外部访问的bob.name,两句中正好self==bob,为何说两者相等呢?init初始化函数传入的self不就是bob吗,这地方不明白的请查看学习手册中运算符重载-构造函数的相关章节。
3、在等效处理后(self.name==bob.name),对于self.name=name这样的赋值语句,就理所当然被描述符Name()的set()所拦截并处理。(此处类似于getattribute会对任何属性读取的拦截,但是是被描述符所拦截)。
4、按上述理解,便会有另外一个疑问,如果name不是个描述符实例,而是个普通的类属性时,self.name不也是==bob.name吗,那bob.name不就应该 是类属性了,而不是实例属性?此处应该如果理解,因为普通变量的操作是不会有描述符那样的拦截动作,所以最终处理是按第1点那样。
5、所以self.name不仅仅是实例属性,更应该理解是“”能被描述符所拦截处理的实例属性“。但是本例子的最终结果是存在描述符状态中,而不是实例属性中。
6、总结:在类中访问描述符实例的状态的方式就是采用self.name和CardHolder.name这样的语句,而不是name。
此文属个人理解,有何不妥之处,请改正。