为啥 Counter 的 __init__ 方法称为描述符?

Posted

技术标签:

【中文标题】为啥 Counter 的 __init__ 方法称为描述符?【英文标题】:Why is the __init__ method of Counter referred to as a descriptor?为什么 Counter 的 __init__ 方法称为描述符? 【发布时间】:2016-05-05 01:37:43 【问题描述】:

我在阅读 Counter 类的 __init__ 方法时,看到了这个:

if not args:
    TypeError("descriptor '__init__' of 'Counter' object "
              "needs an argument")

我不确定descriptor是什么意思,所以我查了python数据模型文档,发现是这样的:

通常,描述符是具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法覆盖:__get__()、__set__() 和 __delete__()。如果为对象定义了这些方法中的任何一个,则称其为描述符。

类定义中似乎没有这些方法,那么为什么将 __init_ 称为描述符?

【问题讨论】:

为了记录,它具有这种设计的唯一原因是允许使用名为self 的关键字参数初始化Counter,而不会意外替换__init__ 中的self 实例;他们必须接受位置可变参数并手动提取“真实”self,因此它没有可以被关键字参数覆盖的外部名称。除非您手动调用 __init__,否则您实际上无法触发此错误(并且这样做时没有在子类中使用 super,这会隐式传递它,就像常规构造一样)。 【参考方案1】:

在python中,所有函数都是描述符(包括__init__)。这实际上是他们在课堂上使用self 的方式。例如,我可以定义一个函数 (foo),然后当我查看它的方法时,我会看到 foo 有一个 __get__ 方法,这使它符合描述符协议:

>>> def foo():
...   pass
... 
>>> dir(foo)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> '__get__' in dir(foo)
True

所以这里使用的术语至少准确。这是否是最好的用语可能会引起争论......

我可能将其称为“绑定方法”而不是描述符,但在 python3.x 中,常规函数、绑定方法和未绑定方法之间的区别变得更加模糊(未绑定方法 python3.x中的常规函数​​)...


当然,我可以使用不同类型的描述符来初始化我的 Counter 子类 ...

class MyDescriptor(object):
    def __get__(self, inst, cls):
        # This is a really useless descriptor!
        return Counter.__init__.__get__(inst, cls)

class MyCounter(Counter):
    __init__ = MyDescriptor()

并抛出一个错误,那么错误消息会更准确,但这是一个非常疯狂的案例,我不希望经常发生。

要真正了解 Raymond 在编写该代码时的想法,我想您必须问他(或深入研究 hg 提交历史并希望他在提交消息中提及)。

【讨论】:

我不明白你的答案,我也不明白它如何符合引用的文档。能详细点吗? 啊,我错过了定义 __get__ 的函数,而不是 Counter。谢谢。 相关:为什么将函数称为描述符,而将其称为函数会更具描述性(没有双关语)? @DavisYoshida -- 我想您可以将__init__ 设置为您想要的任何类型的描述符。它不会必须成为一个函数(尽管它几乎总是如此)。 @timgeb -- 我的意思是__init__ 是一个函数(通常),所有函数都是描述符,因为它们有__get__ 方法。

以上是关于为啥 Counter 的 __init__ 方法称为描述符?的主要内容,如果未能解决你的问题,请参考以下文章

为啥`type.__new__`调用`__init_subclass__`?

菜鸟容易中的招__setattr__

Python 魔法方法

关于python的实例方法问题?

为啥这段代码会打印出无穷大的数字? [关闭]

Python 多线程,线程同步