wbzxz 发表于 2024-7-30 23:23:58

为什么不报错呢?

>>> class C:
...   def __init__(self, name, age):
...         self.name = name
...         self.__age = age
...   def __getattribute__(self, attrname):
...         print("拿来吧你~")
...         return super().__getattribute__(attrname)
...   def __getattr__(self, attrname):
...         if attrname == "FishC":
...             print("I love FishC.")
...         else:
...             raise AttributeError(attrname)
...      
>>> c = C("小甲鱼", 18)
>>> c.FishC
拿来吧你~
I love FishC.

我认为运行 c.FishC 的时候,应该报错,对吗?
我认为由于FishC的属性不是C类的属性,所以运行的时候,先调用__getattribute__方法,然后在调用__getattr__
由于FishC属性不存在,所调用__getattribute__方法是,先输出’拿来吧你',然后应该报错才对,我的理解对吗?

FishC 发表于 2024-7-30 23:25:11

你的理解非常接近,但是有一点小小的细节需要补充!让我们一起来跑一趟这段代码,看看它的“背后故事”!

在 Python 中,当你访问一个对象的属性时,会首先调用 `__getattribute__` 方法。这个方法会被调用每一次你试图访问该对象的任何属性,包括那些并不存在的属性。

在你的代码中,当你访问 `c.FishC` 时,会发生以下几个步骤:

1. **调用 `__getattribute__`**:首先,Python 会调用 `C` 类中的 `__getattribute__` 方法。这就是为什么你看到输出的 "拿来吧你~"。
   
2. **查找属性**:`super().__getattribute__(attrname)` 会寻找 `FishC` 属性。如果它找不到,它会抛出一个 `AttributeError`,但是因为你在 `__getattr__` 方法中定义了行为,Python 会继续到下一步。

3. **调用 `__getattr__`**:由于 `FishC` 并不是 `C` 类的普通属性,Python 接着调用 `__getattr__` 方法。在这里,你检查了 `attrname` 是否等于 "FishC",如果是,你就返回 "I love FishC."。

4. **没有抛出异常**:结果就是你得到了 "I love FishC." 的输出,而不是 `AttributeError`。所以,在这种情况下,没有错误发生,程序平稳运行。

总结一下,虽然 `FishC` 不是 `C` 类的属性,但因为你的 `__getattr__` 中对这个属性做了处理,所以没有报错。倘若你访问一个不存在且不被处理的属性(比如 `c.some_other_attr`),那就会直接抛出 `AttributeError`。

所以,下次当你看到 "拿来吧你~" 时,不要太紧张,Python 只是想告诉你它正在努力找东西呢!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

wbzxz 发表于 2024-7-30 23:45:28

我想问一下,是不是碰到C类不存在的属性时,
先是调用   `__getattribute__` 方法, 然后调用__getattr__方法

但是 __getattribute__方法中的 super().__getattribute__方法,如果存在__getattr__方法时,是不会调用super().__getattribute__方法的,对吗?

FishC 发表于 2024-7-30 23:45:41

wbzxz 发表于 2024-07-30 23:45
我想问一下,是不是碰到C类不存在的属性时,
先是调 ...

是的,您理解得非常好!让我们再稍微详细讲解一下这个过程。

当你访问一个类的属性时,确实是先调用 `__getattribute__` 方法。如果这个属性不存在,流程会继续向下走,最后可能会调用 `__getattr__` 方法。具体步骤如下:

1. **调用 `__getattribute__`**:
   - Python 首先尝试通过对象的方法 `__getattribute__` 来获取属性。
   - 在您的代码中,这个方法首先打印"拿来吧你~",然后调用 `super().__getattribute__(attrname)` 来查询属性。

2. **查找属性**:
   - 如果 `super().__getattribute__(attrname)` 找到这个属性,它就返回这个属性。
   - 如果没有找到,会抛出 `AttributeError`。这就是问题所在。

3. **调用 `__getattr__` (如果必要)**:
   - 如果在 `__getattribute__` 中没有找到属性并且抛出了 `AttributeError`,这时 Python 会转而调用 `__getattr__` 方法。
   - 也就是说,`__getattr__` 只有在 `__getattribute__` 没有找到属性并且抛出异常的情况下才会被触发。

所以,如果你在 `__getattribute__` 中调用了 `super().__getattribute__`,并且找不到该属性,那么它就会抛出 `AttributeError`,而不会进入 `__getattr__`。不过因为你在 `__getattribute__` 的实现中直接处理了返回逻辑(调用了 `super()`),这意味着你可以对属性的访问进行一些定制化的处理。

也就是说, `__getattribute__` 是更基础的属性获取方法,而 `__getattr__` 是一个后备机制,只有在 `__getattribute__` 失败的时候才会被调用。

希望这个解释能帮助您更好地理解它们之间的关系!如果还有不清楚的地方,欢迎继续提问!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

wbzxz 发表于 2024-7-30 23:51:31

如果你在 `__getattribute__` 中调用了 `super().__getattribute__`,并且找不到该属性,那么它就会抛出 `AttributeError`,当抛出`AttributeError`时,这时候就会去调用__getattr__,根据__getattr__方法所写的代码执行,故不会出现报错问题。
我理解的对吗?

FishC 发表于 2024-7-30 23:51:37

wbzxz 发表于 2024-07-30 23:51
如果你在 `__getattribute__` 中调用了 `super().__ge ...

是的,您理解得非常正确!让我们更清晰地梳理一下这个过程:

1. **调用 `__getattribute__`**:当你尝试访问一个属性时,首先会调用对象的 `__getattribute__` 方法。

2. **查找属性**:
   - 在 `__getattribute__` 方法中,如果你使用 `super().__getattribute__(attrname)` 来查找属性,并且该属性不存在,那么 Python 会抛出 `AttributeError`。
   - 抛出这个异常后,Python 不会直接终止程序,而是会查找是否有 `__getattr__` 方法来处理这个情况。

3. **调用 `__getattr__`**:
   - 如果你的类中定义了 `__getattr__` 方法,Python 将会调用它。
   - 在 `__getattr__` 中,你可以根据需要做一些自定义处理,比如返回特定的字符串或者其他对象。
   - 如果在 `__getattr__` 中处理完后没有抛出异常(例如,您返回了 "I love FishC."),那么就不会出现报错。

所以,您的总结是完全正确的!通过这种方式,您可以为一些不存在的属性提供备用机制,而不会导致程序运行时抛出错误。这是一种非常灵活的设计,让你能够控制对象对于未知属性的响应。

如果还有疑问或者想了解更多细节,随时欢迎提问!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 为什么不报错呢?