Python描述符应用-实例属性与描述符的关系

以下是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。

此文属个人理解,有何不妥之处,请改正。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值