Python之管理属性

学习一下几个内容

  • __getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的处理器方法
  • __getattribute__方法,把所有属性都指向Python3.0中类的一个泛型处理器的方法
  • property内置函数,把特定属性访问定位到get和set处理函数,也叫做特性(Property)
  • 描述符协议,把特定属性访问定位到具有任意get和set处理器方法的类的实例

特性协议允许我们把一个特定属性的get和set操作指向我们所提供的函数或方法,使得我们能够插入在属性访问的时候自动运行代码。

*基础知识

attribute = property(fget,fset,fdel,doc)
四个参数分别代表属性attribute的get,set,del方法和文档性信息

#-*-coding:UTF-8-*-
class Person:
    def __init__(self,name):
        self._name = name
    def getName(self):
        print('fetch...')
        return self._name
    def setName(self,value):
        self._name = value
        print('change...')
    def delName(self):
        print('remove...')
        del self._name
    name = property(getName,setName,delName,'name property docs')  #属性name是通过property内置函数创建的并提供一些方法操作name的值
if __name__ == '__main__':
    bob = Person('Bob Smith')
    print(bob.name)     #调用getName方法 获取name的值
    bob.name = 'Robert Smth'  #调用setName方法设置值
    print(bob.name)
    del bob.name
    print(Person.name.__doc__)
####
fetch...
Bob Smith
change...
fetch...
Robert Smth
remove...
name property docs
[Finished in 0.3s]

像所有的类属性一样,实例和较低的子类都继承特性。

class Coder(Person):
    pass
if __name__ == '__main__':
    bob = Coder('Bob Smith')
    print(bob.name)
###
fetch...
Bob Smith
Coder子类从Person继承了name特性

计算属性

class PropSquare:
    def __init__(self, start):
        self.value = start
    def getX(self):
        return self.value**2
    def setX(self,value):
        self.value = value
    X = property(getX,setX)
if __name__ == '__main__':
    P = PropSquare(3)
    Q = PropSquare(32)
    print(P.X)
    P.X = 4
    print(P.X)
    print(Q.X)
###
9
16
1024
说明可以在自定义的函数中做一些数据方面的处理

使用装饰器来编写特性

#-*-coding:UTF-8-*-
"""
@decorator
def func(args):...

相当于
def func(args):...
func = decorator(func)
"""
class Person:
    def __init__(self, name):
        self._name = name
    @property
    def name(self):
        """name property docs"""
        print('fetch.....')
        return self._name

    @name.setter
    def name(self,value):
        print('change')
        self._name = value
    @name.deleter
    def name(self):
        print('remove...')
        del self._name
if __name__ == '__main__':
    rose = Person('Rose Jhon')
    print(rose.name)
    rose.name = 'Roose Jhon'
    print(rose.name)
    del rose.name
###
fetch.....
Rose Jhon
change
fetch.....
Roose Jhon
remove...
装饰是编写特性的替代方法。注意set和del方法的装饰

描述符

描述符提供了拦截属性访问的一种替代方法。特性是描述符的一种,property内置函数是创建一个特定类型描述符的一种简化方法。
描述符协议允许特性我们把一个特定属性的get和set操作指向我们提供的一个单独类对象的方法:他们提供了一种方式插入在访问属性的时候自动运行的代码,并且他们允许我们拦截属性删除并且为属性提供文档。描述符作为独立的类创建,并且他们就像方法函数一样分配给类属性,也可以通过子类和实例继承
*基础知识

class Descriptor:
    def __get__(self,instance,owner):...
    def __set__(self,instance,value):...
    def __delete__(self,instance):.....

带有这些方法的类都可以看做是描述符,并且当他们的一个实例分配给另一个类的属性的时候,它们的这些方法是特殊的----当访问属性的时候,会自动调用它们。
描述符参数:都传递了描述符类实例(self)以及描述符实例所附加的客户类的实例(instance)。__get__访问方法还额外的接收一个owner参数,指定描述符实例要附加到的类。

#-*-coding:UTF-8-*-
"""
描述符
"""
class Name:
    def __get__(self,instance,owner):
        print(self,instance,owner)
        return instance._name
    def __set__(self,instance,value):
        print('change')
        instance._name = value
    def __delete__(self,instance):
        print('remove')
        del instance._name
"""
描述符生成的属性
"""
class Person:
    def __init__(self,name):
        self._name = name
    name = Name()
"""
self是Name类的实例
instance是Person类的实例
owner是Person类
"""
if __name__ == '__main__':
    bob = Person('Jim Green')
    print(bob.name)
    bob.name = 'Jam Green'
    del bob.name
###
<__main__.Name object at 0x00610FB0> <__main__.Person object at 0x00610FD0> 
Jim Green
change
remove

描述符的计算属性

