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> <class '__main__.Person'>
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__拦截所有的属性获取,并且需要将没有管理的属性访问指向超类获取器以避免循环
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容