awp 发表于 2023-5-30 09:58:17

关于__setattr__无限递归的疑惑

本帖最后由 awp 于 2023-5-30 10:03 编辑

新手求问:

class C:
    def __setattr__(self, key, value):
      self.key = value# 无限递归
      # self.__dict__ = value 不会递归

类C造成无限递归。因为__setattr__本身是赋值,函数内部出现赋值,形成无限递归。
为什么self.__dict__ = value也是赋值语句,但不会形成无限递归?

同样的问题:

class D:
    def __init__(self, data):
      self.data = data
    def __contains__(self, item):
      return item in self.data

类D不会无限递归。但contains本身就是in,而函数内部也出现了in,为什么不无限递归?

还有:

类D可以参考类C的写法,省略掉__init__,这么写么?
class D:
    def __contains__(self, data, item):
      return item in self.data

isdkz 发表于 2023-5-30 10:06:12

首先,我们来讨论关于 __setattr__ 的无限递归问题。当你在Python中定义一个类,并在类中重写 __setattr__ 方法的时候,你基本上就在告诉Python:每次我试图设置这个对象的属性时,我都希望你调用这个方法。因此,如果你在 __setattr__ 方法中再试图设置对象的属性(就像你的例子中那样),那么Python会再次调用 __setattr__ ,形成无限递归。

但是,当你直接操作对象的 __dict__ 属性时,你实际上是在绕过 __setattr__ ,直接在对象的属性字典中设置键/值对。这就是为什么 self.__dict__ = value 不会造成无限递归。

关于你的第二个问题,类 D 并不会无限递归,原因是 __contains__ 方法和 in 操作符的逻辑是不同的。 __contains__ 方法定义的是当使用 in 操作符检查一个对象是否包含某个元素时应该做什么。而 in 操作符本身的行为就是调用对象的 __contains__ 方法(如果存在的话)。但在 __contains__ 方法内部使用 in 操作符检查另一个对象(例如 self.data )时,并不会引发递归,因为你检查的对象是不同的。如果你把 return item in self.data 换成 return item in self 一样是会引发无线递归的问题的

对于你的最后一个问题,对于Python类的方法,第一个参数通常被命名为 self ,这是一个传统的约定,用于引用调用该方法的对象本身。而 __contains__ 方法只接受一个额外的参数,即要检查的元素。如果你尝试像你那样定义 __contains__ 方法,Python将会抛出一个TypeError,因为 __contains__ 被调用时只传递了一个参数,但你的定义需要两个。

在你的例子中,你可能需要将 data 定义为类的一个属性,然后在 __contains__ 方法中检查它。例如,你可以这样做:

class D:
    def __init__(self, data):
      self.data = data
    def __contains__(self, item):
      return item in self.data


这样,在创建一个 D 对象时,你可以给 data 传值,然后使用 in 操作符检查一个元素是否在 data 中。

awp 发表于 2023-5-31 09:17:34

isdkz 发表于 2023-5-30 10:06
首先,我们来讨论关于 __setattr__ 的无限递归问题。当你在Python中定义一个类,并在类中重写 __setattr__...

我以为等号=在类中就替代了_setattr_,in在类中就取代了_contains_,难道不是这么理解的么

isdkz 发表于 2023-5-31 09:34:27

awp 发表于 2023-5-31 09:17
我以为等号=在类中就替代了_setattr_,in在类中就取代了_contains_,难道不是这么理解的么

__setattr__ 只有这个类的属性被重新赋值的时候才会触发呀,

然而这里并没有重新赋值这个类的属性,这里使用的是 self.__dict__ = value,而不是 self.__dict__ = value

而 __contains__ 是只有在 in 这个类的对象的时候才会触发,所以在 __contains__ 使用 item in self.data 并不会有什么问题,

因为 self.data 并不是 D 的实例,而 item in self 则不行,因为 self 就是 D 的实例

主要是你需要搞清楚不同魔法方法的触发条件

awp 发表于 2023-5-31 09:35:57

isdkz 发表于 2023-5-31 09:34
__setattr__ 只有这个类的属性被重新赋值的时候才会触发呀,

然而这里并没有重新赋值这个类的属性,这 ...

很清晰,懂了。感谢前辈!
页: [1]
查看完整版本: 关于__setattr__无限递归的疑惑