只爱整活周淑怡 发表于 2020-8-21 17:46:56

setattr问题

关于属性访问
class Rectangle:
    def __init__(self,width=0,height=0):

      self.width=width
      self.height=height

    def __setattr__(self,name,value):
      if name=='square':
            self.width=value
            self.height=value
      else:
         super().__setattr__(name,value)#self.name=value会导致无限递归

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

关于正方形的问题 小甲鱼说在init调用时触发了__setattr__的方法然后执行了else的语句
self.width=value 这里的value 是width的值吗 执行到这里不就已经可以结束了吗
为什么会陷入死循环呢??看解释说是又再次触发了setattr这个方法 怎么看出来的 没懂

还有就是基类的__setattr__方法是什么内容呢好像之前没有提到过诶

Twilight6 发表于 2020-8-21 17:59:04

本帖最后由 Twilight6 于 2020-8-21 18:03 编辑


为什么会陷入死循环呢?

因为你不管执行 else 还是 if 代码块都有赋值操作,那么就会无限调用自身的 __setattr__ 方法

就好如你赋值调用了 __setattr__ , 结果进入 __setattr__ 方法后还是赋值操作,此时又调用自身,然后又是赋值操作又调用自身.....就永远没有出口了


去掉 else 里面的赋值操作,为什么不会陷入死循环呢?

首先 __init__ 方法里面有两个赋值操作:
self.width=width
self.height=height

此时假设你 实例化一个对象 r = Rectangle(5,100) 那么 __init__ 方法会自动执行:
self.width = 5
self.height = 100
这两个赋值操作会调用直接的 __setattr__ ;先来看第一个 self.width=5因为这里进行了赋值操作,所以调用了自己的 __setattr__ 方法

在 __setattr__ 方法中 name参数指的就是被赋值的变量名,简单说就是等号左边的,而这里就是指 width ,而 value 是等号右边的 就是你传入的 5

也就是你赋值self.width=5 时候name = 'width'而 value = 5

__setattr__ 方法中首先进行判断变量名 是否为 square ,这里很显然 name = 'width' 不等于 'square' 所以执行 else

else 是用 super 调用基类的 __setattr__方法 来给 width 赋值为 'square' ,而不是调用自己的 所以不会调用自己的 __setattr__方法

同理 self.height=height 也是一样的 , 所以这样就不会导致 无限递归调用 __setattr__方法 的情况发生了,因为赋值操作到最后都是执行 父类的 __setattr__ 方法,而不是自己类的 __setattr__ 方法

基类的__setattr__方法是什么内容呢?

在对一个属性设置值的时候,就会自动调到这个函数,每个设置值的方式都会进入这个方法,然后给对应变量设置对应的值。

只爱整活周淑怡 发表于 2020-8-21 18:42:35

Twilight6 发表于 2020-8-21 17:59
因为你不管执行 else 还是 if 代码块都有赋值操作,那么就会无限调用自身的 __setattr__ 方法

就 ...

else 是用 super 调用基类的 __setattr__方法 来给 width 赋值为 'square' ,
这句话是啥意思没懂???当我没有令   实例对象.square=value 的时候不是跟else内容没关系吗?

Twilight6 发表于 2020-8-21 18:44:18

只爱整活周淑怡 发表于 2020-8-21 18:42
else 是用 super 调用基类的 __setattr__方法 来给 width 赋值为 'square' ,
这句话是啥意思没懂??? ...



这里只有在你else 下面有 self.name=value 才会导致无限递归

你上面代码是不会无限递归,因为把这个注释了

只爱整活周淑怡 发表于 2020-8-21 19:44:38

Twilight6 发表于 2020-8-21 17:59
因为你不管执行 else 还是 if 代码块都有赋值操作,那么就会无限调用自身的 __setattr__ 方法

就 ...

还有一个就是当name==square的时候是不是 相当于先执行了if的内容赋值成了self.width=5然后又触发了__setattr__方法中的else部分 是这样理解的吗???

