美文网首页
python的属性管理

python的属性管理

作者: 天命_风流 | 来源:发表于2020-01-27 13:51 被阅读0次

python对于类的属性的管理有四种方式:特性、描述符、'__getattr__' 和 '__getattribute__'。使用这些方法可以让你对一个类的属性实现更多操作。

特性(property)

形式:

name = property( getName, setName, delName, 'name doc' )
  • 实现对name属性的管理
  • property有四个参数分别实现类四种功能:获取属性、设置属性、删除属性 和 属性的文档。

用例:

    class CardHolder:
        acctlen = 8
        retireage = 59.5
    
        def __init__(self, acct, name, age, addr):
            self.acct = acct
            self.name = name
            self.age = age
            self.addr = addr
    
        def getName(self):
            return self.__name
    
        def setName(self, value):
            value = value.lower().replace(' ', '-')
            self.__name = value
    
        name = property(getName, setName)  # 对name属性:转化为__name,将字符转为小写,并且将空格转换成'-'
    
        def getAge(self):
            return self.__age
    
        def setAge(self, value):
            if value < 0 or value > 150:
                raise ValueError('invalid age')
            else:
                self.__age = value
    
        age = property(getAge, setAge)  # 对age:判断是否处于0-150之间
    
        def getAcct(self):
            return self.__acct[:-3] + '***'
    
        def setAcct(self, value):
            value = value.replace('-', '')
            if len(value) != self.acctlen:
                raise TypeError('invald acct number')
            else:
                self.__acct = value
    
        acct = property(getAcct, setAcct)  # 对acct:将'-'去除,规定acct长度为8,输出时将后三位转为 ***
    
        def remainGet(self):
            return self.retireage - self.age
    
        remain = property(remainGet)
    
    
    if __name__ == '__main__':
        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')

执行结果:

12345*** / bob-smith / 40 / 19.5 / 123 main st
23456*** / bob-q.-smith / 50 / 9.5 / 123 main st
56781*** / sue-jones / 35 / 24.5 / 124 main st
Bad age for Sue
Can't set sue.remain
Bad acct for Sue

描述符(descriptor)

上面提到的特性是描述符的特殊形式,使用描述符和特性也有许多相似之处,描述符使用一个类实现对属性的管理,形式:

class Descriptor:
    "docstring goes here" #设置属性文档
    def __get__(self, instance, owner) : ... #获取属性
    def __set__(self, instance, value) : ... #设置属性
    def __delete__(self, instance): ... #删除属性
  • self :代表Descriptor类本身
  • instance:代表调用这个描述符的类的实例,如果这个属性是类属性,则会返回None
  • owner:代表实例附加到的类

为了理解这几个参数,有这样一个实例:


image.png
  • 描述符可以嵌套在需要描述的类内,也可以自身独立,在下面给出的例子中就是嵌套的

用例:

class CardHolder:
    acctlen = 8
    retireage = 59.5

    def __init__(self, acct, name, age, addr):
        self.acct = acct
        self.name = name
        self.age = age
        self.addr = addr

    class Name:
        def __get__(self, instance, owner):
            return self.name  # 使用描述符的存储空间,而不是CardHolder的

        def __set__(self, instance, value):  # 在这里你可以使用instance,但是要注意将属性名改成_name,否则会循环
            value = value.lower().replace(' ', '-')
            self.name = value

    name = Name()

    class Age:
        def __get__(self, instance, owner):
            return self.age

        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:  # 在这里使用了instance的属性
                raise TypeError('invald acct number')
            else:
                self.acct = value

    acct = Acct()

    class Remain:
        def __get__(self, instance, owner):
            return instance.retireage - instance.age  # 使用instance.age会触发Age.__get__

        def __set__(self, instance, value):
            raise TypeError('cannot set remain')

    remain = Remain()


if __name__ == '__main__':

    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')

12345*** / bob-smith / 40 / 19.5 / 123 main st
23456*** / bob-q.-smith / 50 / 9.5 / 123 main st
56781*** / sue-jones / 35 / 24.5 / 124 main st
Bad age for Sue
Can't set sue.remain
Bad acct for Sue


特性和描述符通常被用于管理某个特定的属性,我们使用这种方法可以更加深度地定制一个属性,以此实现更加丰富的功能。额外说一下,形如描述符的形式,在一个类中使用另一个类完成自身的功能,这种形式称为委托。
下面我们会使用__getattr__ 和 __getattribute__对类属性进行广泛的捕捉和管理。

__getattr__(捕捉所有未定义的get请求)

使用__getattr__可以对所有未定义的属性获取请求进行捕捉。通常与之搭配的还有__setattr__,它可以捕捉所有属性的设置请求。

形式:

    def __getattr__(self, name)
    def __setattr__(self, name, value)

