在 Python 个描述符中使用 setattr() 和 getattr()

Using setattr() and getattr() in Python descriptors

在尝试以几种不同的方式创建描述符时,我注意到一些我试图理解的奇怪行为。以下是我创建描述符的三种不同方式:

>>> class NumericValueOne():
...     def __init__(self, name):
...         self.name = name
...     def __get__(self, obj, type=None) -> object:
...         return obj.__dict__.get(self.name) or 0
...     def __set__(self, obj, value) -> None:
...         obj.__dict__[self.name] = value
>>> class NumericValueTwo():
...     def __init__(self, name):
...         self.name = name
...         self.internal_name = '_' + self.name
...     def __get__(self, obj, type=None) -> object:
...         return getattr(obj, self.internal_name, 0)
...     def __set__(self, obj, value) -> None:
...         setattr(obj, self.internal_name, value)
>>> class NumericValueThree():
...     def __init__(self, name):
...         self.name = name
...     def __get__(self, obj, type=None) -> object:
...         return getattr(obj, self.name, 0)
...     def __set__(self, obj, value) -> None:
...         setattr(obj, self.name, value)

然后我在 Foo 类 中使用它们,如下所示:

>>> class FooOne():
...     number = NumericValueOne("number")

>>> class FooTwo():
...     number = NumericValueTwo("number")

>>> class FooThree():
...     number = NumericValueThree("number")

my_foo_object_one = FooOne()
my_foo_object_two = FooTwo()
my_foo_object_three = FooThree()

my_foo_object_one.number = 3
my_foo_object_two.number = 3
my_foo_object_three.number = 3

虽然 FooOneFooTwo 在设置和获取值时都按预期工作。 FooThree 抛出以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __set__
  File "<stdin>", line 7, in __set__
  File "<stdin>", line 7, in __set__
  [Previous line repeated 497 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object

好像是setattr()调用了__set__()方法?但是,如果 setattr() 正在修改 obj __dict__,为什么要这样做呢?如果我们使用 internal_name 为什么会这样?

为什么我们需要使用私有变量才能正确使用内置的 getattr()setattr() 方法?此外,这与直接修改 obj __dict__ 有何不同,如 NumericValueOne?

But why should it be doing that if setattr() is modifying the obj __dict__?

setattr 只是修改__dict__。它设置属性,与 x.y = z 完全一样,对于您尝试设置的属性,“设置此属性”意味着“调用您已经在的 setter”。因此,无限递归。

And why does this work if we use internal_name?

该名称与 属性 不对应,因此它只得到一个 __dict__ 条目。