【Python045-魔法方法:属性访问】

一、属性的几种访问方式

1、类.属性名

>>> class C:
    def __init__(self):
        self.x = 'X-man'

        
>>> c = C()
>>> c.x
'X-man'

2、用内置函数getattr()访问属性

>>> getattr(c,'x','莫有这个属性')
'X-man'
>>> getattr(c,'y','莫有这个属性')
'莫有这个属性'
>>> 

3、用property方法访问属性

class C:
    def __init__(self,size=10):
        self.size=size

    def getsize(self):
        return self.size

    def setsize(self,value):
        self.size = value

    def delsize(self):
        del self.size

    x=property(getsize,setsize,delsize)

执行结果:
>>> c = C()
>>> c.x
10
>>> c.x=1
>>> c
<__main__.C object at 0x10c012f60>
>>> c.x
1
>>> del c.x
>>> c.x
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module> 
    c.x
  File "/Users/wufq/Desktop/property.py", line 6, in getsize
    return self.size
AttributeError: 'C' object has no attribute 'size'

4、各类内置函数访问属性

*  __getattr__(self,name)

定义当用户试图获取一个不存在的属性时的行为

* __getattribute__(self,name)

定义当该类的属性被访问时的行为

* __setattr__(self,name,value)

定义当一个属性被设置时的行为

* __delattr__(self,name)

定义当一个属性被删除时的行为

class C:
    def __getattr__(self,nane):
        print("__getattr__")
        
    def __getattribute__(self,name):
        print("__getattribute__")
        return super().__getattribute__(name)
    
    def __setattr__(self,name,value):
        print("__setattr__")
        super().__setattr__(name,value)
        
    def __delattr__(self,name):
        print("__delattr__")
        super().__delattr__(name)

>>> c = C()
>>> c.x
__getattribute__
__getattr__
>>> c.x =1
__setattr__
>>> c.x
__getattribute__
1
>>> del c.x
__delattr__
>>> c.x

'''
代码解析:
|--1、由第一个c.x先后打印出来__getattribute__和__getattr__,可以看出来,程序先找__getattribute__(定义当该类的属性被访问时的行为),然后
|-- 在找__getattr__(属性不存在时的访问行为)
|--2、由c.x =1 打印出来__setattr__,可以得知,当属性被设置时调用的是此方法
|--3、由del c.x=1 打印出来__delattr__可以得知,当属性被删除时调用此方法
'''

 5、写一个矩形类,默认有宽和高两个属性

如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长

class Rectangle:
    def __init__(self,width=0,height=0):
        self.width = width
        self.height = height
    #用square这个属性来标记正方形
    def __setattr__(self,name,):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            self.name = value

    def getArea(self):
        return self.width * self.height

>>> r1 = Rectangle(4,5)
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    r1 = Rectangle(4,5)
  File "/Users/wufq/Desktop/正方形.py", line 4, in __init__
    self.width = width
  File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
    self.name = value
  File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
    self.name = value
  File "/Users/wufq/Desktop/正方形.py", line 12, in __setattr__
    self.name = value
  [Previous line repeated 987 more times]
  File "/Users/wufq/Desktop/正方形.py", line 8, in __setattr__
    if name == 'square':
RecursionError: maximum recursion depth exceeded in comparison ''' 代码解析: |-- 为什么会报错:原因:首先实例化对象时给width和height赋值,赋值时就调用了__setattr__方法 |-- 进入__setattr__方法,会首先判断name有没有等于square,明显此时没有,则执行else里面的语句 |-- self.name = value,则又一次循环调用默认的__init__方法,依次这样就会进入死循环 '''

改进的方法

#第一种方法:采用super()方法调用基类
#第二种方法:采用字典的方法,__dict__

class Rectangle:
    def __init__(self,width=0,height=0):
        self.width = width
        self.height = height
    #用square这个属性来标记正方形
    def __setattr__(self,name,value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            #self.name = value
            #super().__setattr__(name,value)
            self.__dict__[name]=value #采用__dict__方法的原因是:给属性square所赋值存储到dict字典内

    def getArea(self):
        return self.width * self.height
执行结果:
>>> r1 = Rectangle(4,5)
>>> r1.getArea()
20
>>> r1.square = 10
>>> r1.getArea()
100
>>> r1.__dict__
{'width': 10, 'height': 10}

 二、练习题

1、对象的属性发生赋值操作时,将实际的的值+1赋值给相应的属性。

def __setattr__(self,name,value):
    self.name = value + 1

错误原因:属性被赋值时,__setattr__()会被调用,而里边的self.name = value +1又会再次触发__setattr__()调用,导致无限递归(死循环)

正确的写法

class C:
    def __init__(self,name):
        self.name = name
    def __setattr__(self,name,value):
        
        self.__dict__[name]=value+1
        print("name=",self.__dict__[name],"value=",value)

执行结果:
>>> c = C(2)
name= 3 value= 2

 2、自定义该类的属性被访问的行为,你应该重写那个魔法方法

答:__getattribute__(self,name)  :属性被访问时调用这个方法

3、当访问一个不存在的属性时,不报错且提示“该属性不存在”

class Demo:
    def __getattr__(self,name):
        return "该属性不存在"


执行结果:
>>> demo = Demo()
>>> demo.x
'该属性不存在'
>>> 

4、编写一个Demo类,使的下边代码可以正常执行

class Demo:
    def __getattr__(self,name):
        self.name = 'FishC'
        return self.name
    def __getattribute__(self,name):
        return super().__getattribute__(name)
    def __setattr__(self,name,value):
        return super().__setattr__(name,value)

执行结果:
>>> d=Demo()
>>> d.x
'FishC'
>>> d.x = "X-man"
>>> d.x
'X-man'
>>> 

5、编写一个Counter类,用于实时检查对象有多少个属性

#需求:检测对象有多少个属性
class Counter:
    def __init__(self):
        super().__setattr__('counter',0)
    def __setattr__(self,name,value):
        super().__setattr__('counter',self.counter + 1)
        super().__setattr__(name,value)
    def __delattr__(self,name):
        super().__setattr__('counter',self.counter - 1)
        super().__delattr__(name)
        
执行结果:
>>> c = Counter()
>>> c.x=1
>>> c.counter
1
>>> c.y =2
>>> c.z = 3
>>> c.m =4
>>> c.counter
4
>>> del c.m
>>> c.counter
3
>>> 
原文地址:https://www.cnblogs.com/frankruby/p/9912268.html