__setattr __ 会捕捉所有的属性设置,这就导致你可能掉进循环设置的陷阱,这种情况在之后要使用的__getattribute __中也很常见,对此,我们使用两种方法避免:

  1. 使用object :object是python中所有类的父类,所有类都是在继承了object类的基础上进行的。你可以使用object.__setattr __(self, name, value)(__getattribute __类似)将属性设置向超类传递。这种使用方法我在之后" __getattribute __ "的代码中会特别标注。
  2. 使用self.__dict __[key] = value 的形式实现,注意,这种方式在可以在__setattr __中使用,__getattribute __中不要使用

用例:

class CardHolder:
    acctlen = 8
    retireage = 59.5

    def __init__(self, acct, name, age, addr):
        self.acct = acct
        self.name = name
        self.age = age
        self.addr = addr

    def __getattr__(self, item):
        if item == 'acct':  # 请求acct,注意在__setattr__中,保存的内容为 _acct
            return self._acct[:-3] + '***'
        elif item == 'remain':
            return self.retireage - self.age
        else:  # 其它未定义的属性将会抛出异常
            raise AttributeError(item)

    def __setattr__(self, key, value):
        if key == 'name':
            value = value.lower().replace(' ', '-')
        elif key == 'age':
            if value < 0 or value > 150:
                raise ValueError('invalid age')
        elif key == 'acct':  # 注意这里将key 改成了 _acct,否则__getattr__将无法捕捉acct
            key = '_acct'
            value = value.replace('-', '')
            if len(value) != self.acctlen:
                raise TypeError('invald acct number')
        elif key == 'remain':
            raise TypeError('cannot set remain')
        self.__dict__[key] = value  # 将属性保存,不要使用self.key = value 这种代码,会引起循环


if __name__ == '__main__':

    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')

12345*** / bob-smith / 40 / 19.5 / 123 main st
23456*** / bob-q.-smith / 50 / 9.5 / 123 main st
56781*** / sue-jones / 35 / 24.5 / 124 main st
Bad age for Sue
Can't set sue.remain
Bad acct for Sue

__getattribute __(捕捉所有的属性请求)

形式:

    def __getattribute__(self, name)
  • 请注意循环可能带来的问题,下面的例子使用了object.__getattribute __解决这个问题。

用例:

class CardHolder:
    acctlen = 8
    retireage = 59.5

    def __init__(self, acct, name, age, addr):
        self.acct = acct
        self.name = name
        self.age = age
        self.addr = addr

    def __getattribute__(self, item):
        superget = object.__getattribute__  # __getattribute__会捕捉所有属性,使用object(所有类的基类)破除循环
        if item == 'acct':
            return superget(self, 'acct')[:-3] + '***'
        elif item == 'remain':
            return superget(self, 'retireage') - superget(self, 'age')
        else:
            return superget(self, item)

    def __setattr__(self, key, value):
        if key == 'name':
            value = value.lower().replace(' ', '-')
        elif key == 'age':
            if value < 0 or value > 150:
                raise ValueError('invalid age')
        elif key == 'acct':
            value = value.replace('-', '')
            if len(value) != self.acctlen:
                raise TypeError('invald acct number')
        elif key == 'remain':
            raise TypeError('cannot set remain')
        self.__dict__[key] = value


if __name__ == '__main__':

    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')

12345*** / bob-smith / 40 / 19.5 / 123 main st
23456*** / bob-q.-smith / 50 / 9.5 / 123 main st
56781*** / sue-jones / 35 / 24.5 / 124 main st
Bad age for Sue
Can't set sue.remain
Bad acct for Sue

注意,__getattr __ 和 __getattribute __ 不会捕捉内置运算符,也就是说你依然可以使用 __add __这样的函数重载运算符。

相关文章

  • Python高级第四天

    对象属性管理 Python下一切皆对象,每个对象都有多个属性(attribute),Python对属性有一套统一的...

  • python的属性管理

    python对于类的属性的管理有四种方式:特性、描述符、'__getattr__' 和 '__getattribu...

  • Python之管理属性

    学习一下几个内容 __getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的...

  • Python对象属性管理

    _dict_ _slots_ 属性管理hasattr()函数getattr()函数setattr()函数delat...

  • Python的属性搜索

    这是我总结的Python属性搜索的加长版,完整的描述了Python在做属性访问时的过程。了解Python属性搜索的...

  • Python中的property

    Python中的property是用于实现属性可管理性的bulit-in数据类型(从表现上来看,property是...

  • day13面向对象(1)

    01-内置类属性 内置类属性:python中每个类都拥有内置的类属性 02私有化 python中,类和属性的私有化...

  • day014 笔记 08-01

    内置类的属性 python中每个类都拥有内置的类属性。 私有化 python中类中的属性和方法的私有化:直接在属性...

  • 类变量、实例变量、__dict__属性、字典update()

    python中的__dict__属性,__dict__是一个字典,键是属性名,值为属性值。 Python的实例有自...

  • python面向对象

    再次学习python面向对象。 python内置类属性1.dict : 类的属性(包含一个字典,由类的数据属性组成...

网友评论

      本文标题:python的属性管理

      本文链接:https://www.haomeiwen.com/subject/xrtethtx.html