首页 > 其他 > 详细

第45讲:魔方方法——属性访问

时间:2020-09-08 23:15:28      阅读:105      评论:0      收藏:0      [点我收藏+]

一 属性访问相关的知识

1 几个常用的方法:

  • __getattr__(self,name):定义当用户试图获取一个不存在的属性时的行为
  • __getattribute__(self,name):定义当该类的属性被访问时的行为
  • __setattr__(self,name,value):定义当一个属性被设置时的行为
  • __delattr__(self,name):定义当一个属性被删除时的行为

2 举例:

 1 >>> class C(object):
 2 ...     def __getattribute__(self,name):
 3 ...             print("getattribute")
 4 ...             return super().__getattribute__(name)
 5 ...     def __getattr__(self,name):
 6 ...             print("getattr")
 7 ...     def __setattr__(self,name,value):
 8 ...             print("setattr")
 9 ...             super().__setattr__(name,value)
10 ...     def __delattr__(self,name):
11 ...             print("delattr")
12 ...             super().__delattr__(name)
13 ...
14 >>> c = C()
15 >>> c.x
16 getattribute
17 getattr
18 >>> c.x = object
19 setattr
20 >>> c.x
21 getattribute
22 <class object>
23 >>> c.x = 5
24 setattr
25 >>> c.x
26 getattribute
27 5

3 死循环陷阱——课堂练习

练习要求:

  • 写一个矩形类,默认有宽和高两个属性;
  • 如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。

会引起死循环的代码:

 1 class Rectangle(object):
 2     def __init__(self,width=0,height=0):
 3         self.width = width            # 该句是赋值语句,执行过程中已经调用了类本身的__setattr__(self,name,value)方法
 4         self.height = height
 5         
 6     def __setattr__(self,name,value):
 7         if name == square:
 8             self.width = value
 9             self.height = value
10         else:
11             self.name = value          # 此时再次进行赋值,同样会调用类本身的__setattr__(self,name,value)方法,从而陷入死循环
12     
13     def getArea(self):
14         return self.width * self.height
15     
16     def getCirc(self):
17         return 2 * (self.width + self.height)

改正后的代码:

 1 class Rectangle(object):
 2     def __init__(self,width=0,height=0):
 3         self.width = width            # 该句是赋值语句,执行过程中已经调用了类本身的__setattr__(self,name,value)方法
 4         self.height = height
 5         
 6     def __setattr__(self,name,value):
 7         if name == square:
 8             self.width = value
 9             self.height = value
10         else:
11             super().__setattr__(name,value)  # 通过调用父类的__setattr__(self,name,value)方法完成重新赋值的操作,不会造成死循环
12             # self.__dict__[name] = value    # 通过__dict__属性以字典的方式进行赋值,此过程不会调用__setattr__(self,name,value)方法
13     
14     def getArea(self):
15         return self.width * self.height
16     
17     def getCirc(self):
18         return 2 * (self.width + self.height)

 

 

二 课后作业

测试题部分:

0. 请问以下代码的作用是什么?这样写正确吗?(如果不正确,请改正)

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

答:这段代码试图在对象的属性发生赋值操作的时候,将实际的值 +1赋值给相应的属性。但这么写法是错误的,因为每当属性被赋值的时候, __setattr__() 会被调用,而里边的 self.name = value + 1 语句又会再次触发 __setattr__() 调用,导致无限递归。

代码应该这样写:kJSbWVtH

1 def __setattr__(self, name, value):
2         self.__dict__[name] = value + 1

或者:xBp0LSolG

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

 

1. 自定义该类的属性被访问的行为,你应该重写哪个魔法方法?
答:__getattribute__(self, name)

2. 在不上机验证的情况下,你能推断以下代码分别会显示什么吗?

 1 >>> class C:
 2         def __getattr__(self, name):
 3                 print(1)
 4         def __getattribute__(self, name):
 5                 print(2)
 6         def __setattr__(self, name, value):
 7                 print(3)
 8         def __delattr__(self, name):
 9                 print(4)
10 
11                 
12 >>> c = C()
13 >>> c.x = 1
14 # 位置一,请问这里会显示什么?
15 >>> print(c.x)
16 # 位置二,请问这里会显示什么?

答:位置一会显示 3,因为 c.x = 1 是赋值操作,所以会访问 __setattr__() 魔法方法;

       位置二会显示 2 和 None,因为 x 是属于实例对象 c 的属性,所以 c.x 是访问一个存在的属性,因此会访问 __getattribute__() 魔法方法,但我们重写了这个方法,使得它不能按照正常的逻辑返回属性值,而是打印一个 2 代替,由于我们没有写返回值,所以紧接着返回 None 并被 print() 打印出来。

3. 在不上机验证的情况下,你能推断以下代码分别会显示什么吗?

 1 >>> class C:
 2         def __getattr__(self, name):
 3                 print(1)
 4                 return super().__getattr__(name)
 5         def __getattribute__(self, name):
 6                 print(2)
 7                 return super().__getattribute__(name)
 8         def __setattr__(self, name, value):
 9                 print(3)
10                 super().__setattr__(name, value)
11         def __delattr__(self, name):
12                 print(4)
13                 super().__delattr__(name)
14                 
15 >>> c = C()
16 >>> c.x
 

答:在不上机的情况下,我相信80%以上的鱼油很难猜到正确的答案T_T

 1 >>> c = C()
 2 >>> c.x
 3 2
 4 1
 5 Traceback (most recent call last):
 6   File "<pyshell#31>", line 1, in <module>
 7     c.x
 8   File "<pyshell#29>", line 4, in __getattr__
 9     return super().__getattr__(name)
10 AttributeError: super object has no attribute __getattr__

为什么会如此显示呢?我们来分析下:首先 c.x 会先调用 __getattribute__() 魔法方法,打印 2;然后调用 super().__getattribute__(),找不到属性名 x,因此会紧接着调用 __getattr__() ,于是打印 1;但是你猜到了开头没猜到结局……当你希望最后以 super().__getattr__() 终了的时候,Python 竟然告诉你 AttributeError,super 对象木有 __getattr__ !!

求证:O~U{a1

1 >>> dir(super)
2 [__class__, __delattr__, __dir__, __doc__, __eq__, __format__, __ge__, __get__, __getattribute__, __gt__, __hash__, __init__, __init_subclass__, __le__, __lt__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __self__, __self_class__, __setattr__, __sizeof__, __str__, __subclasshook__, __thisclass__]

 

4. 请指出以下代码的问题所在:

1 class Counter:
2         def __init__(self):
3                 self.counter = 0
4         def __setattr__(self, name, value):
5                 self.counter += 1
6                 super().__setattr__(name, value)
7         def __delattr__(self, name):
8                 self.counter -= 1
9                 super().__delattr__(name)

答:初学者重写属性魔法方法很容易陷入的一个误区就是木有“观前顾后”。
以下注释:!aAMZ

 1 class Counter:
 2         def __init__(self):
 3                 self.counter = 0 # 这里会触发 __setattr__ 调用
 4         def __setattr__(self, name, value):
 5                 self.counter += 1
 6 “““既然需要 __setattr__ 调用后才能真正设置 self.counter 的值,所以这时候 self.counter 还没有定义,所以没法 += 1,错误的根源。”””
 7                 super().__setattr__(name, value)
 8         def __delattr__(self, name):
 9                 self.counter -= 1
10                 super().__delattr__(name)

 

 

第45讲:魔方方法——属性访问

原文:https://www.cnblogs.com/luoxun/p/13636252.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!