Twilight6 发表于 2020-8-21 19:47:09

只爱整活周淑怡 发表于 2020-8-21 19:44
还有一个就是当name==square的时候是不是 相当于先执行了if的内容赋值成了self.width=5然后又触发了__set ...

对 理解正确

只爱整活周淑怡 发表于 2020-8-21 20:50:43

Twilight6 发表于 2020-8-21 19:47
对 理解正确

还有个问题 就是这节的课后作业第2题
>>> class C:
      def __getattr__(self, name):
                print(1)
      def __getattribute__(self, name):
                print(2)
      def __setattr__(self, name, value):
                print(3)
      def __delattr__(self, name):
                print(4)

               
>>> c = C()
>>> c.x = 1
# 位置一,请问这里会显示什么?
>>> print(c.x)
# 位置二,请问这里会显示什么?

我打出
c=C()
c.x
为什么只输出的是2 啊 c.x不是访问了一个不存在的属性吗 那不就应该2 1都打出来吗

Twilight6 发表于 2020-8-21 20:52:32

只爱整活周淑怡 发表于 2020-8-21 20:50
还有个问题 就是这节的课后作业第2题





>>> c = C()
>>> c.x = 1
# 位置一,请问这里会显示什么?
>>> print(c.x)
# 位置二,请问这里会显示什么?

你前面给 x 赋值为 1 了,也就是说此时这个实例对象里面有属性 x 了

所以只会调用 __getattribute__ 不会调用__getattr__

只爱整活周淑怡 发表于 2020-8-21 21:03:26

Twilight6 发表于 2020-8-21 20:52
你前面给 x 赋值为 1 了,也就是说此时这个实例对象里面有属性 x 了

所以只会调用 __getattribu ...

不是啊其实我是这样的一段代码class C:
      def __getattr__(self, name):
                print(1)
      def __getattribute__(self, name):
                print(2)
      def __setattr__(self, name, value):
                print(3)
      def __delattr__(self, name):
                print(4)

c=C()
c.x
打出来的是2 为啥不是2 1啊c.x不是尚不存在吗那个时候

Twilight6 发表于 2020-8-21 21:08:40

只爱整活周淑怡 发表于 2020-8-21 21:03
不是啊其实我是这样的一段代码

c=C()



先说下 __getattribute__ 的功能,这个是当你访问属性时候会优先调用,如果属性存在,返回属性的值,如果不存在则报错

但是如果类中定义了 __getattr__ 方法,那么这个报错会被这个方法捕获,自动调用 __getattr__ 方法,返回 __getattr__ 设置的内容

而你这里重写了 __getattribute__ 方法,那么原有的 __getattribute__ 方法被你覆盖了,也就不会报错使得 __getattr__ 也就不会被调用

你把代码改成这样就可以了:
class C:
    def __getattr__(self, name):
      print(1)

    def __getattribute__(self, name):
      print(2)
      return super().__getattribute__(name)

    def __setattr__(self, name, value):
      print(3)

    def __delattr__(self, name):
      print(4)

c = C()
c.x

只爱整活周淑怡 发表于 2020-8-22 00:06:35

Twilight6 发表于 2020-8-21 21:08
先说下 __getattribute__ 的功能,这个是当你访问属性时候会优先调用,如果属性存在,返回属性的值,如 ...

woc大佬牛啊!!!小甲鱼什么cylx

zhangfeiwen 发表于 2020-9-22 21:39:05

我也是卡在这里难受的一匹

123Marchapril 发表于 2021-4-7 16:35:17

Twilight6 发表于 2020-8-21 18:44
这里只有在你else 下面有 self.name=value 才会导致无限递归

你上面代码是不会无限递归,因为把 ...

你好,调用基类的setattr方法的时候为什么不加self呢?基类为一个变量设置了属性,那这个变量就是子类的吗,这是默认的吗?
页: [1]
查看完整版本: setattr问题