计算属性

class DescSquare:
    def __init__(self,start):
        self.value = start
    def __get__(self,instance,owner):
        return self.value**2
    def __set__(self,instance,value):
        self.value = value
class Client1:
    X = DescSquare(3) #类属性 所有实例共享的  描述符类实例化的时候传值
if __name__ == '__main__':
    c1 = Client1()
    print(c1.X)
    print(Client1.X)

在描述符中使用状态信息

  • 描述符状态用来管理内部用于描述符工作的数据
  • 实例状态记录了和客户类相关的信息,以及可能有客户类创建的信息
"""
描述符状态信息
"""
class DescState:
    def __init__(self,value):
        self.value = value
    def __get__(self,instance,owner):
        print('DescState get')
        return self.value*10
    def __set__(self,instance,value):
        print('DescState set')
        self.value = value
class CalcAttrs:
    X = DescState(2)
    Y = 3
    def __init__(self):
        self.Z = 4
if __name__ == '__main__':
    obj = CalcAttrs()
    print(obj.X,obj.Y,obj.Z)
####
value属性仅存在描述符中,在CalcAttrs中使用value也不会冲突

对描述符存储或使用附加到客户类的实例的一个属性,不是描述符类的属性

class InsState:
    def __get__(self,instance,owner):
        print('InsState get')
        return instance._Y*100
    def __set__(self,instance,value):
        print('InsState set')
        instance._Y = value
class CalcAttrs1:
    X = DescState(2)
    Y = InsState()
    def __init__(self):
        self._Y = 3
        self.Z = 4
if __name__ == '__main__':
    obj = CalcAttrs1()
    print(obj.X,obj.Y,obj.Z)
###
DescState get
InsState get
20 300 4
在运行的时候 会传递CalcAttrs1类的实例过去(instance)
在描述符类中instance._Y调用的就是obj的属性。在obj.Y的时候 会调用 InsState实例Y中的__get__方法并传递相关参数过去

__getattr__和__getattribute__

  • __getattr__针对未定义的属性运行----也就是说,属性没有存储在实例上,或者没有从父类之一继承
  • __getattribute__针对每个属性,当使用时,需要小心避免通过把属性访问传递给超类而导致递归循环
def __getattr__(self,name): #On undefined attribute fetch [obj.name]
def __getattribute__(self,name):#on all attribute fetch [obj.name]
def __setattr__(self,name,value):#on all attribute assignment [obj.name=value]
def __delattr__(self,name): #on all attribute deletion [del obj.name]
其中self通常是主体实例对象,name是将要访问的属性的字符串,value是要赋给该属性的对象

例子

#-*-coding:UTF-8-*-
class Catcher:
    def __getattr__(self,name):
        print('Get:',name)
    def __setattr__(self,name,value):
        print('Set:',name,value)
    def test(slef):
        print('test')
class Wrapper:
    def __init__(self,obj):
        self.wrapper = obj
    def __getattr__(self,attrname):
        print('Trace:',attrname)
        return getattr(self.wrapper,attrname)
if __name__ == '__main__':
    X = Catcher()
    X.job
    X.pay = 99
    Y = Wrapper(X)
    Y.test()
###
Get: job
Set: pay 99
Trace: test
test
####
Catcher的实例X在调用X.job时,会遭到getattr方法的拦截(拦截没有定义的属性)

注意使用__setattr__和__getattribute__要避免循环

#-*-coding:UTF-8-*-
class Person:
    def __init__(self,name):
        self._name = name
    # def __getattr__(self,attr):
    #   if attr == 'name':
    #       print('fetch....')
    #       return self._name
    #   else:
    #       raise AttributeError(attr)
    def __getattribute__(self,attr):
        if attr == 'name':
            print('fetch')
            attr = '_name'
        return object.__getattribute__(self,attr)
    def __setattr__(self,attr,value):
        if attr=='name':
            print('change...')
            attr = '_name'
        print(attr)
        self.__dict__[attr] = value   #避免循环 如果使用self._name = value会产生递归循环
    def __delattr__(self,attr):
        if attr == 'name':
            print('remove...')
            attr = '_name'
        del self.__dict__[attr]
if __name__ == '__main__':
    bob = Person('Bob Smith')
    print(bob.name)
    bob.name = 'Robert Smith'
    print(bob.name)
    del bob.name
    print('-'*20)
    sue = Person('Sue Jones')
    print(sue.name)
- 构造函数中的self._name = name会触发__setattr__

__getattr__和__getattribute__比较

  • __getattr__拦截未定义的属性,已经定义的属性不会拦截
  • __getattribute__拦截所有的属性获取,并且需要将没有管理的属性访问指向超类获取器以避免循环

你可能感兴趣的:(Python之管理属性)