Python的“__get*__”和“_del*__”方法有何区别?

41

我几个月前刚开始学习Python,现在想要了解不同的__get*__方法之间的区别:

__get__
__getattr__
__getattribute__
__getitem___

还有它们的__del*__等同方法:

__del__
__delattr__
__delete__
__delitem__

它们之间有什么区别?我应该在何时使用其中之一?为什么大多数__get*__方法都有相应的__set*__,但没有__setattribute__呢?


1
文档没有像“水平”那样“并排”列出它们,但是它确实在单个页面上拥有所有内容,除了{get,set}item之外(这些内容因为包含item而突出),其他所有内容都垂直地紧挨着彼此分成两个小部分。其中一些名称确实过于简洁/相似,但情况并不像你描述的那么糟糕。 - user395760
2
是的,大约20分钟前我意识到它们确实都在同一页上。对此我深感抱歉——这是我的错。然而: 我仍然感到困惑。我认为我已经清楚地表明了我已经阅读了文档,但在特殊方法名称之间仍然难以明确定义。 - Zearin
1个回答

48

你列出的每个方法的文档都可以从文档索引轻松获取。

无论如何,这可能是一个稍微详细的参考:

__get____set____del__是描述符

"简而言之,描述符是自定义当您在模型上引用属性时发生的事件的一种方式。" [官方文档链接]

它们已经被很好地解释了,所以这里提供一些参考资料:

__getattr____getattribute____setattr____delattr__

是可以定义的方法,用于自定义类实例的属性访问(使用、分配或删除x.name)的含义。 [官方文档链接]

示例1:

class Foo:
    def __init__(self):
        self.x = 10
    def __getattr__(self, name):
        return name

f = Foo()
f.x    # -> 10
f.bar   # -> 'bar'

例子2:

class Foo:
    def __init__(self):
        self.x = 10
    def __getattr__(self,name):
        return name
    def __getattribute__(self, name):
        if name == 'bar':
            raise AttributeError
        return 'getattribute'

f = Foo()
f.x    # -> 'getattribute'
f.baz    # -> 'getattribute'
f.bar    # -> 'bar'

__getitem__, __setitem__, __delitem__

这些是用于实现容器对象的方法。[官方文档链接]

例如:

class MyColors:
    def __init__(self):
        self._colors = {'yellow': 1, 'red': 2, 'blue': 3}
    def __getitem__(self, name):
        return self._colors.get(name, 100)

colors = MyColors()
colors['yellow']   # -> 1
colors['brown']    # -> 100

我希望这足以给您一个大致的概念。


1
(1) 我在找到你的答案前大约25分钟意识到这些实际上都记录在同一页上。对于我原来的问题中的错误,我感到很抱歉。(2) 谢谢! Marty Alchin的文章写得非常好。他们澄清了我没有理解的一个重要问题——__get____set__应该在属性中定义,而不是在类中定义!我仍然认为我原帖中的所有方法都需要更具区分性的名称,但我突然感觉不那么无助了。谢谢。☺ - Zearin
1
@Zearin:这里有一篇关于Python属性和方法的好文章,或许你会感兴趣。*(修正链接)* - Rik Poggi
2
@RikPoggi,你并没有说清楚为什么我们在有__getattr__的情况下还需要__get__,或者反过来。你提到的两个答案是相同的。 - overexchange
感谢提供示例和文档链接。如果能在注释中明确说明如何从“AttributeError”转到“__getattr__”,并且可能还要指出,与“__setattr__”和“__getattribute__”不同,“__getattr__”仅在名称未定义或通过“AttributeError”调用时才会被调用,那就太好了。至少这是我的理解。 - Julian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接